From 0afcb06529c4c93aeb357d9554b6b16807bbe5ee Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 5 May 2026 11:38:35 -0400 Subject: [PATCH] feat(headless): add Tabs primitive --- packages/headless/package.json | 4 + .../headless/src/primitives/tabs/README.md | 199 ++++++++ .../headless/src/primitives/tabs/index.ts | 9 + .../src/primitives/tabs/tabs.test.tsx | 456 ++++++++++++++++++ .../headless/src/primitives/tabs/tabs.tsx | 448 +++++++++++++++++ packages/headless/vite.config.ts | 1 + 6 files changed, 1117 insertions(+) create mode 100644 packages/headless/src/primitives/tabs/README.md create mode 100644 packages/headless/src/primitives/tabs/index.ts create mode 100644 packages/headless/src/primitives/tabs/tabs.test.tsx create mode 100644 packages/headless/src/primitives/tabs/tabs.tsx diff --git a/packages/headless/package.json b/packages/headless/package.json index 67e04bed3e3..8080f593513 100644 --- a/packages/headless/package.json +++ b/packages/headless/package.json @@ -8,6 +8,10 @@ "import": "./dist/primitives/accordion/index.js", "types": "./dist/primitives/accordion/index.d.ts" }, + "./tabs": { + "import": "./dist/primitives/tabs/index.js", + "types": "./dist/primitives/tabs/index.d.ts" + }, "./dialog": { "import": "./dist/primitives/dialog/index.js", "types": "./dist/primitives/dialog/index.d.ts" diff --git a/packages/headless/src/primitives/tabs/README.md b/packages/headless/src/primitives/tabs/README.md new file mode 100644 index 00000000000..ab3761db82d --- /dev/null +++ b/packages/headless/src/primitives/tabs/README.md @@ -0,0 +1,199 @@ +# Tabs + +A tabbed interface with automatic or manual activation, keyboard navigation, and an animated indicator. Panels are shown/hidden via the HTML `hidden` attribute (not unmounted). + +## When to Use + +- Switching between views or content sections within the same page area. +- When you need accessible tab navigation with `role="tablist"` / `role="tab"` / `role="tabpanel"`. +- Prefer Tabs over Accordion when content sections are mutually exclusive and should feel like parallel views. + +## Usage + +```tsx +import { Tabs } from '@/primitives/tabs'; + + + + Account + Security + Notifications + + + Account settings content + Security settings content + Notification preferences content +; +``` + +### Controlled + +```tsx +const [value, setValue] = useState('tab1'); + + + {/* ... */} +; +``` + +### Manual Activation + +By default, arrowing to a tab immediately activates it. Use `activationMode="manual"` to require Enter/Space: + +```tsx +{/* Arrow keys move focus, Enter/Space activates */} +``` + +### Vertical Orientation + +```tsx +{/* Arrow Up/Down navigates instead of Left/Right */} +``` + +## Parts + +| Part | Default Element | Description | +| ---------------- | --------------- | -------------------------------------------------- | +| `Tabs` | — | Root context provider | +| `Tabs.List` | `
` | Container for tabs (`role="tablist"`) | +| `Tabs.Tab` | `