Skip to main content

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

Inactive state

  • Inactive tabs:

    • Have tabIndex={-1}
    • Are reachable via arrow key navigation

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

PropTypeDefaultDescription
tabsTabItem[]List of tabs with id, label, and content
defaultTabstringFirst tab idInitially active tab
classNamestring""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 tab
  • ArrowLeft → Previous tab
  • Home → First tab
  • End → Last tab

Accessibility

  • Implements WAI-ARIA Tabs Authoring Practices.

  • Roles:

    • tablist
    • tab
    • tabpanel
  • Attributes:

    • aria-selected
    • aria-controls
    • aria-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.