Sidebar Nav
Collapsible side navigation with grouped sections, accordion children, badges, and a collapse-to-rail toggle. A Lit web component, because the collapse and accordion states are real runtime behavior.
Preview
Use the chevrons button at the top to collapse to an icon rail; click Reports to expand its children.
<esa-sidebar-nav id="demo"></esa-sidebar-nav>
<script>
const el = document.getElementById('demo');
el.items = [
{ label: 'Dashboard', href: '/dashboard', active: true, group: 'Main' },
{ label: 'Projects', href: '/projects', badge: 12, group: 'Main' },
{ label: 'Reports', group: 'Main', children: [
{ label: 'Monthly', href: '/reports/monthly' },
{ label: 'Quarterly', href: '/reports/quarterly' },
] },
{ label: 'Team', href: '/team', group: 'Admin' },
{ label: 'Billing', href: '/billing', group: 'Admin', disabled: true },
{ label: 'Settings', href: '/settings', group: 'Admin' },
];
</script> States
Non-collapsible — set collapsible="false" to hide the toggle and lock the sidebar at full width.
<esa-sidebar-nav id="demo2" collapsible="false"></esa-sidebar-nav> API
| Prop | Type | Default | Description |
|---|---|---|---|
items | EsaSidebarNavItem[] | [] | Nav items (property, set via JS). Each is { label; href?; icon?; badge?; children?; disabled?; group?; active? }. Consecutive items sharing a group render under a group heading. |
collapsed | boolean | false | Collapses to an icon rail. Reflected as an attribute. |
collapsible | boolean | true | Shows the collapse toggle. Set false to lock the sidebar open. |
Events
| Event | Type | Default | Description |
|---|---|---|---|
collapsedchange | CustomEvent | — | Fired when the collapse toggle is clicked. detail: { collapsed: boolean }. Bubbles and composed. |
items is an array property — set it in JS, not as an attribute. Each EsaSidebarNavItem.icon is a raw inline SVG string.
Slots
| Prop | Type | Default | Description |
|---|---|---|---|
header | slot | — | Logo / brand area pinned above the nav list. |
Accessibility
- Renders a
<nav aria-label="Sidebar navigation">over a semantic list. - Active leaf links carry
aria-current="page"; parent accordions exposearia-expanded. - The collapse toggle's
aria-labelflips betweenCollapse sidebar/Expand sidebar. - Disabled items are removed from the tab order (
tabindex="-1",pointer-events: none).
Theming surface
Every public token this component reads, extracted from its source at build time.
The Resolves to column walks each token's real lineage —
component → semantic → primitive → raw value — so you
can see exactly where re-pointing it lands. A spoke re-skins by overriding these in
its theme-<slug>.css; component internals are never edited. No
hook for what you need? File it with /request-lego.
| Token | Tier | Resolves to |
|---|---|---|
--focus-ring-color | component | #65ba74 via --color-border-focus → --color-grass-8 |
--focus-ring-width | component | 2px |
--sidenav-bg | component | #f5f5f5 |
--sidenav-border | component | #e4e4e7 |
--sidenav-link-text | component | #646464 via --color-text-secondary → --color-gray-11 |
--sidenav-link-text-active | component | #2a7e3b via --color-primary-strong → --color-grass-11 |
--sidenav-section-text | component | #838383 via --color-text-muted → --color-gray-10 |
--color-primary | semantic | #46a758 via --color-grass-9 |
--color-primary-subtle | semantic | #f5fbf5 via --color-grass-2 |
--color-surface-sunken | semantic | #f0f0f0 via --color-gray-3 |
--color-text-inverse | semantic | #fcfcfc via --color-gray-1 |
--focus-ring-offset | semantic | 2px |
--letter-spacing-wide | semantic | 0.03em |
--sidebar-width | semantic | 280px |
--sidebar-width-collapsed | semantic | 72px |
--transition-base | semantic | 200ms ease |
--type-size-100 | semantic | clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) |
--type-size-150 | semantic | clamp(0.6875rem, 0.61rem + 0.38vw, 0.875rem) |
--type-size-200 | semantic | clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) |
--font-weight-medium | primitive | 500 |
--font-weight-semibold | primitive | 550 |
--radius-200 | primitive | 0.5rem |
--radius-full | primitive | 9999px |
--spacing-100 | primitive | 0.25rem |
--spacing-200 | primitive | 0.5rem |
--spacing-300 | primitive | 0.75rem |
--spacing-400 | primitive | 1rem |