Tabs
An accessible, WAI-ARIA compliant Tabs component for organizing content into switchable sections with full keyboard support.
Usage
Basic usage
import { Tabs } from '@nofinite/nui';
<Tabs
tabs={[
{ id: 'home', label: 'Home', content: <div>Home Panel</div> },
{ id: 'profile', label: 'Profile', content: <div>Profile Panel</div> },
{ id: 'settings', label: 'Settings', content: <div>Settings Panel</div> },
]}
/>;
With default active tab
<Tabs
defaultTab="profile"
tabs={[
{ id: 'home', label: 'Home', content: <div>Home Panel</div> },
{ id: 'profile', label: 'Profile', content: <div>Profile Panel</div> },
{ id: 'settings', label: 'Settings', content: <div>Settings Panel</div> },
]}
/>
Variants
This component does not support visual variants by design.
Guidelines:
- Tabs follow a single, consistent visual style.
- Custom styling should be applied via CSS variables or
className.
Sizes
The Tabs component does not expose explicit size props.
Guidelines:
- Control spacing and typography using global design tokens.
- Avoid mixing multiple tab sizes within the same view.
States
Tabs manage state internally.
Active state
-
Only one tab can be active at a time.
-
The active tab:
- Has
aria-selected="true" - Receives focus automatically
- Renders its associated panel
- Has
Inactive state
-
Inactive tabs:
- Have
tabIndex={-1} - Are reachable via arrow key navigation
- Have
Native Props / Composition
Tabs is a composite component built with native elements:
- Tabs →
<button role="tab"> - Tab list →
<div role="tablist"> - Panel →
<div role="tabpanel">
Custom class name
<Tabs
className="my-custom-tabs"
tabs={[
{ id: 'one', label: 'One', content: <div>One</div> },
{ id: 'two', label: 'Two', content: <div>Two</div> },
]}
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | TabItem[] | — | List of tabs with id, label, and content |
defaultTab | string | First tab id | Initially active tab |
className | string | "" | Additional class names for root container |
TabItem
interface TabItem {
id: string;
label: string;
content: React.ReactNode;
}
Behavior Notes
- Uses roving tabindex for correct keyboard focus management.
- Focus automatically moves to the active tab.
- Only the active panel is mounted in the DOM.
- State is uncontrolled after initialization.
Keyboard behavior:
ArrowRight→ Next tabArrowLeft→ Previous tabHome→ First tabEnd→ Last tab
Accessibility
-
Implements WAI-ARIA Tabs Authoring Practices.
-
Roles:
tablisttabtabpanel
-
Attributes:
aria-selectedaria-controlsaria-labelledby
-
Keyboard accessible with full arrow navigation.
-
Visible focus indicator via
:focus-visible.
Layout
- Tabs stretch to full container width by default.
- Tab list is horizontal using flex layout.
- Panels stack vertically below the tab list.
<div style={{ maxWidth: 600 }}>
<Tabs tabs={tabs} />
</div>
Best Practices
Do
- Use short, clear labels for tabs.
- Keep tab content related and comparable.
- Ensure tab IDs are unique and stable.
Don’t
- Use tabs for sequential workflows.
- Nest tabs deeply inside other tabs.
- Change tab order dynamically during interaction.