# Nav

## Overview

Nav is the visual expression of navigation links in this design system. Its `.side-nav` variant presents vertical navigation beside page or application content. Primary navigation may also contain one-level native dropdown submenus.

The component styles a native `<nav>` with a direct child `<ul>` or `<ol>` as a sticky horizontal row of links.

Users move to another destination through real links; no content is hidden behind tab panels.

Use breadcrumbs for hierarchy trails and pagination for paged result controls.

## When to use

Use nav for visible links between peer destinations such as sections, pages, or documentation areas. Use `.side-nav` when those destinations belong in vertical navigation beside the main content.

## When not to use

Do not use nav for actions, hierarchy trails, unrelated disclosure content, or paged result controls; use buttons, breadcrumbs, accordion, or pagination. Use a JavaScript-backed ARIA tree instead of `.side-nav` when an application requires arrow-key navigation, type-ahead, multiselect, inline renaming, or drag and drop.

## Usage

```html
<nav aria-label="Site">
  <ul>
    <li><a href="/overview">Overview</a></li>
    <li><a href="/tokens" aria-current="page">Tokens</a></li>
    <li><a href="/components">Components</a></li>
  </ul>
</nav>
```

Use this pattern when users need to jump between peer destinations such as sections, pages, or documentation areas while keeping all content visible.

Do not use buttons when the interaction changes location instead of triggering an action.

Use native `<details>` and `<summary>` when a primary navigation category needs a short list of related destinations. The complete dropdown API is a `details` element inside a top-level list item, with a `summary` followed by one nested list of links:

```html
<nav aria-label="Primary">
  <ul>
    <li>
      <details name="primary-navigation">
        <summary>Resources</summary>
        <ul>
          <li><a href="/resources/forms/">Forms</a></li>
          <li><a href="/resources/help/">Help Center</a></li>
        </ul>
      </details>
    </li>
  </ul>
</nav>
```

Give every dropdown category in the same nav the same `name` value so native details behavior keeps only one submenu open at a time. Categories no longer have separate landing-page links. When a category needs a landing destination, make that destination the first submenu option. Use descriptive category labels, put the most important destination first, avoid redundant category links, and use breadcrumbs or page headings to provide broader context. Keep submenu depth to one level.

Native details use the existing nav control height, padding, gap, focus, and theme tokens, so summaries and links have the same appearance and height in both layout densities. Do not use hover to control a submenu, add nested submenu levels, or introduce ARIA menu and menuitem roles.

Use `nav.side-nav` for vertical navigation beside the main content. A flat list is the default; add native `<details>` and `<summary>` only when related destinations need collapsible hierarchy:

```html
<nav class="side-nav" aria-labelledby="course-files-heading">
  <h2 id="course-files-heading">Course files</h2>
  <ul>
    <li>
      <details open>
        <summary>syllabus</summary>
        <ul>
          <li>
            <a href="/files/syllabus/fall-2026.pdf" aria-current="page">fall-2026.pdf</a>
          </li>
        </ul>
      </details>
    </li>
    <li><a href="/files/reading-list.txt">reading-list.txt</a></li>
  </ul>
</nav>
```

Use only the heading and flat list when grouping is unnecessary. When hierarchy is present, nested lists communicate its structure and summaries disclose groups without JavaScript. Anchors remain ordinary links regardless of destination; mark the current resource or location with `aria-current`. Decorative file and folder icons are not required.

## Behavior

Covered by [Playwright behavior tests](../../tests/e2e/components.spec.js).

