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.
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
import "@luz-ds/catalog/components/sidebar.css";
<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">
<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>
import { initSidebar } from "@luz-ds/catalog/sidebar";
initSidebar();
Variants and options
| Class or attribute | What 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-name, __logo, __version | An optional brand block at the top |
.luz-sidebar, __section-label | A group of links and its label |
.luz-sidebar | 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 | 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 | 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()fromsidebar.jsis 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.jsruns 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 bydata-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>witharia-labelwhen 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
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.
| Part | Colour on background | Light | Dark | High contrast | Needs |
|---|---|---|---|---|---|
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-ringoutline. - 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.
- Color
--luz-focus-ring,--luz-outline,--luz-scrim,--luz-sidebar-active-bg,--luz-sidebar-active-text,--luz-sidebar-bg,--luz-sidebar-border,--luz-sidebar-hover,--luz-sidebar-label,--luz-sidebar-logo-bg,--luz-sidebar-text,--luz-sidebar-text-hover,--luz-surface-hover,--luz-surface-page,--luz-text-primary- 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.