Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Tabs

A set of layered sections of content — known as tab panels — that are displayed one at a time.

Playground

Make changes to your account here.

—variant
—orientation
—children
—disabled
Usage
Give the root a defaultValue and pair each TabsTrigger with the TabsContent that shares its value. Triggers live inside a single TabsList; only the active panel is rendered visible. Control the selection with value + onValueChange when the active tab must sync with URL or app state.

Make changes to your account here.

import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@workspace/ui/components/tabs"

<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
  </TabsList>
  <TabsContent value="account">
    Make changes to your account here.
  </TabsContent>
  <TabsContent value="password">Change your password here.</TabsContent>
</Tabs>
Line
variant="line" on the TabsList drops the muted pill background and marks the active trigger with a 2px foreground underline instead — the classic browser-tabs look for page-level navigation sitting on the background.

A summary of your workspace activity.

<Tabs defaultValue="overview">
  <TabsList variant="line">
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="analytics">Analytics</TabsTrigger>
    <TabsTrigger value="reports">Reports</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">...</TabsContent>
</Tabs>
Vertical
orientation="vertical" on the root stacks the list beside the panel and switches arrow-key navigation to Up and Down. Triggers stretch to the list width and align their labels to the start; the line variant moves its indicator to the end edge.

Make changes to your account here.

<Tabs defaultValue="account" orientation="vertical">
  <TabsList variant="line">
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
    <TabsTrigger value="settings">Settings</TabsTrigger>
  </TabsList>
  <TabsContent value="account">...</TabsContent>
</Tabs>
With icons
Drop an icon before the label inside the trigger — svgs are sized to 16px automatically and never intercept clicks. Keep the label text; icon-only triggers make weak tab affordances.

Make changes to your account here.

<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">
      <UserIcon />
      Account
    </TabsTrigger>
    <TabsTrigger value="password">
      <LockIcon />
      Password
    </TabsTrigger>
    <TabsTrigger value="settings">
      <GearSixIcon />
      Settings
    </TabsTrigger>
  </TabsList>
  <TabsContent value="account">...</TabsContent>
</Tabs>
Disabled
Pass disabled to a TabsTrigger to block just that section — it dims to 50% opacity and is skipped by both pointer and keyboard navigation. Its panel stays unreachable until the trigger is enabled again.

Make changes to your account here.

<Tabs defaultValue="account">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
    <TabsTrigger value="billing" disabled>
      Billing
    </TabsTrigger>
  </TabsList>
  <TabsContent value="account">...</TabsContent>
</Tabs>
RTL
Radix reads direction from the dir prop (or the app-wide DirectionProvider), not from the surrounding markup — a dir="rtl" wrapper alone is not enough. With the prop set, arrow keys follow the reading direction and the vertical line indicator sticks to the logical end edge.

قم بإجراء تغييرات على حسابك هنا.

<Tabs defaultValue="account" dir="rtl">
  <TabsList>
    <TabsTrigger value="account">الحساب</TabsTrigger>
    <TabsTrigger value="password">كلمة المرور</TabsTrigger>
  </TabsList>
  <TabsContent value="account">قم بإجراء تغييرات على حسابك هنا.</TabsContent>
  <TabsContent value="password">قم بتغيير كلمة المرور هنا.</TabsContent>
</Tabs>
Accessibility
Implements the WAI-ARIA Tabs pattern: tablist, tab and tabpanel roles are wired up with aria-controls and aria-labelledby automatically. The list is a single Tab stop — arrow keys move between triggers (Home/End jump to the edges) and selection follows focus by default; set activationMode="manual" to require Enter or Space when switching panels is expensive.
<Tabs defaultValue="account" activationMode="manual">
  <TabsList>
    <TabsTrigger value="account">Account</TabsTrigger>
    <TabsTrigger value="password">Password</TabsTrigger>
  </TabsList>
  <TabsContent value="account">...</TabsContent>
  <TabsContent value="password">...</TabsContent>
</Tabs>
API Reference
Tabs wraps the Radix Tabs primitive and accepts all of its props — uncontrolled via defaultValue or controlled via value + onValueChange. Orientation is set on the root and inherited by the list and triggers; the visual variant is set on the TabsList.
ComponentPropDefault
TabsdefaultValue? / value?: string—
TabsonValueChange?: (value: string) => void—
Tabsorientation?: "horizontal" | "vertical""horizontal"
TabsactivationMode?: "automatic" | "manual""automatic"
Tabsdir?: "ltr" | "rtl"inherited
TabsListvariant?: "default" | "line""default"
TabsTriggervalue: stringrequired
TabsTriggerdisabled?: booleanfalse
TabsContentvalue: stringrequired
TabsContentforceMount?: true—
<Tabs data-slot="tabs" data-orientation="horizontal|vertical" />
<TabsList data-slot="tabs-list" data-variant="default|line" />
<TabsTrigger data-slot="tabs-trigger" data-state="active|inactive" />
<TabsContent data-slot="tabs-content" data-state="active|inactive" />

Tokens used

10 design tokens consumed by the Tabs component.

  • --muted

    Muted

    bg-muted
  • --muted-foreground

    Muted Foreground

    text-muted-foregrounddark:text-muted-foreground
  • --card

    Card

    data-active:bg-card
  • --foreground

    Foreground

    text-foreground/60hover:text-foregrounddata-active:text-foregroundafter:bg-foreground
  • --input

    Input

    dark:data-active:border-inputdark:data-active:bg-input/30
  • --ring

    Ring

    focus-visible:border-ringfocus-visible:ring-ring/50focus-visible:outline-ring
  • --text-sm

    Text Small (14px)

    text-sm
  • --radius-lg

    Radius 1× (10px)

    rounded-lg
  • --radius-md

    Radius 0.8× (8px)

    rounded-md
  • --spacing

    Spacing unit (4px)

    gap-2p-[3px]h-9px-2py-1gap-1.5