PixelSidebar

Vertical navigation rail with sections, nested items, badges, and an optional collapsible width.

  • Sections with optional titles and nested items up to two levels deep
  • Controlled and uncontrolled collapse with width swap and toggle button
  • Tone-aware badges per item and surface-coherent borders/typography
  • aside-style nav landmark with aria-current and aria-expanded toggle
Status
stable
Category
navigation
Since
v1.9.0

API

import { PixelSidebar } from '@pxlkit/ui-kit';
PropTypeDefaultDescription
collapsibleboolean—Shows the collapse toggle in the header row.
defaultCollapsedboolean—Initial collapsed state while uncontrolled.
collapsedboolean—Collapsed state; leave unset for an uncontrolled rail.
onCollapsedChange(next: boolean) => void—Called with the collapsed state the toggle asks for.
sections (required)PixelSidebarSectionProps[]—The sections, in order.
headerReact.ReactNode—Header content beside the toggle (hidden while collapsed).
footerReact.ReactNode—Footer row content.
surface'pixel' | 'linear'—Surface override; defaults to the nearest provider.

Also takes the native attributes and event handlers of its element (HTMLAttributes<HTMLElement>).

ref points to its element (HTMLElement).

Accessibility

WCAG target: 2.1 AA

  • nav landmark
  • aria-current
  • aria-expanded toggle
  • badge aria-label

Collapse toggle uses aria-expanded; collapsed items expose their label via aria-label and title.

Keyboard

KeyActionWhen
TabMoves focus through items and the collapse toggle—
Enter / SpaceActivates the focused item or toggle—

Usage

import { PixelSidebar } from '@pxlkit/ui-kit';

const sections = [
  {
    title: 'Workspace',
    items: [
      { id: 'dashboard', label: 'Dashboard', active: true },
      { id: 'projects', label: 'Projects', badge: { label: '4', tone: 'cyan' as const } },
      { id: 'tasks', label: 'Tasks' },
    ],
  },
  {
    title: 'Account',
    items: [
      { id: 'settings', label: 'Settings' },
      { id: 'billing', label: 'Billing', badge: { label: 'NEW', tone: 'green' as const } },
    ],
  },
];

export function Default() {
  return (
    <div style={{ height: 320 }}>
      <PixelSidebar sections={sections} header={<span className="text-xs text-retro-text">pxlkit</span>} />
    </div>
  );
}

Examples

Default

import { PixelSidebar } from '@pxlkit/ui-kit';

const sections = [
  {
    title: 'Workspace',
    items: [
      { id: 'dashboard', label: 'Dashboard', active: true },
      { id: 'projects', label: 'Projects', badge: { label: '4', tone: 'cyan' as const } },
      { id: 'tasks', label: 'Tasks' },
    ],
  },
  {
    title: 'Account',
    items: [
      { id: 'settings', label: 'Settings' },
      { id: 'billing', label: 'Billing', badge: { label: 'NEW', tone: 'green' as const } },
    ],
  },
];

export function Default() {
  return (
    <div style={{ height: 320 }}>
      <PixelSidebar sections={sections} header={<span className="text-xs text-retro-text">pxlkit</span>} />
    </div>
  );
}

Collapsible

import React from 'react';
import { PixelSidebar } from '@pxlkit/ui-kit';

const sections = [
  {
    title: 'Workspace',
    items: [
      { id: 'dashboard', label: 'Dashboard', active: true },
      { id: 'projects', label: 'Projects', badge: { label: '4', tone: 'cyan' as const } },
      { id: 'tasks', label: 'Tasks' },
    ],
  },
  {
    title: 'Account',
    items: [
      { id: 'settings', label: 'Settings' },
      { id: 'billing', label: 'Billing', badge: { label: 'NEW', tone: 'green' as const } },
    ],
  },
];

export function Collapsible() {
  const [collapsed, setCollapsed] = React.useState(false);
  return (
    <div style={{ height: 320 }}>
      <PixelSidebar
        collapsible
        collapsed={collapsed}
        onCollapsedChange={setCollapsed}
        sections={sections}
        header={<span className="text-xs text-retro-text">pxlkit</span>}
      />
    </div>
  );
}

Nested items

import { PixelSidebar } from '@pxlkit/ui-kit';

export function Nested() {
  const nestedSections = [
    {
      title: 'Library',
      items: [
        {
          id: 'components',
          label: 'Components',
          active: true,
          nested: [
            { id: 'buttons', label: 'Buttons' },
            { id: 'forms', label: 'Forms' },
            { id: 'navigation', label: 'Navigation' },
          ],
        },
        { id: 'tokens', label: 'Tokens' },
      ],
    },
  ];
  return (
    <div style={{ height: 320 }}>
      <PixelSidebar sections={nestedSections} />
    </div>
  );
}