← Biblioteca componentes/navegacao/nav-fixa-vidro/ficha.md CódigoTela cheia
navegacao/nav-fixa-vidro

Menu fixo com fundo de vidro

🟢 estavel complexidade média prefixo nav- menu header fixo vidro scroll sublinhado

Incluir · as linhas que se colam num site novo, depois do core (CLAUDE.md §10)

<link rel="stylesheet" href="componentes/navegacao/nav-fixa-vidro/estilo.css">
<script src="componentes/navegacao/nav-fixa-vidro/script.js"></script>

markup.html · 30 linhas · 1,3 KB

<!-- ==========================================================================
     NAV — menu fixo que ganha fundo de vidro no scroll
     Prefixo: nav-
     Requer: core/tokens.css, core/base.css + este estilo.css e script.js
     Pareia com navegacao/drawer-fullscreen: o botão hambúrguer aqui só
     carrega [data-drawer-open]; quem escuta é o script do drawer. Sem o
     drawer na página, o botão fica inerte e nada quebra.
     ========================================================================== -->

<header class="nav sec-dark" data-nav>
  <div class="wrap nav-inner">
    <a href="#topo" class="nav-logo" aria-label="Início">
      <img src="../../../showroom/demo/marca.svg" alt="Nome da marca">
    </a>

    <nav class="nav-links" aria-label="Principal">
      <a href="#manifesto">Quem somos</a>
      <a href="#solucoes">Soluções</a>
      <a href="#estrutura">Estrutura</a>
      <a href="#metodo">Método</a>
      <a href="#duvidas">Dúvidas</a>
    </nav>

    <a href="#contato" class="nav-cta">Fale com um especialista</a>

    <button class="nav-burger" data-drawer-open aria-label="Abrir menu">
      <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><path d="M3 7h18M3 12h18M3 17h18"/></svg>
    </button>
  </div>
</header>

estilo.css · 63 linhas · 2,5 KB

/* ==========================================================================
   NAV — menu fixo que ganha fundo de vidro no scroll
   Prefixo: nav-
   Tokens usados: --fg --fg-muted --rule --accent --dark --dark-rgb --light-2
                  --display --nav-h --radius-pill --ease

   A nav carrega o próprio escopo de tom (.sec-dark) porque vive fora de
   qualquer <section>: sem isso --fg e --rule não resolvem.
   ========================================================================== */

.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  border-bottom:1px solid transparent;
  transition:background .45s,border-color .45s,backdrop-filter .45s;
}
/* Enquanto está no topo, o menu é invisível: só o conteúdo flutua sobre a
   foto do hero. O vidro entra depois dos primeiros 30px de rolagem. */
.nav.is-scrolled{
  background:rgba(var(--dark-rgb),.82);
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);
  border-bottom-color:var(--rule);
}

.nav-inner{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:var(--nav-h)}

.nav-logo{flex:0 0 auto;line-height:0;display:block}
.nav-logo img{height:22px;width:auto;display:block}

.nav-links{display:flex;align-items:center;gap:clamp(14px,2vw,30px);min-width:0}
.nav-links a{
  font-size:12.5px;letter-spacing:.04em;color:var(--fg-muted);
  position:relative;padding:4px 0;white-space:nowrap;transition:color .25s;
}
.nav-links a:hover{color:var(--fg)}
/* Sublinhado que cresce da esquerda. transform em vez de width para animar
   no compositor e não relayout a cada frame. */
.nav-links a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--accent);
  transform:scaleX(0);transform-origin:left;transition:transform .32s var(--ease);
}
.nav-links a:hover::after{transform:scaleX(1)}

.nav-cta{
  font-family:var(--display);font-weight:700;font-size:10.5px;
  letter-spacing:.1em;text-transform:uppercase;
  background:var(--accent);color:var(--dark);
  border-radius:var(--radius-pill);padding:12px 22px;
  white-space:nowrap;flex:0 0 auto;transition:background .3s;
}
.nav-cta:hover{background:var(--light-2)}

.nav-burger{display:none;background:none;border:none;color:var(--fg);padding:6px;line-height:0}

/* 1000px e não 900: o menu tem 5 links longos mais um CTA. Abaixo disso os
   links começam a se atropelar antes de o layout "parecer" mobile. */
@media(max-width:1000px){
  .nav-links{display:none}
  .nav-cta{display:none}
  .nav-burger{display:block}
}
@media(max-width:560px){
  .nav-logo img{height:17px}
}

script.js · 15 linhas · 0,6 KB

/* ==========================================================================
   NAV — menu fixo que ganha fundo de vidro no scroll
   Faz uma coisa só: alterna .is-scrolled depois de 30px de rolagem.
   Não conhece o hero nem o drawer.
   ========================================================================== */

(function () {
  var nav = document.querySelector('[data-nav]');
  if (!nav) return;

  /* 30px em vez de 0: um toque no trackpad não deve piscar o vidro. */
  window.LIB.onScroll(function () {
    nav.classList.toggle('is-scrolled', scrollY > 30);
  });
})();

Menu fixo no topo, invisível enquanto a página está no início — só a marca, os links e o CTA flutuam sobre a foto do hero. Passados 30px de rolagem, entra um fundo de vidro escuro com blur e uma linha inferior. Os links ganham sublinhado que cresce da esquerda no hover.


Quando usar

Quando não usar


Padrão editorial

Slot Regra
Links 1 a 2 palavras cada, 4 a 6 itens. Substantivos, não verbos: "Soluções", não "Ver soluções".
CTA 3 a 5 palavras, ação concreta: "Fale com um especialista", "Agende seu diagnóstico". Nunca "Contato".
Marca SVG. Nunca texto estilizado — logo é vetor fornecido pelo cliente.

A ordem dos links espelha a ordem das seções na página. Link que não tem seção correspondente vira página nova, não item de menu.


Como usar

<link rel="stylesheet" href="componentes/navegacao/nav-fixa-vidro/estilo.css">
<script src="componentes/navegacao/nav-fixa-vidro/script.js"></script>

Cole como primeiro filho do <body>, antes do hero.

Ganchos

Gancho Papel
[data-nav] raiz — o script procura por ele
.nav.is-scrolled estado de vidro, aplicado pelo script
[data-drawer-open] no botão hambúrguer — quem escuta é o navegacao/drawer-fullscreen

Pareamento

O hambúrguer aqui não abre nada sozinho: ele só carrega [data-drawer-open]. Quem escuta é o script do drawer. Sem drawer na página o botão fica inerte e nada quebra no console. Se você usa este menu, quase sempre quer o navegacao/drawer-fullscreen junto.

Variações


Gotchas


Histórico

Projeto Onde Data Diferenças
Liberta Wealth index.html e quem-somos.html, topo 2026-08 Origem. .logo sem prefixo (colidia com o rodapé e o drawer), .scrolled em vez de .is-scrolled, id #nav, borda #222 crua, e o estado de scroll era calculado dentro do heroUpdate() do hero — páginas sem hero dependiam de um if (!hero) no meio da função.

Normalizações aplicadas na extração: prefixo em tudo (.logo → .nav-logo); .scrolled → .is-scrolled; id → [data-nav]; borda crua → --rule; #0a0a0a do CTA → --dark; border-radius:24px → --radius-pill; escopo de tom explícito no elemento; desacoplado do hero — o script agora só assina LIB.onScroll e olha scrollY; o hambúrguer virou [data-drawer-open] em vez de id conhecido pelo script do drawer.