# Navigation bar / Barra de navegación

> Header with logo, links and actions. On mobile, links move into a menu.

Page: https://componentes-lovat.vercel.app/c/navbar/  
License: MIT · Design: Origin UI (MIT) · comp-577.tsx, navigation-menu.tsx  
Requires: tokens.css, button  
Context: ambos · Density: media · Tone: sobria

## Files

- Tokens (once): https://componentes-lovat.vercel.app/tokens.css
- Dependency `button`: https://componentes-lovat.vercel.app/r/button.json
- HTML: https://componentes-lovat.vercel.app/raw/navbar/navbar.html
- CSS: https://componentes-lovat.vercel.app/raw/navbar/navbar.css
- JS: https://componentes-lovat.vercel.app/raw/navbar/navbar.js
- Everything in one file: https://componentes-lovat.vercel.app/r/navbar.html

Dark theme: add class `dark` to `<html>`.

## When to use

- As the header of any site.

## When not to use

- With more than 6 links: group them or use a sidebar.

## Accessibility

Roles: navigation, link, button

| Keys | Action |
|---|---|
| Tab | Moves through links and buttons |
| Esc | Closes the mobile menu |

- <nav> with aria-label and aria-current=page on the current page.
- The mobile menu is a native popover: closes with Esc or outside click and returns focus.
- No role=menu: these are navigation links.

## Code

```html
<header class="c-navbar">
  <div class="c-navbar__inner">
    <div class="c-navbar__start">
      <button class="c-btn c-btn--ghost c-btn--icon c-navbar__toggle" type="button" aria-label="Menú" aria-expanded="false" aria-controls="navbar-mobile" data-c-navbar>
        <svg class="c-navbar__burger" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 12L20 12"/><path d="M4 12H20"/><path d="M4 12H20"/></svg>
      </button>
      <div class="c-navbar__main">
        <a class="c-navbar__logo" href="#" aria-label="Inicio"><svg viewBox="0 0 33 33" fill="currentColor" aria-hidden="true"><path d="M20.46 1.766 17.303.923l-2.66 9.896-2.403-8.934-3.157.843 2.595 9.652-6.464-6.442-2.311 2.304 7.09 7.066-8.83-2.358-.846 3.146 9.648 2.577a6.516 6.516 0 0 1-.169-1.478c0-3.598 2.927-6.515 6.537-6.515s6.537 2.917 6.537 6.515c0 .505-.057.997-.167 1.468l8.768 2.342.846-3.147-9.686-2.586 8.83-2.358-.845-3.147-9.686 2.587 6.464-6.442-2.311-2.304-6.992 6.969 2.369-8.81Z"/><path d="M22.695 18.7a6.495 6.495 0 0 1-1.626 2.986l6.352 6.33 2.31-2.303-7.036-7.013ZM21.005 21.752a6.538 6.538 0 0 1-2.922 1.722l2.312 8.596 3.157-.843-2.547-9.475ZM17.965 23.505a6.569 6.569 0 0 1-1.632.205 6.566 6.566 0 0 1-1.743-.235l-2.314 8.605 3.157.843 2.532-9.418ZM14.478 23.444a6.54 6.54 0 0 1-2.87-1.747l-6.367 6.346 2.31 2.303 6.927-6.902ZM11.555 21.64a6.492 6.492 0 0 1-1.585-2.948L1.173 21.04l.846 3.146 9.536-2.546Z"/></svg></a>
        <nav aria-label="Principal">
          <ul class="c-navbar__list">
            <li><a class="c-navbar__link" href="#" aria-current="page">Inicio</a></li>
            <li><a class="c-navbar__link" href="#features">Funciones</a></li>
            <li><a class="c-navbar__link" href="#pricing">Precios</a></li>
            <li><a class="c-navbar__link" href="#about">Nosotros</a></li>
          </ul>
        </nav>
      </div>
    </div>
    <div class="c-navbar__end">
      <a class="c-btn c-btn--ghost c-btn--sm" href="#login">Entrar</a>
      <a class="c-btn c-btn--primary c-btn--sm" href="#signup">Empezar</a>
    </div>
  </div>
  <nav class="c-navbar__mobile" id="navbar-mobile" popover aria-label="Principal (móvil)">
    <ul>
      <li><a class="c-navbar__mobile-link" href="#" aria-current="page">Inicio</a></li>
      <li><a class="c-navbar__mobile-link" href="#features">Funciones</a></li>
      <li><a class="c-navbar__mobile-link" href="#pricing">Precios</a></li>
      <li><a class="c-navbar__mobile-link" href="#about">Nosotros</a></li>
    </ul>
  </nav>
</header>
```

