Components · App chrome
Sidebar
The 240px navigation rail — a brand block, grouped nav items, and a footer profile. The active item takes a primary-bg tint with a primary-coloured icon and bold weight. The rail to your left is the real one; the preview below is a static instance.
Preview
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Sidebar" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `sidebar` spec (variants: nav-item, nav-item-active, section-label) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `sidebar` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
╭──────────────────────────╮
│ ⬡ Zetta Product │ ← sidebar-brand-block (Inter 700, 16px)
│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ ◈ Dashboard │ ← sidebar-nav-item (active — primary-bg fill)
│ ◉ Reports │ ← sidebar-nav-item (rest)
│ ◉ Settings │
│ │
│ TEAM │ ← sidebar-nav-section-label (muted, 12px)
│ ◉ Members │
│ ◉ Billing │
│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ [●] Alice Johnson │ ← sidebar-footer-profile
│ Admin │
╰──────────────────────────╯
240px wide, full viewport height
| State | Visual |
|---|---|
| Expanded | Full width, label + icon per nav item |
| Collapsed | Icon-only rail, tooltips on hover |
| Nav item — default | transparent fill, {colors.muted} icon + text |
| Nav item — hover | {colors.surface-muted} fill |
| Nav item — focus | 2px solid {colors.border-focus} inset outline |
| Nav item — active | {colors.primary-bg} fill, {colors.primary} text, 2px solid {colors.primary} left border |
| Nav group — collapsed | Chevron pointing right |
| Nav group — expanded | Chevron pointing down, children visible |
When to use
- Application layouts with 5 or more primary navigation destinations.
- Product surfaces where the user needs persistent wayfinding across sections.
When not to use
- Simple single-page tools with fewer than 4 nav items — use a top navigation bar (Header) instead.
- Marketing or public-facing pages — sidebar implies an authenticated application shell.
Do
- Group related nav items under section labels when the list exceeds 6 items.
- Mark exactly one item as active at a time — never zero, never multiple.
- Keep brand block content to logo + product name only; no interactive elements.
Don't
- Don't put actions (buttons, dropdowns) in the sidebar nav column — nav items are links only.
- Don't nest sidebar inside a scrollable parent — sidebar must span full viewport height.
- Nav item labels: concise nouns or noun phrases. 1–2 words. Title case. "Dashboard", "Team Members", "Billing".
- Section labels: short all-caps nouns. Under 15 characters. "WORKSPACE", "TEAM", "ADMIN".
- Profile block: show display name + role or email. Truncate long strings with ellipsis — do not wrap.
- Brand block: product name only. Do not include marketing copy, taglines, or version numbers.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Move focus through nav items |
Enter |
Navigate to the focused item destination |
Space |
Expand / collapse a collapsible nav group |
Escape |
Collapse expanded group (if in group context) |
Escape |
Close sidebar drawer (mobile only) |
Accessibility
- Landmark: wrap the sidebar in
<nav aria-label="Main navigation">or userole="navigation". - Active item:
aria-current="page"on the active nav item — not just a visual change. - Section labels: use
<li role="presentation">or a visually-hidden heading so screen readers announce the group. - Keyboard trap (mobile): when sidebar opens as a drawer, trap focus inside until dismissed. Restore focus to the trigger element on close.
- Accessibility theme: nav item height must be 44px minimum — override the standard 32px token. See
zetta-accessibility.mdSection 7.
Spec
Values and token references resolved straight from the Zetta spec.
sidebar
- backgroundColor
- {colors.surface-sidebar}
- borderRightColor
- {colors.hairline}
- borderRightWidth
- 1px
- width
- 240px
sidebar-brand-block
- paddingX
- 16px
- paddingY
- 24px
- fontFamily
- Inter
- fontSize
- 16px
- fontWeight
- 700
- lineHeight
- 1.3
- letterSpacing
- -0.01em
- textColor
- {colors.ink}
sidebar-nav-item
- textColor
- {colors.body}
- iconColor
- {colors.muted}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 500
- lineHeight
- 1.4
- borderRadius
- {rounded.base}
- height
- 32px
- paddingX
- 12px
- paddingY
- 8px
- gap
- 8px
- iconSize
- 20px
- cursor
- pointer
sidebar-nav-item-hover
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
- iconColor
- {colors.ink}
sidebar-nav-item-active
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.ink}
- iconColor
- {colors.primary}
- fontWeight
- 600
sidebar-nav-item-focus
- outline
- 2px solid {colors.border-focus}
- outlineOffset
- 2px
sidebar-nav-section-label
- textColor
- {colors.muted}
- fontFamily
- Geist
- fontSize
- 12px
- fontWeight
- 500
- letterSpacing
- 0.01em
- paddingX
- 12px
- paddingY
- 4px
- marginTop
- 8px
sidebar-footer-profile
- display
- flex
- flexDirection
- row
- alignItems
- center
- paddingX
- 16px
- paddingY
- 16px
- borderTopColor
- {colors.hairline}
- borderTopWidth
- 1px
- gap
- 8px
sidebar-collapsed
- width
- 60px
- overflow
- hidden
sidebar-collapsed-nav-item
- width
- 36px
- height
- 36px
- borderRadius
- {rounded.base}
- display
- flex
- alignItems
- center
- justifyContent
- center
- margin
- 0 auto
sidebar-collapse-trigger
- position
- absolute
- right
- -12px
- top
- 24px
- width
- 24px
- height
- 24px
- borderRadius
- {rounded.full}
- backgroundColor
- {colors.surface-card}
- borderColor
- {colors.hairline}
- borderWidth
- 1px
- shadow
- {elevation.shadow.raised}
- iconSize
- 12px
- iconColor
- {colors.muted}
- cursor
- pointer
- zIndex
- 1
sidebar-nav-item-badge
- marginLeft
- auto
- minWidth
- 18px
- height
- 18px
- borderRadius
- {rounded.full}
- backgroundColor
- {colors.danger}
- textColor
- {colors.danger-text}
- fontFamily
- Geist
- fontSize
- 10px
- fontWeight
- 700
- display
- flex
- alignItems
- center
- justifyContent
- center
- paddingX
- 4px
sidebar-nav-group-trigger
- chevronSize
- 14px
- chevronColor
- {colors.muted}
- chevronTransition
- transform {motion.duration-base} {motion.easing-default}
sidebar-nav-group-trigger-open
- chevronTransform
- rotate(90deg)
sidebar-nav-group-content
- paddingLeft
- 28px
- overflow
- hidden
- transition
- height {motion.duration-base} {motion.easing-default}
sidebar-nav-sub-item
- textColor
- {colors.body}
- fontFamily
- Geist
- fontSize
- 13px
- fontWeight
- 400
- height
- 28px
- paddingX
- {spacing.sm}
- borderRadius
- {rounded.base}
- cursor
- pointer
sidebar-nav-sub-item-hover
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
sidebar-nav-sub-item-active
- backgroundColor
- {colors.primary-bg}
- textColor
- {colors.primary}
- fontWeight
- 500
Known gaps
- Sidebar resize (user-draggable width between 60px and 320px) — not yet specified.
- Sidebar pinned/unpinned state persistence — runtime/product concern.