- NAV-001: Nav renders as a single horizontal row of links and allows horizontal overflow on smaller screens instead of wrapping onto multiple lines.
- NAV-002: Links should keep native link semantics instead of using tab roles or button behavior.
- NAV-003: Current-location items should be identified with `aria-current` and remain visually distinct from sibling links.
- NAV-004: The nav is sticky by default, and in-content sticky elements should inherit the shared layout offset so they stop below it instead of sliding underneath it.
- NAV-005: The direct child primary navigation uses a higher stack level than in-page navigation so page-level navs scroll underneath it.
- NAV-006: Comfortable and compact density come from the shared layout tokens for min-height, padding, and gap.
- NAV-007: `.side-nav` renders vertical navigation as a sticky side navigation at wider viewports without relying on link destinations or accessible-name text for styling. Its links match dropdown navigation links with full control padding and a primary-subtle hover surface while preserving their normal text color.
- NAV-008: Default nav styles activate only when the navigation has a direct child `ul` or `ol`, leaving navigation with a different markup contract untouched.
- NAV-009: A `.side-nav` may use marker-free nested lists with independent native `<details>` and `<summary>` disclosures; each nested level adds consistent indentation without requiring JavaScript, and nesting remains optional.
- NAV-010: Folder summaries do not receive the link hover surface. Links and summaries retain visible keyboard focus treatment, and current links use `aria-current` with a distinct selected surface.
- NAV-011: Side-navigation rows remain square, adapt to comfortable and compact layout density, and do not require authored decorative icon markup.
- NAV-012: Nested `.side-nav` disclosures override accordion presentation while preserving native disclosure semantics, and their generated indicator stops animating when reduced motion is preferred.
- NAV-013: A direct-child primary navigation fills the body's flush inline width without negative margins or compensating width expansion.
- NDD-001: A native `details` nested in a top-level nav list item provides one-level navigation submenus, while flat navigation without `details` remains unchanged.
- NDD-002: Each dropdown category uses a `summary` followed by one nested list and has no separate landing-page link or trigger control.
- NDD-003: Submenu destinations remain ordinary anchors, with the most important destination first when a category needs a landing destination.
- NDD-004: Native details expose the disclosure state and nested links without authored IDs or JavaScript.
- NDD-005: Native summaries open and close through click, tap, Enter, or Space and do not depend on hover.
- NDD-006: Dropdown categories sharing a native `details[name]` value allow only one submenu to remain open at a time without custom dismissal handling.
- NDD-007: Current categories use `aria-current` on their summaries, with their selected border overlapping the nav divider like an ordinary current link; current submenu destinations use `aria-current` on their anchors.
- NDD-008: Summaries and ordinary nav links use the same computed control height in comfortable and compact layout themes.
- NDD-009: Open nested lists retain the flat dropdown surface, border, radius, and constrained overflow treatment without an elevation shadow.
- NDD-010: The dropdown markup works inside the existing mobile header navigation composition.
- NDD-011: Dropdown summaries keep their configured dimensions in default, hover, focus, and open states across comfortable and compact density themes, and their indicator respects reduced motion.
- NDD-012: At narrow viewports, non-popover navigation keeps horizontal overflow within its direct list while open submenus remain absolutely positioned over surrounding content without reflowing the navigation row.

## Accessibility

- Use a native `<nav>` with a clear accessible name through `aria-label` or `aria-labelledby` when the page has more than one navigation landmark.
- Use `.side-nav` for side-navigation styling and keep `aria-label` or `aria-labelledby` for the accessible name only.
- Keep destinations as real `<a>` elements so keyboard, history, and assistive technology behavior remain intact.
- Use `aria-current="page"` or `aria-current="location"` on the current destination when it remains a link.
- Use nested lists to express hierarchy and `<summary>` only to disclose a group. Do not add ARIA tree roles to this native sequential-keyboard-navigation pattern.
- Do not add ARIA tab roles unless the interface is a real tabbed panel system with the expected keyboard behavior.
- Use native `<details>` and `<summary>` only for short primary-navigation submenus; use the standalone `dropdown` component for compact actions or non-primary link lists.
- Put the landing destination first in the submenu when a category needs one; do not add a separate category landing link.
- Use a descriptive category label, prioritize the most important destination first, and avoid redundant category links. Use breadcrumbs and page headings for broader context.
- Mark a current category with `aria-current="page"` on its `<summary>` and a current destination with `aria-current="page"` on its anchor.
- Keep submenu depth to one level and use real anchors for every submenu destination.

