Primitives
Tabs
Hairline-split tabs with a signal active segment.
● LIVE
All nodes nominal. Subnet load within tolerance.
"use client";import { Tabs, TabsContent, TabsList, TabsTrigger,} from "@/registry/okibi/ui/tabs";export default function TabsDemo() { return ( <Tabs defaultValue="status" className="w-80 max-w-full"> <TabsList> <TabsTrigger value="status">Status</TabsTrigger> <TabsTrigger value="signal">Signal</TabsTrigger> <TabsTrigger value="logs">Logs</TabsTrigger> </TabsList> <TabsContent value="status" className="text-sm text-muted-foreground"> All nodes nominal. Subnet load within tolerance. </TabsContent> <TabsContent value="signal" className="text-sm text-muted-foreground"> Uplink locked at 98% strength on band Charlie. </TabsContent> <TabsContent value="logs" className="text-sm text-muted-foreground"> No intrusions recorded in the last cycle. </TabsContent> </Tabs> );}npx shadcn@latest add https://okibi.cndr.dev/r/tabs.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/tabs.jsonInstall the required dependencies:
npm install @radix-ui/react-tabsCopy 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 thetablist/tab/tabpanelroles and roving-tabindex keyboard model out of the box. - Arrow keys move between triggers and activate the panel;
Home/Endjump 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-ringwith offset), so focus is always visible. disabledtriggers are skipped by keyboard navigation and dimmed toopacity-60; the active tab snaps to a solidbg-primaryfill (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
TabsTriggervalueto exactly oneTabsContentvalueso panels resolve correctly. - Keep trigger labels short — the list is
w-fitand 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-fitwith 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.