For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
Home

Tabs

A tabbed interface that displays one content panel at a time, with keyboard navigation and optional audio feedback.

Preview content
Account
Active items shown here.
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";

export const TabsDemo = () => (
    <div className="flex flex-col gap-8">
      {/* Basic */}
      <Tabs defaultValue="preview">
        <TabsList>
          <TabsTrigger value="preview" sound="tabSwitch">Preview</TabsTrigger>
          <TabsTrigger value="code" sound="tabSwitch">Code</TabsTrigger>
          <TabsTrigger value="docs" sound="tabSwitch">Docs</TabsTrigger>
        </TabsList>
        <TabsContent value="preview"><div className="bg-muted/40 flex h-20 items-center justify-center rounded-md border text-sm">Preview content</div></TabsContent>
        <TabsContent value="code"><div className="bg-muted/40 flex h-20 items-center justify-center rounded-md border text-sm">Code content</div></TabsContent>
        <TabsContent value="docs"><div className="bg-muted/40 flex h-20 items-center justify-center rounded-md border text-sm">Docs content</div></TabsContent>
      </Tabs>
      {/* With icons and card panels */}
      <Tabs defaultValue="account" className="w-full">
        <TabsList className="w-full">
          <TabsTrigger value="account" className="flex-1" sound="tabSwitch">Account</TabsTrigger>
          <TabsTrigger value="password" className="flex-1" sound="tabSwitch">Password</TabsTrigger>
        </TabsList>
        <TabsContent value="account">
          <Card>
            <CardHeader><CardTitle>Account</CardTitle></CardHeader>
            <CardContent className="flex flex-col gap-3">
              <div className="flex flex-col gap-1.5">
                <Label htmlFor="tabs-name">Name</Label>
                <Input id="tabs-name" defaultValue="Nuno Santos" />
              </div>
              <div className="flex flex-col gap-1.5">
                <Label htmlFor="tabs-username">Username</Label>
                <Input id="tabs-username" defaultValue="@nunosantos" />
              </div>
            </CardContent>
            <CardFooter><Button>Save changes</Button></CardFooter>
          </Card>
        </TabsContent>
        <TabsContent value="password">
          <Card>
            <CardHeader><CardTitle>Password</CardTitle></CardHeader>
            <CardContent className="flex flex-col gap-3">
              <div className="flex flex-col gap-1.5">
                <Label htmlFor="tabs-current">Current password</Label>
                <Input id="tabs-current" type="password" />
              </div>
              <div className="flex flex-col gap-1.5">
                <Label htmlFor="tabs-new">New password</Label>
                <Input id="tabs-new" type="password" />
              </div>
            </CardContent>
            <CardFooter><Button>Update password</Button></CardFooter>
          </Card>
        </TabsContent>
      </Tabs>
      {/* Disabled trigger */}
      <Tabs defaultValue="active">
        <TabsList>
          <TabsTrigger value="active" sound="tabSwitch">Active</TabsTrigger>
          <TabsTrigger value="archived" sound="tabSwitch">Archived</TabsTrigger>
          <TabsTrigger value="deleted" disabled>Deleted</TabsTrigger>
        </TabsList>
        <TabsContent value="active"><div className="text-muted-foreground text-sm">Active items shown here.</div></TabsContent>
        <TabsContent value="archived"><div className="text-muted-foreground text-sm">Archived items shown here.</div></TabsContent>
      </Tabs>
    </div>
  );

Full-width list

Pass className="w-full" to TabsList and className="flex-1" to each TabsTrigger to stretch across the container.

<TabsList className="w-full">
  <TabsTrigger value="account" className="flex-1">Account</TabsTrigger>
  <TabsTrigger value="settings" className="flex-1">Settings</TabsTrigger>
</TabsList>

Controlled

const [tab, setTab] = useState("preview");
 
<Tabs value={tab} onValueChange={setTab}>
  ...
</Tabs>

With sound

Pass sound to each TabsTrigger. Use "tabSwitch" for a subtle transition cue.

<TabsTrigger value="preview" sound="tabSwitch">Preview</TabsTrigger>

Vertical orientation

