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 →
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 &amp; Archives ]</p>
        <h1 className={styles.headingLight}>Every project across era &amp; disciplines</h1>
      </section>
      <section className={styles.sectionWhite}>
        <p className={styles.labelDark}>[ Services ]</p>
        <h1 className={styles.headingDark}>Strategy, design &amp; development</h1>
      </section>
      <section className={styles.sectionCream}>
        <p className={styles.labelDark}>[ Contact ]</p>
        <h1 className={styles.headingDark}>Let&apos;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;
  }
}

May 4, 2026

NAVIGATION

Nav Slide Panel

A minimal header that slides in from the top on load. Desktop shows logo, nav links, language switcher, and underlined contact CTA. Mobile collapses to logo + menu toggle with a full-screen white panel featuring large stagger-animated nav links, office locations, language selector, and copyright.

Apr 27, 2026

NAVIGATION

Nav Agency Grid

A fixed agency-style navigation bar using a 4-column CSS grid with staggered GSAP entrance animations, current-page indicator with animated dash, scroll-direction hide/show, desktop hover dropdown, and a full-screen mobile menu with large typography.

Apr 10, 2026

NAVIGATION

Nav Minimal

A clean top navigation bar with logo (image or text), centered nav links with hover-reveal submenu row, locale switcher, and account/cart icons. Collapses to logo + cart + hamburger on mobile.