Skip to main content

Catalog · Navigation

Navbar

Puts the main links of a site in a header's row, and in a panel under it on a narrow screen.

Fits best: any site — the bar of a site's main sections, at the top of the page

Example

A bar with the name Acme and three links: Products, Pricing and Support.

At 768 px of window width and below the links collapse into a menu. Its panel opens under this example's bar where the browser supports CSS anchor positioning, and under the top of the window, where a header sits, elsewhere.

Known issues

If you keep pressing Tab after the last link of the open menu, focus moves on into the page and the menu stays open over it, hiding what is behind. Escape closes it.

This meets WCAG 2.4.11 (AA) and does not meet 2.4.12 (AAA).

  • Browsers without anchor positioning. Where CSS anchor positioning is supported (Chrome and Edge 125, Safari 26, Firefox 147) the panel opens under the bar wherever it ends, even with a notice or the cookie banner above it. Elsewhere it opens at --luz-bar-h from the top of the window: with enlarged text and a bar that wraps to a second row, or with a band above the bar before the page scrolls, it can cover part of the bar. Escape or a click outside closes it.
  • Forced colors in Firefox. The dim layer behind the open menu is not drawn, so the page is not dimmed. The panel keeps its border, so it is still set apart.
  • Breakpoints in Safari on a Mac. With always-visible scrollbars, Safari does not count the scrollbar in width media queries (WebKit bug 52653), so the menu collapses, or the links return to the row, when the window is up to about 15 px wider than 768 px.

Code

Copy the markup

Bundler
import "@luz-ds/catalog/components/navbar.css";
HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/navbar.min.css" integrity="sha384-0Ei6tmImyRUt2v+8h1nC5eFL476Rf4AFL1pDqjqETU8/+v4Of4/M+I+ZOzS49yN7" crossorigin="anonymous">

The navbar goes in a bar's row, after the menu button and the site's name. The bar is yours, or a header of the catalog, such as the site header, which also sets the anchor the panel opens under.

HTML
<header class="o-sticky">
  <div class="o-container o-container--inline o-row">
    <button class="luz-nav-toggle" type="button" popovertarget="site-nav" aria-label="Menu">
      <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h16M4 12h16M4 18h16"/></g></svg>
    </button>
    <a href="/">Acme</a>
    <nav class="luz-navbar" id="site-nav" aria-label="Main" popover>
      <ul class="luz-navbar__list">
        <li><a class="luz-navbar__link" href="/products/" aria-current="page">Products</a></li>
        <li><a class="luz-navbar__link" href="/pricing/">Pricing</a></li>
        <li><a class="luz-navbar__link" href="/support/">Support</a></li>
      </ul>
    </nav>
  </div>
</header>

Variants and options

Class or propertyEffect
.luz-navbar
On the <nav popover>: a row of the bar from 769 px; a panel under the bar, the width of the window, at 768 px and below
.luz-navbar__list
The <ul>: a row that wraps, with no markers; a column in the open panel
.luz-navbar__link
Each link, at least 44 px tall; with aria-current, the primary text colour and a 2 px underline
anchor-name: --luz-bar
Set on the bar, with anchor-scope: --luz-bar, by the header of a kind of site or by you: the panel opens under it. Without it, the panel opens at --luz-bar-h from the top of the window

How it works

A native popover on the <nav>, opened by a .luz-nav-toggle with popovertarget. No JavaScript: the browser gives Escape, light dismiss, the place of the open panel in the Tab order and the expanded state of the button.

  • From 769 px the closed popover is shown as a row of the bar, and the menu button is hidden.
  • At 768 px and below the panel is fixed under the bar, the width of the window, with a shadow, and a dim layer that starts under the bar so the bar is never dimmed.
  • A panel left open when the window grows stays under the bar until it is closed.
  • Without the popover API the links stay in the row and wrap, and the menu button stays hidden.
  • The submenu of the catalog (Submenu) can go in a list item.

Without JavaScript

Nothing changes: the navbar is HTML, a native popover and CSS.

Keyboard and ARIA

Every link is reached with Tab in the order of the page.

  • Narrow and closed, the links are not in the Tab order. Enter or Space on the menu button opens the panel, and the next Tab reaches its first link.
  • Escape, or a click outside, closes the panel and puts focus back on the button. The button says whether it is expanded.
  • Give the <nav> a name that no other navigation of the page has, such as "Main".
  • Mark the current page with aria-current="page", or "true" for the section a page belongs to.

Contrast and focus are in the Design tab.

Design

When to use

Use the navbar for the few main links of a site, in the header every page shares.

When not to use

Do not use it for an app with many sections, or for links in the page.

  • For many sections, or a hierarchy, use the sidebar.
  • For the place of a page in a hierarchy, use a breadcrumb.
  • For the sections of one page, use tabs or a list of in-page links.

Do and don't

Do A few links with short names, the current one underlined: the row reads in one glance.
Don't Every page of the site in the bar: the row wraps, and nothing stands out as the way in.

Writing the content

Name each link after the page it opens, in one or two words.

  • Five links or fewer; put the rest in the footer, or a submenu.
  • Name the menu button after what it opens: "Menu".

Contrast and focus

Links are secondary text on the page; the current one is primary text with an underline.

PartColour on backgroundLightDarkHigh contrastNeeds
Link
--luz-text-secondary on --luz-surface-page
6.2:1
7.5:1
19.3:1
4.5:1
Current link and its underline
--luz-text-primary on --luz-surface-page
15.8:1
15.1:1
21.0:1
4.5:1
Focus ring
--luz-focus-ring on --luz-surface-page
5.6:1
7.7:1
21.0:1
3:1

Measured from the token values of each mode, rounded down. Needs is the WCAG 2.2 AA minimum: 4.5:1 for text, 3:1 for focus rings, borders and icons.

  • Focus: each link takes the link ring, a 2 px --luz-focus-ring outline 2 px outside.
  • Current link: the underline says it as well as the colour (red line 6), and forced colors keep it.
  • Panel: it is the page surface with a divider at its foot and a shadow; the dim layer behind it never covers the bar.

Tokens it uses

The 14 tokens that components/navbar.css reads, by family. Each family links to its page, with the values in every mode. In Figma they are variables with the same names: see Tokens in Figma.

Spacing and sizing
--luz-control-lg, --luz-space-2xs, --luz-space-lg, --luz-space-md, --luz-space-xs
Typography
--luz-font-sans, --luz-text-md
Shadows
--luz-shadow-lg

Keyboard and ARIA are in the Code tab.