GitHub

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.

Stable wc import '@esa/ecology/esa-sidebar-nav';

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

PropTypeDefaultDescription
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

EventTypeDefaultDescription
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

PropTypeDefaultDescription
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 expose aria-expanded.
  • The collapse toggle's aria-label flips between Collapse 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.

TokenTierResolves 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