## Variables

| Variable | Type | Description |
| --- | --- | --- |
| `--nav-border-color` | `<color>` | Sets the bottom border color for navigation bars; change it when nav needs more or less separation from content. |
| `--nav-color-accent-subtle` | `<color>` | Sets --nav-color-accent-subtle so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-color-focus-ring` | `<color>` | Sets --nav-color-focus-ring so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-control-border-width` | `<length>` | Sets --nav-control-border-width so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-control-gap` | `<length>` | Sets --nav-control-gap so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-control-min-height` | `<length>` | Sets --nav-control-min-height so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-control-padding-block` | `<length>` | Sets --nav-control-padding-block so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-control-padding-inline` | `<length>` | Sets --nav-control-padding-inline so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-current-background` | `<color>` | Sets the background behind the current nav item; change it when the selected page state needs a stronger or subtler surface. |
| `--nav-dropdown-background` | `<color>` | Sets the dropdown panel surface color; change it when navigation menus need a different raised surface. |
| `--nav-dropdown-border-color` | `<color>` | Sets the dropdown panel border color; change it when submenu edges need a different separation color. |
| `--nav-dropdown-min-inline-size` | `<length>` | Sets the minimum inline size for navigation dropdown panels; change it when submenu labels need more or less room. |
| `--nav-dropdown-panel-offset` | `<length>` | Sets the gap between a navigation dropdown trigger and its panel; change it when the attached menu needs tighter or looser separation. |
| `--nav-dropdown-radius` | `<length>` | Sets the dropdown panel corner radius; change it when submenu panels need to match a different control shape. |
| `--nav-focus-ring-size` | `<length>` | Sets --nav-focus-ring-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-font-family-sans` | `Any` | Sets --nav-font-family-sans so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-font-size` | `<length>` | Sets --nav-font-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-icon-font-family` | `Any` | Sets the icon font used for side-navigation disclosure indicators. |
| `--nav-icon-size` | `<length>` | Sets the disclosure indicator size for nested side-navigation groups. |
| `--nav-indent` | `<length>` | Sets the inline indentation added at each nested side-navigation level. |
| `--nav-line-height` | `<number>` | Sets --nav-line-height so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-link-color` | `<color>` | Sets the default nav link color; change it when nav links should inherit a different page or header context. |
| `--nav-link-current-color` | `<color>` | Sets the current nav item text and underline color; change it when selected navigation should use a different accent. |
| `--nav-link-hover-color` | `<color>` | Sets the hover color for nav links; change it when navigation needs a clearer interactive cue. |
| `--nav-page-min-inline-size` | `<length>` | Sets --nav-page-min-inline-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-safe-area-top` | `<length>` | Sets --nav-safe-area-top so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-space-inline` | `<length>` | Sets --nav-space-inline so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--nav-space-stack` | `<length>` | Sets the block gap used by table-of-contents navigation; change it when in-page navigation needs denser or looser item rhythm. |
| `--nav-sticky-offset` | `<length>` | Sets the sticky top offset for side navigation; change it when side nav needs to clear custom persistent chrome. |

## Depends On

- `--color-accent-subtle`
- `--color-border-subtle`
- `--color-focus-ring`
- `--color-primary-subtle`
- `--color-text`
- `--color-text-accent`
- `--font-family-sans`
- `--font-line-height-body`
- `--font-size`
- `--icon-font-family`
- `--layout-control-border-width`
- `--layout-control-gap`
- `--layout-control-icon-size`
- `--layout-control-min-height`
- `--layout-control-padding-block`
- `--layout-control-padding-inline`
- `--layout-control-radius`
- `--layout-focus-ring-size`
- `--layout-page-min-inline-size`
- `--layout-safe-area-top`
- `--layout-space-inline`
- `--layout-space-stack`
- `--layout-sticky-offset`
