Skip to content
Primitives

Tabs

Hairline-split tabs with a signal active segment.

● LIVE
All nodes nominal. Subnet load within tolerance.
npx shadcn@latest add https://okibi.cndr.dev/r/tabs.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/tabs.json

Install the required dependencies:

npm install @radix-ui/react-tabs

Copy the source from the Code tab above into components/ui/tabs.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

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

<Tabs defaultValue="overview">
  <TabsList>
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="telemetry">Telemetry</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">Overview panel…</TabsContent>
  <TabsContent value="telemetry">Telemetry panel…</TabsContent>
</Tabs>

Anatomy

<Tabs defaultValue="a">      {/* root; pairs triggers to panels by value */}
  <TabsList>                 {/* the bordered segmented strip */}
    <TabsTrigger value="a" />
    <TabsTrigger value="b" />
  </TabsList>
  <TabsContent value="a" />  {/* one panel per trigger value */}
  <TabsContent value="b" />
</Tabs>

Accessibility

  • Wraps Radix Tabs, which supplies the tablist/tab/tabpanel roles and roving-tabindex keyboard model out of the box.
  • Arrow keys move between triggers and activate the panel; Home/End jump to the first/last tab — all handled by the primitive.
  • Triggers and the active panel carry the standard square signal focus ring (ring-2 ring-ring with offset), so focus is always visible.
  • disabled triggers are skipped by keyboard navigation and dimmed to opacity-60; the active tab snaps to a solid bg-primary fill (accent-as-fill, no spark-on-spark text).
  • Inactive panels are unmounted by Radix, so there is no enter animation to replay on switch — content snaps in for an instant segmented-control feel.

Guidelines

Do

  • Match every TabsTrigger value to exactly one TabsContent value so panels resolve correctly.
  • Keep trigger labels short — the list is w-fit and triggers split by hairline rules, so a few uppercase words read best.
  • Wrap an overflowing list in a ScrollArea (or let it wrap) when you have many triggers.

Don't

  • Do not stuff a long row of triggers into the bare list — it is inline-flex w-fit with no built-in wrap or scroll affordance.
  • Do not re-add a panel enter animation; it replays on every switch and reads as load lag.
  • Do not rely on orientation='vertical' for styling — the divider rules are tuned for the horizontal strip.

On this page