Tabbed panel with roving tabindex and WAI-ARIA keyboard navigation, available as a sugar items[] API or a compositional List/Trigger/Panel API.
Sugar API (items[]) for quick setup or compositional API (List/Trigger/Panel) for full control over rendering.
Horizontal or vertical orientation with arrow-key navigation, Home/End edges, and roving tabindex.
Automatic activation (select on focus) or manual activation (Enter/Space confirms).
Optional scrollable tablist with a fade-mask for overflow, and keepMounted for persistent panel state.
Pixel and linear surfaces inherit kit-wide tokens; controlled or uncontrolled via value/defaultTab.
Status
stable
Category
navigation
Since
v1.0.0
API
import { PixelTabs } from '@pxlkit/ui-kit';
PixelTabs
Prop
Type
Default
Description
items
TabItem[]
—
The tabs as data. Without them, compose PixelTabs.List, PixelTabs.Trigger and PixelTabs.Panel.
defaultValue
string
—
Uncontrolled initial active tab id. Canonical name; aliases defaultTab.
defaultTab
string
—
Deprecated. Use defaultValue instead. Retained as alias for one minor.
value
string
—
Controlled active tab id. When set, defaultValue is ignored.
onChange
(id: string) => void
—
Fires whenever the active tab changes (controlled or uncontrolled).
surface
'pixel' | 'linear'
—
Visual surface treatment override.
ariaLabel
string
'Tabs'
Accessible label for the tablist.
orientation
'horizontal' | 'vertical'
'horizontal'
Layout direction for the tab list + keyboard nav.
keepMounted
boolean
false
When true, all panels render in the DOM (hidden via CSS).
scrollable
boolean
false
When true, the tablist scrolls horizontally with a fade-mask. Ignored for vertical orientation.
activationMode
'automatic' | 'manual'
'automatic'
'automatic' (default) selects on focus. 'manual' requires Enter/Space.
children
React.ReactNode
—
Compositional children. Ignored when items is provided.
ref points to <div>.
PixelTabs.List
Prop
Type
Default
Description
ariaLabel
string
'Tabs'
Accessible label announced for the tablist.
scrollable
boolean
false
When true, the tablist scrolls horizontally with a fade-mask. Ignored for vertical orientation.
Also takes the native attributes and event handlers of <div> (HTMLAttributes<HTMLDivElement>), except role.
ref points to <div>.
PixelTabs.Trigger
Prop
Type
Default
Description
value* (required)
string
—
Tab id this trigger controls — must match a PixelTabsPanel value.
icon
React.ReactNode
—
Optional leading icon node.
Also takes the native attributes and event handlers of <button> (ButtonHTMLAttributes<HTMLButtonElement>), except role.
ref points to <button>.
PixelTabs.Panel
Prop
Type
Default
Description
value* (required)
string
—
Tab id this panel belongs to — must match a PixelTabsTrigger value.
keepMounted
boolean
—
Override the root's keepMounted setting for this panel.
bordered
boolean
false
Render with surface-aware border + radius chrome. Defaults to false (no chrome).
Also takes the native attributes and event handlers of <div> (HTMLAttributes<HTMLDivElement>), except role.
ref points to <div>.
Accessibility
WCAG target: 2.1 AA
tablist
Renders role="tablist" on the list container with aria-orientation reflecting the orientation prop, role="tab" on each trigger with aria-selected and aria-controls wiring, and role="tabpanel" on the panel with aria-labelledby pointing back at its trigger. Panels are tabIndex=0 so keyboard users can reach panel content after activating a tab. The active panel is the only one rendered by default; keepMounted preserves all panels in the DOM and uses the hidden attribute to mask inactive ones.
Keyboard
Key
Action
When
Tab
Moves focus into/out of the tablist; only the active tab is in the tab sequence (roving tabindex).
—
ArrowRight
Focuses the next tab (wraps to first).
orientation="horizontal"
ArrowLeft
Focuses the previous tab (wraps to last).
orientation="horizontal"
ArrowDown
Focuses the next tab (wraps to first).
orientation="vertical"
ArrowUp
Focuses the previous tab (wraps to last).
orientation="vertical"
Home
Focuses the first tab in the list.
—
End
Focuses the last tab in the list.
—
Enter
Activates the focused tab.
activationMode="manual"
Space
Activates the focused tab.
activationMode="manual"
Usage
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function Default() {
return <PixelTabs items={ITEMS} defaultValue="overview" />;
}
Examples
Default
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function Default() {
return <PixelTabs items={ITEMS} defaultValue="overview" />;
}
Controlled
import { useState } from 'react';
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function Controlled() {
const [active, setActive] = useState('activity');
return (
<div className="space-y-2">
<PixelTabs items={ITEMS} value={active} onChange={setActive} />
<p className="text-xs text-retro-muted">Active tab: {active}</p>
</div>
);
}
Vertical
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function Vertical() {
return <PixelTabs items={ITEMS} defaultValue="overview" orientation="vertical" />;
}
Manual activation
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function ManualActivation() {
return (
<PixelTabs
items={ITEMS}
defaultValue="overview"
activationMode="manual"
ariaLabel="Manual activation tabs"
/>
);
}
Surfaces
import { PixelTabs } from '@pxlkit/ui-kit';
import type { TabItem } from '@pxlkit/ui-kit';
const ITEMS: TabItem[] = [
{ id: 'overview', label: 'Overview', content: <p>High-level summary of the project.</p> },
{ id: 'activity', label: 'Activity', content: <p>Recent events and commits.</p> },
{ id: 'settings', label: 'Settings', content: <p>Configuration for this workspace.</p> },
];
export function Surfaces() {
return (
<div className="grid grid-cols-1 gap-6">
<PixelTabs items={ITEMS} defaultValue="overview" surface="pixel" ariaLabel="Pixel tabs" />
<PixelTabs items={ITEMS} defaultValue="overview" surface="linear" ariaLabel="Linear tabs" />
</div>
);
}