SideNav
Organismbeta@macaw-ds/ui · since 0.1.0The product's primary navigation in the left column of AppShell. Not for sections within a page; that is TabNav.
Import
import { SideNav, SideNavGroup, SideNavItem } from '@macaw-ds/ui';Props
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label* | string | Main | Names the navigation landmark, e.g. "Main". |
| title | string | Billing | On SideNav.Group: the group heading. |
| active | boolean | false | On SideNav.Item: the current page. Accessibility: Sets aria-current="page". |
| icon | ReactNode | — | On SideNav.Item: a decorative Icon before the label. |
| asChild | boolean | false | On SideNav.Item: render a router Link instead of an anchor. |
| children | ReactNode | — | SideNav.Group elements. |
Slots
Named parts you pass content into.
- Group
- Item
Tokens used
Every value this component renders comes from one of these tokens.
| Token | Light | Dark |
|---|---|---|
| color.status.accent-bg semantic · color | oklch(0.97 0.02 262) | oklch(0.24 0.08 262) |
| color.status.accent-fg semantic · color | oklch(0.38 0.15 262) | oklch(0.88 0.08 262) |
| color.fg.secondary semantic · color | oklch(0.38 0.012 250) | oklch(0.87 0.007 250) |
| color.fg.primary semantic · color | oklch(0.15 0.012 250) | oklch(0.985 0.002 250) |
| color.fg.muted semantic · color | oklch(0.48 0.012 250) | oklch(0.66 0.01 250) |
| color.surface.layer-1-hover semantic · color | oklch(0.965 0.003 250) | oklch(0.21 0.012 250) |
| size.control.sm semantic · dimension | 28px | 28px |
| text.size.label-sm semantic · dimension | 0.75rem | 0.75rem |
| text.size.caption semantic · dimension | 0.6875rem | 0.6875rem |
| color.focus.ring semantic · color | oklch(0.51 0.2 262) | oklch(0.68 0.16 262) |
Eject
Ejecting copies the source into your repo and records it in .macaw/ejected.json, so the fork stays visible. The same file is served as a shadcn-compatible registry item.
npx @macaw-ds/cli add side-nav --ejectnpx shadcn@latest add https://macaw-design-system.vercel.app/r/side-nav.jsonBundle budget: 2,000 bytes gzipped, enforced by size-limit in CI.
Manifest
registry/manifests/side-nav.json, the contract the playground, CLI, plugin and MCP server read.
{
"name": "SideNav",
"slug": "side-nav",
"package": "@macaw-ds/ui",
"status": "beta",
"since": "0.1.0",
"level": "organism",
"category": "navigation",
"purpose": "The product's primary navigation in the left column of AppShell. Not for sections within a page; that is TabNav.",
"description": "Groups of links with optional headings and icons. One item is active and carries aria-current=\"page\". Item height follows the density mode. AppShell gives it a full-height, independently scrolling column on surface layer-1.",
"exports": [
"SideNav",
"SideNavGroup",
"SideNavItem"
],
"props": {
"aria-label": {
"type": "string",
"default": "Main",
"required": true,
"description": "Names the navigation landmark, e.g. \"Main\"."
},
"title": {
"type": "string",
"default": "Billing",
"required": false,
"description": "On SideNav.Group: the group heading.",
"control": "none"
},
"active": {
"type": "boolean",
"default": false,
"required": false,
"description": "On SideNav.Item: the current page.",
"a11y": "Sets aria-current=\"page\"."
},
"icon": {
"type": "node",
"required": false,
"description": "On SideNav.Item: a decorative Icon before the label.",
"control": "none"
},
"asChild": {
"type": "boolean",
"default": false,
"required": false,
"description": "On SideNav.Item: render a router Link instead of an anchor.",
"control": "none"
},
"children": {
"type": "node",
"required": false,
"description": "SideNav.Group elements.",
"control": "none"
}
},
"slots": [
"Group",
"Item"
],
"tokens": [
"color.status.accent-bg",
"color.status.accent-fg",
"color.fg.secondary",
"color.fg.primary",
"color.fg.muted",
"color.surface.layer-1-hover",
"size.control.sm",
"text.size.label-sm",
"text.size.caption",
"color.focus.ring"
],
"a11y": {
"role": "navigation, containing lists of links",
"focusable": true,
"keyboard": [
"Tab: move between items (each is a link)",
"Enter: follow the link"
],
"name": "The landmark takes its name from aria-label; each group list from its visible title (aria-labelledby); each item from its text. Group titles are not headings, so they do not interrupt the page outline.",
"notes": [
"The active item has aria-current=\"page\" and a tinted background plus accent text, so selection is not shown by colour of one property alone.",
"Icons are decorative; the label always names the item."
]
},
"motion": {
"description": "Item background and text colour ease on hover.",
"tokens": [
"duration.fast",
"ease.standard"
]
},
"budget": {
"gzip": 2000
},
"rules": [
"Exactly one item is active, and it matches the current route.",
"Group related destinations under a short heading; keep fewer than ten items per group.",
"With a router, pass the Link through asChild so client-side navigation keeps working.",
"Place SideNav in AppShell's sideNav slot; it does not set its own width or background."
],
"dos": [
"Use icons for every item in a group or for none of them."
],
"donts": [
"Do not put actions (buttons) in SideNav; it is for navigation only.",
"Do not nest groups."
],
"examples": [
{
"name": "Default",
"props": {
"aria-label": "Main"
}
}
],
"registryDependencies": [],
"dependencies": [
"radix-ui"
],
"related": [
"app-shell",
"tab-nav",
"top-bar"
]
}