<Tabs orientation="vertical" className="flex-row">
  <TabsList className="h-auto flex-col">
    <TabsTrigger value="general">General</TabsTrigger>
    <TabsTrigger value="security">Security</TabsTrigger>
  </TabsList>
  <TabsContent value="general">…</TabsContent>
  <TabsContent value="security">…</TabsContent>
</Tabs>

Props

Tabs

PropTypeDefaultDescription
defaultValuestring—Uncontrolled active tab
valuestring—Controlled active tab
onValueChange(value: string) => void—Callback on tab change
orientation"horizontal" | "vertical""horizontal"Keyboard navigation axis

TabsTrigger

PropTypeDefaultDescription
valuestring—Matches a TabsContent value
disabledbooleanfalsePrevents selection
soundFeedbackType—Audio feedback on click
hapticbooleanfalseHaptic feedback on click

Usage

import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
 
<Tabs defaultValue="preview">
  <TabsList>
    <TabsTrigger value="preview">Preview</TabsTrigger>
    <TabsTrigger value="code">Code</TabsTrigger>
  </TabsList>
  <TabsContent value="preview">…</TabsContent>
  <TabsContent value="code">…</TabsContent>
</Tabs>

Specs

# Mint — Tabs Specs
 
## Component
- name: tabs
- file: registry/mint/tabs.tsx
- installs to: components/ui/tabs.tsx
- dependencies: radix-ui
- registryDependencies: none
 
## What is it
A compound tabbed interface that reveals one content panel at a time. Use it to
separate related content into discrete views within the same screen area. Prefer
Tabs over an accordion when all sections are equally likely to be visited and
vertical stacking would create excessive scroll. Do not use when only one panel
will ever exist.
 
## Visual identity
Pill-shaped list container on a muted background with a floating active trigger
rendered in card-surface white. Rounded corners (rounded-lg list, rounded-md
triggers) give a soft, contained feel. Compact height (h-9 list, h-[calc(100%-1px)]
trigger) keeps the chrome minimal.
 
## Design tokens
--muted: oklch(0.96 0.02 155) / oklch(0.2 0.03 155)             — TabsList background
--muted-foreground: oklch(0.52 0.06 155) / oklch(0.6 0.06 155)  — inactive trigger text
--background: oklch(1 0 0) / oklch(0.145 0 0)            — active trigger fill (light)
--foreground: oklch(0.145 0 0) / oklch(0.985 0 0)        — active trigger text (light)
--input: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%)           — active trigger fill (dark: bg-input/30) and border
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155)              — focus ring and outline
 
## Sub-components
- Tabs          → Radix Tabs.Root; wraps the full compound; flex flex-col gap-2
- TabsList      → Radix Tabs.List; pill container; bg-muted inline-flex h-9 rounded-lg p-[3px]
- TabsTrigger   → Radix Tabs.Trigger; individual tab button; integrates sound + haptic via useFeedback
- TabsContent   → Radix Tabs.Content; panel shown when its trigger is active; flex-1 outline-none
 
## States
- data-[state=active]:  trigger gets bg-background shadow-sm; dark: bg-input/30 border-input text-foreground
- data-[state=inactive]: trigger text uses text-foreground (light) / text-muted-foreground (dark), no fill
- focus-visible:        3px ring in --ring/50, 1px outline-ring, border-ring on trigger
- disabled:             pointer-events-none opacity-50 on trigger
 
## Conventions
- sounds / haptic: GIN Design exclusive — plays audio or triggers haptic feedback on interaction (TabsTrigger `sound` and `haptic` props via useFeedback hook); recommended sound: "tabSwitch"
- Sub-components expose data-slot="tabs", data-slot="tabs-list", data-slot="tabs-trigger", data-slot="tabs-content" for CSS targeting
- Full-width layout: add className="w-full" to TabsList and className="flex-1" to each TabsTrigger
- Vertical orientation: set orientation="vertical" on Tabs, add flex-col to TabsList, wrap Tabs in flex-row
- Every TabsTrigger value must have a matching TabsContent value
- SVG icons inside TabsTrigger auto-size to 16px; pointer-events are disabled on them
- Controlled usage: pass value + onValueChange to Tabs; uncontrolled: use defaultValue only

Installation

GIN Design Plugin
$ npx shadcn add https://gin-design.vercel.app/tabs