Skip to content

SideNavigation

C-50 · Components · Navigation

All elements · SideNavigation on the Components page

Last checked 2026-10-22 · Awaiting approval · Accessibility tests

Copies the bun run a11y:approve command for SideNavigation.

SideNavigation

C-50WCAG 2.2 AA · Passed

The app's rail of destinations, with nested levels the user opens and closes (USWDS Side navigation). The App shell places it in its nav column and adds the tree extensions (groups, row menus, organize mode); here it stands alone, as it would in a settings area or a help center. The site's own Contents rail is one too.

Axis Values
nav.qf-nav[aria-label] › __items the landmark, named for what it lists ("Main", "Settings"), and its scrolling list of rows
a.qf-nav__item › .qf-icon · __label · __badge a destination: optional leading icon, the label (ellipsis when long), an optional count
__row › __twisty[aria-expanded] · __item · __action a destination with children: the twisty (a button named "Expand / Collapse …") shows or hides its __subitems; an optional row action
__subitems + style="--qf-nav-depth: N" · [hidden] a nested level, indented N steps; collapsed with hidden
__item[aria-current="page"] current page: the chrome fill, which its parent rows keep, so the path to it stays marked when a level is open
__section a heading row that names a run of items
In the shell .qf-shell > .qf-nav: placed in the nav column, with fillets joining the current row to the app bar; tree extensions in composites/nav-tree.css

Guidance

When to use

  • List the app's main destinations in the shell's nav column, where they stay in view on every page.
  • Show a hierarchy of up to three levels the user moves through often, such as Cases › Benefit claims › Supporting documents, opening and closing levels with the twisty.
  • Give a settings area, a help center or an admin console its own section list beside the content, outside the shell.
  • Mark the page the user is on with aria-current="page", so the rail doubles as a "you are here".

When to consider something else

  • When the views are peers of one record and switch in place without changing the page, use Tabs. See Tabs.
  • When the user browses and selects items in a hierarchy (folders, a taxonomy) rather than going to pages, use Tree. See Tree.
  • When the user only needs the path back up to the root, add a Breadcrumb above the title instead. See Breadcrumb.
  • When the destinations are a handful of secondary links (Help, Settings), put them in the shell's utility strip. See App shell.

Usability

Do
  • Keep labels short and in the words of the page titles they lead to.
  • Keep the order stable; let users hide, not reorder, items unless the app has an organize mode (nav-tree.css).
  • Open the level that holds the current page when a page loads, and leave the others as the user left them.
  • Use __section headings to split a long rail into named runs instead of nesting deeper.
  • Use __badge only for a count the user acts on, such as new items in a queue.
Don’t
  • Don't nest more than three levels; deeper paths belong in the page (a Tree or a Breadcrumb).
  • Don't make a row with children only a toggle when it is also a page; give it both the twisty and the link.
  • Don't put actions that change data (Delete, Archive) in the rail; row actions open a menu.
  • Don't hide the current page inside a collapsed level.

Accessibility

Keyboard

SideNavigation keyboard
Keys Action
Tab / Shift+Tab Moves through the twisties, links and row actions in reading order.
Enter Follows the focused link.
Enter or Space on a twisty Opens or closes its level; focus stays on the twisty.
Shift+F10 or the context-menu key In the shell, opens the focused row's menu (nav-tree.css, D-3).

Roles and names

  • The rail is a nav landmark with an aria-label that says what it lists ("Main", "Settings", "Case management"); use different names when a page has more than one.
  • Destinations are links (a.qf-nav__item); a row that only groups others is a button with aria-expanded and aria-controls.
  • Each twisty is a button with aria-expanded, aria-controls naming its __subitems, and an aria-label of "Expand …" or "Collapse …" with the row's label.
  • The current page carries aria-current="page"; only one item does.
  • Icons are aria-hidden; every item keeps a visible text label. A __badge count needs words for screen readers when the number alone is unclear ("12 open").
  • A collapsed level is hidden, so its links leave the Tab order and the accessibility tree.

Focus

  • Every row part shows the focus ring; on the current row's chrome fill the ring is drawn inset in white (light) or blue-900 (dark) so it keeps 3:1.
  • Opening or closing a level never moves focus.
  • The page's skip link goes past the rail to the content.

Other requirements

  • Item text is 9.03:1 on the page (6.49:1 in dark); the current row is 9.44:1 white on the chrome fill and 14.76:1 on a nested current row.
  • Rows are 1.75rem tall, twisties 1.5 × 2rem and row actions 2rem, meeting 2.5.8.
  • The current page is marked by a fill and bold text, not color alone, and its parents keep the fill so the path stays visible (1.4.1).
  • Long labels end in an ellipsis; give them a tooltip or keep them short so 1.4.10 reflow does not hide meaning.

Test your implementation too. This guidance covers the element as specified here. How a screen composes it (labels, order, announcements) still needs its own keyboard and screen reader check.

Accessibility tests

WCAG 2.2 AA · Passed

Passed the gate (run 358b316 on 2026-10-22): no finding in any theme. Awaiting a person’s approval of the checklist below.

SideNavigation test checklist
Area Automated A person checks
General Passed Color is never the only cue, text alternatives say the same thing as the graphic, and the element reads the same in the light and the dark theme.
Zoom Passed At 200% zoom, at 320 CSS pixels wide and with the WCAG text-spacing overrides, nothing is lost, clipped or overlapping.
Keyboard Passed Every row of the Keyboard table works, focus is always visible and never trapped, and the tab order follows the reading order. (4 rows)
Screen reader Passed With NVDA or JAWS on Windows and VoiceOver on macOS, names, roles and states are announced as Roles and names says, and changes are announced once. (6 items)

Approving. When the checklist passes on the checked run, record it with bun run a11y:approve -- side-navigation --by "Your name" and commit a11y/approvals.json with the rebuilt pages. All test status

Package

Consumers link one stylesheet, css/classic/index.css, which includes every element. The files below are where this element is defined.

SideNavigation package
Source css/classic/components/side-navigation.css
css/classic/composites/nav-organize.css
css/classic/composites/nav-tree.css
Specimens also use Box · Card · Chip · Icon · Table · Text · Toolbar
Sprites sprites/icons.svg
React Not yet available: the React binding has not started.

Latest updates

SideNavigation latest updates
Date Version Breaking Description
2026-10-20 0.1 No SideNavigation (C-50) catalogs the shell's .qf-nav rail as a standalone component: its rules move from composites/shell.css to components/side-navigation.css (rows, twisties, nested __subitems, aria-current="page" and the fill its parents keep, __section, __badge), with a specimen outside the shell. The shell keeps only placement (.qf-shell > .qf-nav: the nav column, clipping, the drawer layer, the list's room under the masthead) and the fillets that join the current row to the app bar, now off outside the shell. Element pages credit .qf-nav to SideNavigation instead of the App shell

All release notes