NavigationSimpleApril 24, 2026
Nav Glassmorphic
A floating glassmorphic navigation bar with frosted semi-transparent background, centered nav links, locale selector, and outlined CTA button. Collapses to logo + hamburger on mobile with an expanding panel featuring nav links, social links, and email.
View Full Demo →Preview
Source
demo.jsx
import NavGlassmorphic from "./index.jsx";
import { navGlassmorphic } from "../demo-data.js";
import styles from "./demo.module.css";
export default function NavGlassmorphicDemo() {
return (
<div className={styles.wrapper}>
<NavGlassmorphic {...navGlassmorphic} />
<section className={styles.sectionBlack}>
<p className={styles.labelLight}>[ Works & Archives ]</p>
<h1 className={styles.headingLight}>Every project across era & disciplines</h1>
</section>
<section className={styles.sectionWhite}>
<p className={styles.labelDark}>[ Services ]</p>
<h1 className={styles.headingDark}>Strategy, design & development</h1>
</section>
<section className={styles.sectionCream}>
<p className={styles.labelDark}>[ Contact ]</p>
<h1 className={styles.headingDark}>Let's build something together</h1>
</section>
</div>
);
}
index.jsx
"use client";
import { useState } from "react";
import Link from "next/link";
import styles from "./styles.module.css";
function GlobeIcon() {
return (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
<circle cx="12" cy="12" r="10" />
<path d="M2 12h20" />
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
</svg>
);
}
function ArrowIcon() {
return (
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
);
}
export default function NavGlassmorphic({
logo = { text: "Brand" },
links = [],
locale = "EN",
cta = { label: "Get Started", href: "#" },
socials = [],
email = "",
barBackground = "rgba(75, 75, 70, 0.72)",
activeColor = "#c8553a",
}) {
const [menuOpen, setMenuOpen] = useState(false);
return (
<header className={styles.header}>
<div
className={`${styles.bar} ${menuOpen ? styles.barOpen : ""}`}
style={{ "--bar-bg": barBackground, "--active-color": activeColor }}
>
<div className={styles.topRow}>
<Link href={logo.href ?? "/"} className={styles.logo}>
{logo.src ? (
<img src={logo.src} alt={logo.alt ?? ""} className={styles.logoImg} />
) : (
<span className={styles.logoText}>{logo.text}</span>
)}
</Link>
<nav className={styles.nav} aria-label="Main">
{links.map((link) => (
<Link
key={link.label}
href={link.href ?? "#"}
className={styles.navLink}
>
{link.label}
</Link>
))}
</nav>
<div className={styles.right}>
<button className={styles.localeBtn}>
<GlobeIcon />
<span>{locale}</span>
</button>
<Link href={cta.href ?? "#"} className={styles.ctaBtn}>
<span>{cta.label}</span>
<ArrowIcon />
</Link>
</div>
<button
className={styles.menuBtn}
aria-label={menuOpen ? "Close menu" : "Open menu"}
onClick={() => setMenuOpen((o) => !o)}
>
<span className={`${styles.menuIcon} ${menuOpen ? styles.menuIconOpen : ""}`} />
</button>
</div>
<div className={`${styles.mobileMenu} ${menuOpen ? styles.mobileMenuOpen : ""}`}>
<nav className={styles.mobileNav}>
{links.map((link) => (
<Link
key={link.label}
href={link.href ?? "#"}
className={styles.mobileLink}
onClick={() => setMenuOpen(false)}
>
{link.label}
</Link>
))}
</nav>
<hr className={styles.divider} />
{socials.length > 0 && (
<div className={styles.mobileSocials}>
{socials.map((s) => (
<Link
key={s.label}
href={s.href ?? "#"}
className={styles.socialLink}
target="_blank"
rel="noopener noreferrer"
>
{s.label}
</Link>
))}
</div>
)}
{email && (
<a href={`mailto:${email}`} className={styles.emailLink}>
{email}
</a>
)}
</div>
</div>
</header>
);
}
demo.module.css
.wrapper {
background: #f1ebe7;
min-height: 300vh;
overflow-y: auto;
}
.sectionBlack {
min-height: 100vh;
background: #111;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 8rem 2rem 4rem;
}
.sectionWhite {
min-height: 100vh;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 4rem 2rem;
}
.sectionCream {
min-height: 100vh;
background: #f1ebe7;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 4rem 2rem;
}
.label {
font-size: 0.72rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.14em;
margin: 0 0 1rem;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
.labelLight {
composes: label;
color: #999;
}
.labelDark {
composes: label;
color: #555;
}
.heading {
font-size: clamp(1.75rem, 4vw, 3rem);
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1.15;
margin: 0;
max-width: 700px;
}
.headingLight {
composes: heading;
color: #fff;
}
.headingDark {
composes: heading;
color: #222;
}
styles.module.css
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
padding: 2.5rem 2rem 0;
}
/* ---- Glassmorphic bar ---- */
.bar {
background: var(--bar-bg, rgba(75, 75, 70, 0.72));
backdrop-filter: blur(12px) saturate(1.2);
-webkit-backdrop-filter: blur(12px) saturate(1.2);
border-radius: 14px;
overflow: hidden;
}
/* ---- Top row ---- */
.topRow {
display: flex;
align-items: center;
height: 64px;
padding: 0 1.5rem;
}
/* ---- Logo ---- */
.logo {
display: inline-flex;
align-items: center;
text-decoration: none;
color: #fff;
flex-shrink: 0;
}
.logoImg {
height: 28px;
width: auto;
display: block;
}
.logoText {
font-size: 1.75rem;
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1;
color: #fff;
}
/* ---- Desktop nav (centered) ---- */
.nav {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 2.25rem;
}
.navLink {
font-size: 0.72rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgba(255, 255, 255, 0.85);
text-decoration: none;
white-space: nowrap;
transition: color 0.2s;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
.navLink:hover {
color: var(--active-color, #c8553a);
}
/* ---- Right side (desktop) ---- */
.right {
display: flex;
align-items: center;
gap: 0.65rem;
flex-shrink: 0;
}
.localeBtn {
display: flex;
align-items: center;
gap: 0.4rem;
background: #fff;
border: none;
border-radius: 100px;
padding: 0.45rem 0.85rem;
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.06em;
cursor: pointer;
color: #333;
line-height: 1;
}
.ctaBtn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
border: 1.5px solid rgba(255, 255, 255, 0.9);
border-radius: 100px;
padding: 0.5rem 1.25rem;
color: #fff;
background: transparent;
font-size: 0.68rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
text-decoration: none;
white-space: nowrap;
line-height: 1;
transition: background 0.2s;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
.ctaBtn:hover {
background: rgba(255, 255, 255, 0.12);
}
/* ---- Mobile menu button (hidden on desktop) ---- */
.menuBtn {
display: none;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: none;
border: none;
cursor: pointer;
padding: 0;
margin-left: auto;
}
.menuIcon {
display: block;
position: relative;
width: 22px;
height: 2px;
background: transparent;
}
.menuIcon::before,
.menuIcon::after {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 1.5px;
background: #fff;
transition: transform 0.35s ease, top 0.35s ease;
}
.menuIcon::before {
top: -4px;
}
.menuIcon::after {
top: 4px;
}
.menuIconOpen::before {
top: 0;
transform: rotate(45deg);
}
.menuIconOpen::after {
top: 0;
transform: rotate(-45deg);
}
/* ---- Mobile expanded menu ---- */
.mobileMenu {
max-height: 0;
overflow: hidden;
transition: max-height 0.6s cubic-bezier(0.76, 0, 0.24, 1);
padding: 0 1.5rem;
}
.mobileMenuOpen {
max-height: calc(100vh - 100px);
overflow-y: auto;
}
.mobileNav {
display: flex;
flex-direction: column;
gap: 0.15rem;
padding: 1.75rem 0 1.5rem;
}
.mobileLink {
font-size: 0.82rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #fff;
text-decoration: none;
padding: 0.4rem 0;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
.divider {
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.2);
margin: 0;
}
.mobileSocials {
display: flex;
flex-direction: column;
gap: 0.15rem;
padding: 1.25rem 0;
}
.socialLink {
font-size: 0.78rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #fff;
text-decoration: none;
padding: 0.3rem 0;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
.emailLink {
display: block;
font-size: 0.72rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #fff;
text-decoration: none;
padding: 0.25rem 0 1.75rem;
font-family: ui-monospace, "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", "Courier New", monospace;
}
/* ---- Responsive ---- */
@media (max-width: 1024px) {
.nav {
display: none;
}
.right {
display: none;
}
.menuBtn {
display: flex;
}
}
@media (max-width: 480px) {
.header {
padding: 0.75rem 1rem 0;
}
.topRow {
height: 56px;
padding: 0 1.25rem;
}
.mobileMenu {
padding: 0 1.25rem;
}
}