@ai-created/ui
All components

Fields & selection

Tabs / useTabPanelProps

A controlled, keyboard-navigable tablist for switching between peer panels in place.

TabsTabsPropsTabuseTabPanelProps
View source

Component workbench

Configure the production component, inspect it at responsive widths, copy synchronized JSX, and share the exact state from one workspace.

Overview keeps the essential project context in one place.

Anatomy

Named regions define what belongs to the component, what remains optional, and where semantic responsibility lives.

1

Tablist

Required

Named container coordinating arrow-key navigation.

[role=tablist]
2

Tab

Required

Roving-focus button with label and optional icon.

button[role=tab]
3

Active indicator

Required

2px accent line along the active tab.

4

Panel

Required

Consumer-rendered view associated through useTabPanelProps.

[role=tabpanel]

Construction and authoring

The Figma-equivalent asset model, auto-layout, resizing, properties, slots, content bounds, localization, and governance contract.

Asset kind

component

Design library name

Components/Navigation/Tabs

Maturity

stable

Last reviewed

2026-08-29

Canvas applicability

Publish a component set with active-tab variants and nested tab instances.

Auto layout

Direction
horizontal
Gap
2px
Padding
0px
Alignment
End aligned on the active indicator
Wrap
No wrap

Resizing

Width
hug
Height
fixed
Minimum width
Content width
Maximum width
Parent or viewport width
Minimum height
36px
Maximum height
36px
Overflow
visible

Exposed design properties

PropertyTypeDefaultCode mappingOptions or notes
Active tabRequiredvariantNoneactiveNo additional constraint
Tab list labelRequiredtextViewslabelNo additional constraint
TabsRequiredslotNonetabsNo additional constraint
Tab iconinstance-swapNoneTab.iconNo additional constraint

Nested assets and slots

Tab list

primitive / required

Keyboard navigation container.

<div role="tablist">

Tab

subcomponent / required

Repeatable label, optional icon, and indicator.

Tab data item

Tab panel

helper / required

Consumer-owned related content panel.

useTabPanelProps

Content limits

Tabs

Recommended two to five peer views

Overflow: Use page navigation or another information architecture for larger sets.

All labels should remain visible without horizontal ambiguity.

Tab label

One or two words, maximum 20 characters

Overflow: Do not truncate.

Short labels improve comparison and keyboard predictability.

Localization and RTL

Translatable: Tab list label, Tab labels

  • Visual order follows document direction.
  • Left and right arrow expectations must be tested for the supported browser behavior.
  • Test all labels at twice English length.
  • Test panel relationships after translation.

Responsive behavior

Intrinsic single row owned by its container.

Parent-selected narrow breakpoint

  • Do not wrap tab rows.
  • Use horizontal scrolling only with an explicit overflow affordance, otherwise choose a different navigation pattern.

Authoring limits

  • Prototype active variants do not validate roving focus or panel relationships.
  • Consumers must pass the same stable id to Tabs and useTabPanelProps.

Accountable owner

Design systems navigation primitive owner

Canonical source: src/components/Tabs.tsx

  • Changes to tab order or activation require keyboard and information-architecture review.
  • A breaking property, token, or behavior change requires design and engineering approval, a migration note, and a major release.
  • The design asset, implementation, documentation, accessibility contract, and regression coverage must ship in the same change.
Read the system construction standard

Visual specification

These measurements and semantic tokens define the supported visual contract. Local overrides should preserve the same hierarchy and interaction cues.

Tab padding

16px horizontal / 8px vertical

Tab gap

2px

Icon/label gap

8px

Active indicator

2px high

Top radius

6px

Rules

  • Use one concise row when possible.
  • The active state combines background, text, and indicator, not color alone.
  • Render only peer views that share the same page context.

Responsive behavior

  • The consumer should provide horizontal scrolling or an alternate pattern if labels overflow.
  • Do not wrap one tablist over multiple lines.

Token dependencies

--color-highlight

Active tab background.

--color-accent

Active indicator.

--color-text / --color-text2 / --color-text3

Active, hover, and inactive hierarchy.

--motion-fast

Color transition.

States and behavior

A component is not complete until its passive, interactive, disabled, and exceptional states remain understandable in both themes.

active

Trigger
Tab key equals active.
Visual response
Highlight background, primary text, and accent indicator.
Behavior
tabIndex is 0; associated panel is visible.
Accessibility
aria-selected true.

inactive

Trigger
Tab key differs from active.
Visual response
Tertiary text that strengthens on hover.
Behavior
tabIndex is -1; activation selects it.
Accessibility
aria-selected false.

focus-visible

Trigger
Roving focus reaches a tab.
Visual response
Global button focus outline.
Behavior
Arrow, Home, and End select and focus another tab.

Accessibility contract

Semantics, accessible naming, keyboard behavior, focus and announcements are implementation requirements, not optional documentation.

Semantics
Explicit tablist, tab, and tabpanel roles with aria-selected, aria-controls, and aria-labelledby.
Accessible name
Required label names the tablist; each tab label names its tab.
  • Pass the same stable id to Tabs and useTabPanelProps so relationships are emitted.
  • Render one panel for every tab key and hide inactive panels.
  • Keep tab keys DOM-id safe and unique.
  • Icons are decorative.

Keyboard behavior

Arrow Right / Arrow Left
Selects and focuses the next / previous tab with wraparound.
Home / End
Selects and focuses the first / last tab.
Enter / Space
Native button activation selects a focused tab.
Tab
Moves from the active tab into the active panel or next focus target.

Implementation

Use the public package API and preserve the controlled-state, native-attribute, and ref contracts described below.

tsx
import { Tabs, useTabPanelProps, type Tab } from '@ai-created/ui';

Client Component required

  • Tabs is controlled and uses automatic activation during arrow navigation.
  • Without a shared provided id, DOM ids are generated but cross-component aria relationships are intentionally omitted.
  • useTabPanelProps returns role, ids, tabIndex, and hidden.
tsx
const panel = useTabPanelProps("overview", active, tabsId);
<Tabs id={tabsId} tabs={tabs} active={active} onChange={setActive} label="Project views" />
<div {...panel}>Overview content</div>

Public API

These props and helpers are compatibility contracts. Changes require migration guidance and release notes.

PropTypeDefaultContract
tabsTab<T>[]requiredKeys, labels, and optional icons.
activeTrequiredActive tab key.
onChange(key: T) => voidrequiredCalled when tab changes.
labelstringrequiredTablist accessible label.
idstringgenerated idStable group id for panels.

Usage guidance

These rules keep the component recognizable across products without preventing deliberate composition.

Do

  • Use for two to six related peer views.
  • Keep labels short and use a stable default tab.

Do not

  • Do not use tabs for primary site navigation.
  • Do not place unrelated tasks in the same tablist.

Testing contract

The component should be verified as rendered UI, an interaction model, an accessibility surface, and a theme-aware visual artifact.

Unit

  • Emits ids and panel relationships only when the stable id is shared.
  • Handles empty tabs without errors.

Interaction

  • Click, arrow wraparound, Home, End, focus movement, and panel visibility work.

Accessibility

  • Roving tabIndex, selected state, tablist name, and panel relationships are valid.

Visual

  • Capture each active tab, focus, optional icons, overflow pressure, and themes.

Related specifications