Skip to main content

Catalog · Navigation

Sidebar

Holds the main navigation of a site or an app, and becomes a drawer on small screens.

Fits best: docs, app — the navigation of a site with many pages or sections, beside the content, with a drawer on a phone

Example

A sidebar with the product name, a list of links and the color mode control.

.luz-nav-toggle

On small screens the sidebar becomes a drawer. This page's navigation uses it live.

Known issues

In a browser without the popover API the navigation does not become a drawer: it stays visible above the content on small screens.

Code

Copy the markup

Bundler
import "@luz-ds/catalog/components/sidebar.css";
HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/sidebar.min.css" integrity="sha384-vt36+ffiZY/k7pMatnSg+vcsgcYLY2cJSuWh3NBijr6OCkoyR0URAZbP0j6jTpjY" crossorigin="anonymous">
HTML
<header>
  <button class="luz-nav-toggle" type="button" popovertarget="site-nav" aria-label="Menu">
    <span class="luz-nav-toggle__bar"></span>
    <span class="luz-nav-toggle__bar"></span>
    <span class="luz-nav-toggle__bar"></span>
  </button>
</header>

<nav class="luz-sidebar" id="site-nav" popover aria-label="Main">
  <div class="luz-sidebar__section">
    <div class="luz-sidebar__section-label">Account</div>
    <a class="luz-sidebar__link is-active" href="/profile/" aria-current="page">Profile</a>
    <a class="luz-sidebar__link" href="/billing/">Billing</a>
  </div>
</nav>
JS
import { initSidebar } from "@luz-ds/catalog/sidebar";
initSidebar();

Variants and options

Class or attributeWhat it does
.luz-sidebar
The navigation column, in the sidebar colours
popover on .luz-sidebar
Makes it a drawer at 768 px and below
.luz-sidebar__brand, __brand-name, __logo, __version
An optional brand block at the top
.luz-sidebar__section, __section-label
A group of links and its label
.luz-sidebar__link
A link
data-luz-spy on any element
Not part of the sidebar: the scroll spy also marks the links to sections of the page inside it, such as a table of contents styled your own way
.is-active on a link
Marks the current link with weight and colour; pair it with aria-current="page"
.luz-sidebar__footer
Pinned to the bottom, for example for the color mode
.luz-nav-toggle
The menu button, shown at 768 px and below; put it first in your header row
.luz-nav-toggle--fixed
Pins the menu button to the top start corner, for a page with no header

How it works

A <nav> landmark, styled by components/sidebar.css. On small screens it is a native popover drawer.

  • The menu button opens it with popovertarget, with no script. The browser gives Escape, a click outside to close, the top layer and the expanded state.
  • initSidebar() from sidebar.js is optional: it marks the link of the section in view on a long page (aria-current="true") and closes the drawer when a link is followed. catalog.js runs it on a page that has a sidebar. A page that loads only core has no scroll spy, and the drawer works without it.
  • The scroll spy finds its links by .luz-sidebar__link, and by data-luz-spy: on any element (a <nav> of contents), it spies every link inside that points at a section of the page (href="#…"). The attribute is the hook; the look of those links is yours.

Without JavaScript

The sidebar and the drawer work. The current link is the one your markup marks; it does not follow the scroll.

Keyboard and ARIA

Tab moves through the links in order. On small screens, the menu button opens the drawer; Escape or a click outside closes it, and focus goes back to the button.

  • Name the <nav> with aria-label when the page has more than one.
  • Mark the current page with aria-current="page" as well as .is-active: the class is only the look.
  • The menu button keeps the name "Menu" whether the drawer is open or closed; the popover exposes the state.

Contrast and focus are in the Design tab.

Design

When to use

Use a sidebar for the main navigation of an app or a documentation site with many sections.

When not to use

Do not use a sidebar for a site with a few pages: a header navigation is simpler.

  • For the trail to the current page, use a breadcrumb.
  • For panels of one page, use tabs.

Do and don't

Do Short nouns, grouped under a label, in the order people need them.
Don't Sentences and catch-alls as links: “Manage your billing details” is slow to scan and “Other stuff” says nothing.

Writing the content

Name each link after the page it opens, in a word or two.

  • Section labels are nouns: "Account", "Billing".
  • Order the links by how often people need them.

Contrast and focus

The sidebar has its own tokens: light text on a dark panel in every mode, and a filled current link.

PartColour on backgroundLightDarkHigh contrastNeeds
Link
--luz-sidebar-text on --luz-sidebar-bg
10.9:1
10.0:1
14.1:1
4.5:1
Link, hover
--luz-sidebar-text-hover on --luz-sidebar-hover
13.0:1
16.4:1
17.6:1
4.5:1
Current link
--luz-sidebar-active-text on --luz-sidebar-active-bg
10.3:1
10.2:1
21.0:1
4.5:1
Current link fill
--luz-sidebar-active-bg on --luz-sidebar-bg
8.9:1
10.2:1
21.0:1
3:1
Section label and version
--luz-sidebar-label on --luz-sidebar-bg
6.7:1
6.6:1
6.4:1
4.5:1
Menu button
--luz-text-primary on --luz-surface-page
15.8:1
15.1:1
21.0:1
4.5:1
Menu button border
--luz-outline on --luz-surface-page
4.3:1
3.6:1
21.0:1
3:1
Menu button 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: a link gets a 2 px outline inside its edge, in its own text colour, so the ring has the link's own contrast; other controls in the sidebar take --luz-sidebar-text-hover. The menu button has the 2 px --luz-focus-ring outline.
  • Not colour alone: the current link is filled and carries aria-current.
  • Target: the menu button is 44 px square.

Tokens it uses

The 36 tokens that components/sidebar.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-md, --luz-space-sm, --luz-space-xl, --luz-space-xs
Typography
--luz-font-mono, --luz-font-sans, --luz-text-md, --luz-text-sm, --luz-text-xl, --luz-text-xs
Radius
--luz-radius-md
Shadows
--luz-shadow-lg
Motion
--luz-duration-base, --luz-duration-fast, --luz-duration-slow, --luz-easing-standard
Layers
--luz-z-modal, --luz-z-sidebar
Layout and breakpoints
--luz-sidebar-width

Keyboard and ARIA are in the Code tab.