/// Navigation
NavLink
Navigation link row for sidebars and menus — leading icon, label, optional description and trailing count/chevron, an inked active row with a 2px accent leading rule, and collapsible nested sub-links.
import { NavLink } from "@protocore/pds";Basics
Each row is a NavLink.Root with an icon and label. Mark the current row active — it inks and grows the 2px accent leading rule. Add a count for an at-a-glance total.
Descriptions
A description adds a sans-13 muted second line — useful when a label alone is ambiguous. Keep both lines to one line each; the row truncates rather than wraps.
Nested sub-links
Wrap child rows in a NavLink.Sub to get a collapsible parent (Radix Collapsible). The parent label becomes a disclosure button with a rotating caret; children render indented.
Router links
Pass asChild to render your framework's <Link> as the row while NavLink still draws the icon and trailing affordance. In asChild mode the label is the child's own text and description is unsupported (it needs a second row).
When to use it
NavLink is the row primitive for a Sidebar, a slide-in Sheet menu, or any vertical navigation list. Sidebar gives you the rail, groups, and mono UPPERCASE group labels; NavLink is the richer item when you need an icon, a count, a description, or collapsible children.
- Reach for
Sidebar.Itemfor a plain label row; reach forNavLink.Rootwhen the row carries an icon, count, or description. - Use
NavLink.Subfor one level of nesting. Deeper trees read better in aTreeView. - The
countis metadata (mono, muted) — not a status. For a colored status use aBadgeinrightSection. activeis a visual promise of the current location: set it on exactly one row per view.
Usage
Do
- Set `active` on exactly one row — the current location — driven by your router.
- Use `NavLink.Sub` for collapsible sections; the caret signals expandability.
- Pass `asChild` to keep client-side navigation with a framework `<Link>`.
- Keep labels short; add a description only when the label is ambiguous.
Don't
- Mark more than one row active — the accent is a single-location signal.
- Put a colored status pill in `count`; count is muted metadata.
- Nest `NavLink.Sub` more than one level deep — use TreeView for real trees.
- Rely on the trailing chevron alone to convey state — it is decorative.
Accessibility
| Keys | Action |
|---|---|
| Tab | Move focus to the next row (link or Sub trigger) |
| Enter | Follow the focused link, or toggle a Sub disclosure |
| Space | Toggle the focused Sub disclosure |
- The active row carries `aria-current="page"` so assistive tech announces the current location.
- NavLink.Sub renders a Radix Collapsible: the trigger is a real `<button>` with `aria-expanded` and its content is associated for screen readers.
- A disabled row sets `aria-disabled` and drops pointer events; give every icon-only affordance a text label via the row's `label`.
- Wrap a set of rows in a `<nav aria-label>` so the group is announced as a navigation landmark.