Navbar
Navbar — the site header bar: a brand, a navigation slot, an actions slot.
Examples
<header class="navbar navbar-sticky">
<a class="navbar-brand" href="#home">Acme</a>
<nav class="nav navbar-nav" aria-label="Site">
<ul class="nav-list">
<li><a class="nav-link" href="#home" aria-current="page">Overview</a></li>
<li><a class="nav-link" href="#pricing">Pricing</a></li>
<li><a class="nav-link" href="#docs">Docs</a></li>
</ul>
</nav>
<div class="navbar-actions">
<a class="btn btn-primary btn-sm" href="#signup">Sign up</a>
</div>
<button class="btn btn-quiet btn-icon navbar-toggle" type="button"
command="show-modal" commandfor="site-menu" aria-label="Open menu"></button>
<dialog class="drawer drawer-start" id="site-menu" aria-label="Site menu">
<header class="drawer-header">
<p class="drawer-title">Menu</p>
<button class="btn btn-quiet drawer-close" type="button"
command="close" commandfor="site-menu">Close</button>
</header>
<div class="drawer-body">
<nav class="nav nav-vertical" aria-label="Site menu links">
<ul class="nav-list">
<li><a class="nav-link" href="#home" aria-current="page">Overview</a></li>
<li><a class="nav-link" href="#pricing">Pricing</a></li>
<li><a class="nav-link" href="#docs">Docs</a></li>
</ul>
</nav>
</div>
</dialog>
</header>Narrow the window and the bar’s nav gives way to the toggle.
The panel is a modal <dialog> opened by an invoker command, so the focus trap, Escape and the inert page are the platform’s — and no script is involved anywhere.
<header class="navbar">
<a class="navbar-brand" href="#home">Acme</a>
<div class="navbar-actions">
<a class="btn btn-quiet btn-sm" href="#account">Account</a>
</div>
</header>The nav slot is optional. There is no inner container: a full-bleed bar with a centred column is page layout, so put your own <div> inside with max-inline-size: var(--container).
Tokens 19
Level 2, declared on .navbar itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --navbar-actions-gap | var(--space-2) |
| --navbar-brand-color | var(--color-on-surface) |
| --navbar-brand-font-family | var(--font-primary) |
| --navbar-brand-font-size | var(--font-size-lg) |
| --navbar-brand-font-weight | var(--font-weight-semibold) |
| --navbar-brand-gap | var(--space-2) |
| --navbar-gap | var(--space-4) |
| --navbar-min-block-size | 3.5rem |
| --navbar-padding-block | var(--space-2) |
| --navbar-padding-inline | var(--space-4) |
| --navbar-z-index | 50 |
| --navbar-bg | var(--color-surface) |
| --navbar-border-color | var(--color-border-subtle) |
| --navbar-border-width | var(--border-width) |
| --navbar-color | var(--color-on-surface) |
| --navbar-shadow | none |
| --navbar-toggle-bar-block-size | 0.8125rem |
| --navbar-toggle-bar-inline-size | 1.375rem |
| --navbar-toggle-bar-thickness | 2px |
Variants and states
Variants
.navbar-lg.navbar-sm.navbar-sticky
Inside it
.navbar-brand.navbar-toggle.navbar-nav.navbar-split.navbar-actions
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
:focusdata-legacy
Before you ship it
What you have to do 5 requirements
- Name every landmark.
aria-labelon each<nav>and on the<dialog>. - Name the toggle. It is icon-only and this file draws its glyph, so it has no text:
aria-labelon the button. - Ship the drawer, or drop the toggle. A toggle whose
commandforpoints at nothing is a dead control. - Mark the current page in both copies of the list with
aria-current="page"— they are two elements and neither knows about the other. - Raise
--scroll-paddingif the bar is sticky and tall. The reset setsscroll-padding-block-start: var(--scroll-padding, 6.25rem)onhtml, which is what stops an in-page anchor landing underneath a sticky header; a taller bar needs a bigger value.
src/css/components/navbar.css · npx mostlycss add navbar