```css
/* navbar · Componentes (MIRÓ Labs). Diseño de coss ui (MIT, © coss.com, antes Origin UI).
   Generado por tools/build_css.py desde navbar.tw.json. No editar a mano. Necesita tokens.css. */
.c-navbar__inner{display:flex}
.c-navbar__start{display:flex}
.c-navbar__main{display:flex}
.c-navbar__list{display:flex}
.c-navbar__link{display:flex}
.c-navbar__end{display:flex}
.c-navbar__mobile-link{display:flex;width:100%}
.c-navbar__list{flex:1}
.c-navbar__link{flex-direction:column}
.c-navbar__mobile-link{flex-direction:column}
.c-navbar__inner{align-items:center}
.c-navbar__start{align-items:center}
.c-navbar__main{align-items:center}
.c-navbar__list{align-items:center}
.c-navbar__end{align-items:center}
.c-navbar__inner{justify-content:space-between}
.c-navbar__list{justify-content:center}
.c-navbar__link{gap:calc(var(--spacing)*1)}
.c-navbar__mobile-link{gap:calc(var(--spacing)*1)}
.c-navbar__start{gap:calc(var(--spacing)*2)}
.c-navbar__list{gap:calc(var(--spacing)*2)}
.c-navbar__end{gap:calc(var(--spacing)*2)}
.c-navbar__inner{gap:calc(var(--spacing)*4)}
.c-navbar__main{gap:calc(var(--spacing)*6)}
.c-navbar__mobile{border-radius:calc(var(--radius) - 2px)}
.c-navbar__link{border-radius:calc(var(--radius) - 4px)}
.c-navbar__mobile-link{border-radius:calc(var(--radius) - 4px)}
.c-navbar__mobile{border-style:var(--tw-border-style);border-width:1px}
.c-navbar{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}
.c-navbar__mobile{background-color:var(--popover);padding:calc(var(--spacing)*1)}
.c-navbar__link{padding:calc(var(--spacing)*2)}
.c-navbar__mobile-link{padding:calc(var(--spacing)*2)}
.c-navbar{padding-inline:calc(var(--spacing)*4)}
.c-navbar__link{padding-block:calc(var(--spacing)*1.5)}
.c-navbar__mobile-link{padding-block:calc(var(--spacing)*1.5)}
.c-navbar__link{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}
.c-navbar__mobile-link{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}
.c-navbar__link{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--muted-foreground)}
.c-navbar__mobile{color:var(--popover-foreground)}
.c-navbar__logo{color:var(--primary)}
.c-navbar__link{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}
.c-navbar__mobile-link{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}
.c-navbar__link{--tw-outline-style:none;outline-style:none}
.c-navbar__mobile-link{--tw-outline-style:none;outline-style:none}
@media (hover:hover){
.c-navbar__link:hover{background-color:var(--accent)}
.c-navbar__mobile-link:hover{background-color:var(--accent)}
}
.c-navbar__link:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
.c-navbar__mobile-link:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}
@media not all and (min-width:48rem){
.c-navbar__list{display:none}
}
/* propio */
.c-navbar{background:var(--background);width:100%}
.c-navbar__logo{display:inline-flex}
.c-navbar__logo svg{width:32px;height:32px}
.c-navbar__link:focus-visible,.c-navbar__mobile-link:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent);box-shadow:0 0 0 3px var(--tw-ring-color)}
.c-navbar__mobile{position:fixed;margin:0;inset:auto;opacity:0;scale:.95;transform-origin:top left;transition:opacity .15s,scale .15s,overlay .15s allow-discrete,display .15s allow-discrete}
.c-navbar__mobile:popover-open{opacity:1;scale:1}
@starting-style{.c-navbar__mobile:popover-open{opacity:0;scale:.95}}
.c-navbar__mobile ul{display:grid;list-style:none;margin:0;padding:0}
.c-navbar__burger path{transform-origin:center;transition:transform .3s cubic-bezier(.5,.85,.25,1.1)}
.c-navbar__burger path:nth-child(1){transform:translateY(-7px)}
.c-navbar__burger path:nth-child(3){transform:translateY(7px)}
.c-navbar__toggle[aria-expanded=true] .c-navbar__burger path:nth-child(1){transform:rotate(315deg)}
.c-navbar__toggle[aria-expanded=true] .c-navbar__burger path:nth-child(2){transform:rotate(45deg)}
.c-navbar__toggle[aria-expanded=true] .c-navbar__burger path:nth-child(3){transform:rotate(135deg)}
.c-navbar .c-btn{font-size:var(--text-sm)}
@media (width >= 48rem){.c-navbar__mobile{display:none!important}}
.c-navbar__inner{height:calc(var(--spacing)*16)}
.c-navbar__list{list-style:none}
.c-navbar__logo:hover{color:color-mix(in oklab,var(--primary)90%,transparent)}
.c-navbar__link:hover{color:var(--primary)}
.c-navbar__link[aria-current=page],.c-navbar__mobile-link[aria-current=page]{background-color:var(--accent);color:var(--accent-foreground)}
.c-navbar__mobile{width:calc(var(--spacing)*36);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a}
@media (width >= 48rem){.c-navbar{padding-inline:calc(var(--spacing)*6)}.c-navbar__toggle{display:none}}
```

```js
/* Barra de navegación: en móvil, el botón abre el menú (popover nativo) debajo de él. */
(function () {
  const C = (window.Componentes = window.Componentes || {});
  function setup(btn) {
    const pop = document.getElementById(btn.getAttribute('aria-controls'));
    if (!pop || !pop.showPopover) return;
    const place = () => { const r = btn.getBoundingClientRect(); pop.style.top = r.bottom + 6 + 'px'; pop.style.left = Math.max(8, r.left) + 'px'; };
    btn.addEventListener('click', () => { if (pop.matches(':popover-open')) pop.hidePopover(); else { pop.showPopover(); place(); pop.querySelector('a')?.focus(); } });
    pop.addEventListener('toggle', (e) => { btn.setAttribute('aria-expanded', String(e.newState === 'open')); if (e.newState === 'closed' && pop.contains(document.activeElement)) btn.focus(); });
    pop.addEventListener('click', (e) => { if (e.target.closest('a')) pop.hidePopover(); });
  }
  function init(root) {
    (root || document).querySelectorAll('[data-c-navbar]:not([data-c-ready])').forEach((b) => { b.dataset.cReady = ''; try { setup(b); } catch (e) { console.warn('[navbar]', e); } });
  }
  C.navbar = { init };
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => init()); else init();
})();
```

## Credits

- Origin UI (MIT) · comp-577.tsx, navigation-menu.tsx: https://21st.dev/@originui
- Adrian Roselli, Don't use ARIA menu roles for site nav: https://adrianroselli.com/2017/10/dont-use-aria-menu-roles-for-site-nav.html
