Side Navigation
nswds-ui/side-navFreeComponent
Vertical section navigation with arbitrary collapsible nesting, current-page marking and auto-expansion of the active branch.
Install it
npx shadcn@latest add https://raw.githubusercontent.com/digitalnsw/nswds-ui/main/apps/registry/public/r/side-nav.jsonSource
1'use client'23import { cva, type VariantProps } from 'class-variance-authority'4import React from 'react'56import { cn } from '@/lib/utils'78import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/collapsible'9import { Link } from '@/components/link'10import { IconChevronRight } from '@/icons/chevron-right'1112/**13 * One entry in a `SideNav` tree.14 *15 * - `href` and no `links` — a leaf link.16 * - `links` — a branch. At the top level it renders as a section heading over an17 * always-visible list; at any deeper level it renders as a collapsible18 * trigger. A branch's own `href` is IGNORED (the row is a disclosure button,19 * not a link) — a dev-only warning flags it, mirroring the nswds-app20 * `SidebarNavigation` behaviour where branch rows only toggled.21 * - Neither — a dead entry; rendered as inert text and flagged with a dev-only22 * warning rather than a decoy `<a>` with no destination.23 */24type SideNavItem = {25 /** Visible text for the link, section heading, or branch trigger. */26 title: string27 href?: string28 links?: SideNavItem[]29}3031/**32 * Row treatment shared by leaf links and branch triggers — the nswds-app33 * left-rail language: each row carries its own left border sitting exactly on34 * the list's rail (the `-ml-px` on the `<li>`), so hover and the current-page35 * state recolour the rail segment beside the row.36 *37 * Departures from the source, both deliberate:38 * - The idle border is `border-transparent` rather than the bare `border-l`39 * default: idle rows show the rail through their transparent border, so the40 * idle rail colour is defined once (on the list) instead of twice.41 * - The current row adds `dark:text-white`. The source left dark active text42 * to fall through to the idle `dark:text-grey-400`, which on the43 * `dark:bg-white/20` highlight is both low-contrast and cascade-order44 * dependent; bold white ink clears WCAG 2.2 AA (1.4.3) on that overlay.45 *46 * The source's hover font-weight change (`hover:font-semibold`) is NOT ported.47 * DESIGN.md's Derived State Rule has hover, active and focus deriving from one48 * ink via `color-mix`, and a weight jump is not a derivation: it changes glyph49 * advance widths, so a label sitting near its wrap point can reflow — and grow50 * the row — under the pointer. The remaining hover treatment (rail colour,51 * 10% ink tint, ink shift) already distinguishes the state without moving52// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from @nswds/ui
All 46 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Aspect Ratioaspect-ratio | @nswds/ui | Components | Free | 2 deps · 2 files | |
| Badgebadge | @nswds/ui | Components | Free | 4 deps · 2 files | |
| Buttonbutton | @nswds/ui | Components | Free | 4 deps · 2 files | |
| Cardcard | @nswds/ui | Components | Free | 2 deps · 2 files | |
| Collapsiblecollapsible | @nswds/ui | Components | Free | 1 dep | |
| Drawerdrawer | @nswds/ui | Components | Free | 3 deps · 2 files | |
| Expandable Searchexpandable-search | @nswds/ui | Components | Free | 3 deps · 2 files | |
| Fieldfield | @nswds/ui | Components | Free | 4 deps · 2 files |
