Tabs
A component for organizing content into multiple tabs
Default Variant
The standard tabs style with background and rounded corners.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4
5 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
6</script>
7
8<Tabs value="account">
9 <TabsList>
10 <TabsTrigger value="account">Account</TabsTrigger>
11 <TabsTrigger value="password">Password</TabsTrigger>
12 <TabsTrigger value="settings">Settings</TabsTrigger>
13 </TabsList>
14 <TabsContent value="account">
15 <h3 class="text-lg font-medium">Account Information</h3>
16 <p class="text-sm text-muted-foreground">View and manage your account details here.</p>
17 </TabsContent>
18 <TabsContent value="password">
19 <h3 class="text-lg font-medium">Password Settings</h3>
20 <p class="text-sm text-muted-foreground">Change your password and security preferences.</p>
21 </TabsContent>
22 <TabsContent value="settings">
23 <h3 class="text-lg font-medium">Application Settings</h3>
24 <p class="text-sm text-muted-foreground">Configure your application preferences and options.</p>
25 </TabsContent>
26</Tabs>Underline Variant
Clean underline style with active indicator at the bottom.
1
2<Tabs value="overview">
3 <TabsList variant="underline">
4 <TabsTrigger variant="underline" value="overview">Overview</TabsTrigger>
5 <TabsTrigger variant="underline" value="analytics">Analytics</TabsTrigger>
6 <TabsTrigger variant="underline" value="reports">Reports</TabsTrigger>
7 <TabsTrigger variant="underline" value="notifications">Notifications</TabsTrigger>
8 </TabsList>
9 <TabsContent value="overview">
10 <h3 class="text-lg font-medium">Overview Dashboard</h3>
11 <p class="text-sm text-muted-foreground">Your main dashboard overview with key metrics.</p>
12 </TabsContent>
13</Tabs>Pills Variant
Rounded pill-style tabs with smooth transitions.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4 import { Home, User, Settings } from "@kareyes/aether/icons";
5
6 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
7</script>
8
9<Tabs value="home">
10 <TabsList variant="pills">
11 <TabsTrigger variant="pills" value="home">
12 <Home class="size-4" />
13 Home
14 </TabsTrigger>
15 <TabsTrigger variant="pills" value="profile">
16 <User class="size-4" />
17 Profile
18 </TabsTrigger>
19 <TabsTrigger variant="pills" value="settings">
20 <Settings class="size-4" />
21 Settings
22 </TabsTrigger>
23 </TabsList>
24 <TabsContent value="home">
25 <h3 class="text-lg font-medium">Home Page</h3>
26 <p class="text-sm text-muted-foreground">Welcome to your home page.</p>
27 </TabsContent>
28</Tabs>Solid Variant
Bold solid background style with primary color for active state.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4 import { Mail, Calendar, Bell } from "@kareyes/aether/icons";
5
6 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
7</script>
8
9<Tabs value="inbox">
10 <TabsList variant="solid">
11 <TabsTrigger variant="solid" value="inbox">
12 <Mail class="size-4" />
13 Inbox
14 </TabsTrigger>
15 <TabsTrigger variant="solid" value="scheduled">
16 <Calendar class="size-4" />
17 Scheduled
18 </TabsTrigger>
19 <TabsTrigger variant="solid" value="notifications">
20 <Bell class="size-4" />
21 Alerts
22 </TabsTrigger>
23 </TabsList>
24 <TabsContent value="inbox">
25 <h3 class="text-lg font-medium">Inbox Messages</h3>
26 <p class="text-sm text-muted-foreground">View all your incoming messages.</p>
27 </TabsContent>
28</Tabs>Segmented Variant
Segmented control style with continuous background.
1
2<Tabs value="daily">
3 <TabsList variant="segmented">
4 <TabsTrigger variant="segmented" value="daily">Daily</TabsTrigger>
5 <TabsTrigger variant="segmented" value="weekly">Weekly</TabsTrigger>
6 <TabsTrigger variant="segmented" value="monthly">Monthly</TabsTrigger>
7 <TabsTrigger variant="segmented" value="yearly">Yearly</TabsTrigger>
8 </TabsList>
9 <TabsContent value="daily">
10 <h3 class="text-lg font-medium">Daily Statistics</h3>
11 <p class="text-sm text-muted-foreground">View your daily performance metrics.</p>
12 </TabsContent>
13</Tabs>Size Variants
Different sizes for various use cases.
1
2<!-- Small Size -->
3<Tabs value="tab1">
4 <TabsList size="sm">
5 <TabsTrigger size="sm" value="tab1">Small Tab 1</TabsTrigger>
6 <TabsTrigger size="sm" value="tab2">Small Tab 2</TabsTrigger>
7 <TabsTrigger size="sm" value="tab3">Small Tab 3</TabsTrigger>
8 </TabsList>
9 <TabsContent value="tab1" padding="sm">
10 <p class="text-sm text-muted-foreground">Small size content area</p>
11 </TabsContent>
12</Tabs>
13
14<!-- Large Size -->
15<Tabs value="tab1">
16 <TabsList size="lg">
17 <TabsTrigger size="lg" value="tab1">Large Tab 1</TabsTrigger>
18 <TabsTrigger size="lg" value="tab2">Large Tab 2</TabsTrigger>
19 <TabsTrigger size="lg" value="tab3">Large Tab 3</TabsTrigger>
20 </TabsList>
21 <TabsContent value="tab1" padding="lg">
22 <p class="text-sm text-muted-foreground">Large size for better touch targets</p>
23 </TabsContent>
24</Tabs>Animation Variants
Different transition effects for content changes.
1
2<!-- Slide Animation -->
3<Tabs value="slide1">
4 <TabsList variant="pills">
5 <TabsTrigger variant="pills" value="slide1">Slide 1</TabsTrigger>
6 <TabsTrigger variant="pills" value="slide2">Slide 2</TabsTrigger>
7 <TabsTrigger variant="pills" value="slide3">Slide 3</TabsTrigger>
8 </TabsList>
9 <TabsContent value="slide1" animation="slide">
10 <div class="rounded-lg border border-border p-4">
11 <p class="text-sm">Content slides in from bottom</p>
12 </div>
13 </TabsContent>
14</Tabs>
15
16<!-- Scale Animation -->
17<Tabs value="scale1">
18 <TabsList variant="solid">
19 <TabsTrigger variant="solid" value="scale1">Scale 1</TabsTrigger>
20 <TabsTrigger variant="solid" value="scale2">Scale 2</TabsTrigger>
21 <TabsTrigger variant="solid" value="scale3">Scale 3</TabsTrigger>
22 </TabsList>
23 <TabsContent value="scale1" animation="scale">
24 <div class="rounded-lg border border-border p-4">
25 <p class="text-sm">Content zooms in on appear</p>
26 </div>
27 </TabsContent>
28</Tabs>Responsive: Overflow Scroll
Many tabs in a single row. On mobile (≤640px), the tab list scrolls horizontally with a hidden scrollbar.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4 import { Home, BarChart3, Settings, CreditCard, Users, Puzzle, Shield } from "@kareyes/aether/icons";
5
6 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
7</script>
8
9<Tabs value="dash">
10 <TabsList variant="underline">
11 <TabsTrigger variant="underline" value="dash">
12 <Home class="size-4" />
13 Dashboard
14 </TabsTrigger>
15 <TabsTrigger variant="underline" value="analytics">
16 <BarChart3 class="size-4" />
17 Analytics
18 </TabsTrigger>
19 <TabsTrigger variant="underline" value="reports">Reports</TabsTrigger>
20 <TabsTrigger variant="underline" value="settings">
21 <Settings class="size-4" />
22 Settings
23 </TabsTrigger>
24 <TabsTrigger variant="underline" value="billing">
25 <CreditCard class="size-4" />
26 Billing
27 </TabsTrigger>
28 <TabsTrigger variant="underline" value="team">
29 <Users class="size-4" />
30 Team
31 </TabsTrigger>
32 <TabsTrigger variant="underline" value="integrations">
33 <Puzzle class="size-4" />
34 Integrations
35 </TabsTrigger>
36 <TabsTrigger variant="underline" value="security">
37 <Shield class="size-4" />
38 Security
39 </TabsTrigger>
40 </TabsList>
41 <TabsContent value="dash">
42 <h4 class="font-medium">Dashboard</h4>
43 <p class="text-sm text-muted-foreground mt-1">
44 Resize the browser to see horizontal scroll on mobile.
45 </p>
46 </TabsContent>
47</Tabs>Responsive: Vertical Tabs
Vertical tabs automatically convert to horizontal layout on mobile (≤640px). Resize the browser to see the switch.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4 import { Home, User, Settings, Shield } from "@kareyes/aether/icons";
5
6 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
7</script>
8
9<!-- Vertical Default -->
10<Tabs orientation="vertical" value="general">
11 <TabsList>
12 <TabsTrigger value="general">General</TabsTrigger>
13 <TabsTrigger value="appearance">Appearance</TabsTrigger>
14 <TabsTrigger value="notifications">Notifications</TabsTrigger>
15 </TabsList>
16 <TabsContent value="general">
17 <h4 class="font-medium">General Settings</h4>
18 <p class="text-sm text-muted-foreground mt-1">
19 On desktop, tabs are vertical. On mobile ≤640px, they switch to horizontal.
20 </p>
21 </TabsContent>
22</Tabs>
23
24<!-- Vertical Pills -->
25<Tabs orientation="vertical" value="v1">
26 <TabsList variant="pills">
27 <TabsTrigger variant="pills" value="v1">
28 <Home class="size-4" />
29 Dashboard
30 </TabsTrigger>
31 <TabsTrigger variant="pills" value="v2">
32 <User class="size-4" />
33 Profile
34 </TabsTrigger>
35 <TabsTrigger variant="pills" value="v3">
36 <Settings class="size-4" />
37 Settings
38 </TabsTrigger>
39 <TabsTrigger variant="pills" value="v4">
40 <Shield class="size-4" />
41 Privacy
42 </TabsTrigger>
43 </TabsList>
44 <TabsContent value="v1">
45 <h4 class="font-medium">Dashboard</h4>
46 <p class="text-sm text-muted-foreground mt-1">Vertical pills variant with icons.</p>
47 </TabsContent>
48</Tabs>Dropdown on Mobile
With dropdownOnMobile and items props, tabs convert to a Select dropdown on mobile (≤640px).
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4
5 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
6
7 const settingsTabs: TabsPrimitives.TabItem[] = [
8 { value: "general", label: "General" },
9 { value: "appearance", label: "Appearance" },
10 { value: "notifications", label: "Notifications" },
11 { value: "privacy", label: "Privacy" },
12 { value: "security", label: "Security" },
13 ];
14</script>
15
16<Tabs value="general" dropdownOnMobile items={settingsTabs}>
17 <TabsList variant="underline">
18 <TabsTrigger variant="underline" value="general">General</TabsTrigger>
19 <TabsTrigger variant="underline" value="appearance">Appearance</TabsTrigger>
20 <TabsTrigger variant="underline" value="notifications">Notifications</TabsTrigger>
21 <TabsTrigger variant="underline" value="privacy">Privacy</TabsTrigger>
22 <TabsTrigger variant="underline" value="security">Security</TabsTrigger>
23 </TabsList>
24 <TabsContent value="general">
25 <h4 class="font-medium">General Settings</h4>
26 <p class="text-sm text-muted-foreground mt-1">
27 On mobile, the tab list becomes a Select dropdown.
28 </p>
29 </TabsContent>
30</Tabs>Disabled Tabs
Individual tabs can be disabled while keeping others interactive.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4
5 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
6</script>
7
8<Tabs value="enabled">
9 <TabsList>
10 <TabsTrigger value="enabled">Enabled</TabsTrigger>
11 <TabsTrigger value="disabled" disabled>Disabled</TabsTrigger>
12 <TabsTrigger value="another">Another Tab</TabsTrigger>
13 </TabsList>
14 <TabsContent value="enabled">
15 <p class="text-sm text-muted-foreground">
16 This tab is active. The "Disabled" tab cannot be selected.
17 </p>
18 </TabsContent>
19</Tabs>Combinations
Mixing variants, sizes, and animations.
1
2<script lang="ts">
3 import { TabsPrimitives } from "@kareyes/aether";
4 import { Home, User, Settings } from "@kareyes/aether/icons";
5
6 const { Tabs, TabsList, TabsTrigger, TabsContent } = TabsPrimitives;
7</script>
8
9<!-- Pills + Large + Slide -->
10<Tabs value="ch1">
11 <TabsList variant="pills" size="lg">
12 <TabsTrigger variant="pills" size="lg" value="ch1">
13 <Home class="size-4" />
14 Home
15 </TabsTrigger>
16 <TabsTrigger variant="pills" size="lg" value="ch2">
17 <User class="size-4" />
18 Profile
19 </TabsTrigger>
20 <TabsTrigger variant="pills" size="lg" value="ch3">
21 <Settings class="size-4" />
22 Settings
23 </TabsTrigger>
24 </TabsList>
25 <TabsContent value="ch1" animation="slide" padding="lg">
26 <p class="text-sm text-muted-foreground">Large pills with slide animation</p>
27 </TabsContent>
28</Tabs>
29
30<!-- Underline + Small + No Animation -->
31<Tabs value="cm1">
32 <TabsList variant="underline" size="sm">
33 <TabsTrigger variant="underline" size="sm" value="cm1">Compact 1</TabsTrigger>
34 <TabsTrigger variant="underline" size="sm" value="cm2">Compact 2</TabsTrigger>
35 <TabsTrigger variant="underline" size="sm" value="cm3">Compact 3</TabsTrigger>
36 </TabsList>
37 <TabsContent value="cm1" animation="none" padding="sm">
38 <p class="text-sm text-muted-foreground">Small underline with instant switch</p>
39 </TabsContent>
40</Tabs>
41
42<!-- Solid + Scale Animation -->
43<Tabs value="cs1">
44 <TabsList variant="solid">
45 <TabsTrigger variant="solid" value="cs1">Overview</TabsTrigger>
46 <TabsTrigger variant="solid" value="cs2">Details</TabsTrigger>
47 <TabsTrigger variant="solid" value="cs3">History</TabsTrigger>
48 </TabsList>
49 <TabsContent value="cs1" animation="scale">
50 <p class="text-sm text-muted-foreground">Solid variant with zoom-in effect</p>
51 </TabsContent>
52</Tabs>Features
- 🎨 5 Visual Variants: Default, Underline, Pills, Solid, Segmented
- 📏 3 Size Options: Small, Default, Large
- ✨ 4 Animation Styles: None, Fade, Slide, Scale
- ♿ Fully Accessible: Built on bits-ui primitives
- 🎯 Type-Safe: Full TypeScript support
- 🎭 Customizable: Extensive styling options with Tailwind
- 🔄 Reactive: Svelte 5 runes for optimal performance
Component Architecture
TabsList Component
- Uses
tv()function for variant management - 5 visual variants with distinct styling
- 3 size options for flexible layouts
- Exported types:
TabsListVariant,TabsListSize
TabsTrigger Component
- Matching variants with TabsList for consistency
- Each variant has unique active states:
- Default: White background on active
- Underline: Bottom border indicator on active
- Pills: Rounded pill with shadow on active
- Solid: Primary background and text on active
- Segmented: White background with shadow on active
- Size variants adjust padding and text size
- Exported types:
TabsTriggerVariant,TabsTriggerSize
TabsContent Component
- Animation and padding control via variants
- 4 animation options: none, fade, slide, scale
- 4 padding options: none, sm, default, lg
- Exported types:
TabsContentAnimation,TabsContentPadding
Usage
Basic Example
<script lang="ts">
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@kareyes/aether";
</script>
<Tabs value="account">
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="account">
<p>Manage your account settings here.</p>
</TabsContent>
<TabsContent value="password">
<p>Change your password here.</p>
</TabsContent>
<TabsContent value="settings">
<p>Configure your preferences here.</p>
</TabsContent>
</Tabs>
Variants
Default Variant
The standard tabs style with background and rounded corners.
<Tabs value="account">
<TabsList variant="default">
<TabsTrigger variant="default" value="account">Account</TabsTrigger>
<TabsTrigger variant="default" value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">Account content</TabsContent>
<TabsContent value="password">Password content</TabsContent>
</Tabs>
Underline Variant
Clean underline style with active indicator at the bottom.
<Tabs value="overview">
<TabsList variant="underline">
<TabsTrigger variant="underline" value="overview">Overview</TabsTrigger>
<TabsTrigger variant="underline" value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="overview">Overview content</TabsContent>
<TabsContent value="analytics">Analytics content</TabsContent>
</Tabs>
Pills Variant
Rounded pill-style tabs with smooth transitions.
<Tabs value="home">
<TabsList variant="pills">
<TabsTrigger variant="pills" value="home">Home</TabsTrigger>
<TabsTrigger variant="pills" value="profile">Profile</TabsTrigger>
</TabsList>
<TabsContent value="home">Home content</TabsContent>
<TabsContent value="profile">Profile content</TabsContent>
</Tabs>
Solid Variant
Bold solid background style with primary color for active state.
<Tabs value="inbox">
<TabsList variant="solid">
<TabsTrigger variant="solid" value="inbox">Inbox</TabsTrigger>
<TabsTrigger variant="solid" value="sent">Sent</TabsTrigger>
</TabsList>
<TabsContent value="inbox">Inbox content</TabsContent>
<TabsContent value="sent">Sent content</TabsContent>
</Tabs>
Segmented Variant
Segmented control style with continuous background.
<Tabs value="daily">
<TabsList variant="segmented">
<TabsTrigger variant="segmented" value="daily">Daily</TabsTrigger>
<TabsTrigger variant="segmented" value="weekly">Weekly</TabsTrigger>
</TabsList>
<TabsContent value="daily">Daily content</TabsContent>
<TabsContent value="weekly">Weekly content</TabsContent>
</Tabs>
Sizes
Small
Compact size for dense UIs.
<TabsList size="sm">
<TabsTrigger size="sm" value="tab1">Tab 1</TabsTrigger>
<TabsTrigger size="sm" value="tab2">Tab 2</TabsTrigger>
</TabsList>
Default
Standard size for most use cases.
<TabsList size="default">
<TabsTrigger size="default" value="tab1">Tab 1</TabsTrigger>
<TabsTrigger size="default" value="tab2">Tab 2</TabsTrigger>
</TabsList>
Large
Larger size for better touch targets and prominence.
<TabsList size="lg">
<TabsTrigger size="lg" value="tab1">Tab 1</TabsTrigger>
<TabsTrigger size="lg" value="tab2">Tab 2</TabsTrigger>
</TabsList>
Animations
Fade (Default)
Smooth fade-in transition.
<TabsContent value="tab1" animation="fade">
Content with fade animation
</TabsContent>
Slide
Content slides in from bottom.
<TabsContent value="tab1" animation="slide">
Content with slide animation
</TabsContent>
Scale
Content zooms in on appear.
<TabsContent value="tab1" animation="scale">
Content with scale animation
</TabsContent>
None
No animation.
<TabsContent value="tab1" animation="none">
Content without animation
</TabsContent>
With Icons
Enhance tabs with icons for better visual communication.
<script>
import { Home, User, Settings } from "@lucide/svelte";
</script>
<TabsList variant="pills">
<TabsTrigger variant="pills" value="home">
<Home class="size-4" />
Home
</TabsTrigger>
<TabsTrigger variant="pills" value="profile">
<User class="size-4" />
Profile
</TabsTrigger>
<TabsTrigger variant="pills" value="settings">
<Settings class="size-4" />
Settings
</TabsTrigger>
</TabsList>
Content Padding
Control the spacing above the content area.
<!-- No padding -->
<TabsContent value="tab1" padding="none">
Content without top padding
</TabsContent>
<!-- Small padding -->
<TabsContent value="tab1" padding="sm">
Content with small padding
</TabsContent>
<!-- Default padding -->
<TabsContent value="tab1" padding="default">
Content with default padding
</TabsContent>
<!-- Large padding -->
<TabsContent value="tab1" padding="lg">
Content with large padding
</TabsContent>
Responsive Behavior
The Tabs component is fully responsive out of the box. No additional props are needed — responsive styles are applied automatically via Tailwind CSS breakpoints.
Mobile (≤640px)
- Horizontal scroll: The tab list scrolls horizontally with hidden scrollbar for a clean look
- Touch-friendly: Triggers have a minimum height of 44px (WCAG touch target) and increased padding
- No wrapping: Tabs stay in a single row with
flex-nowrapandshrink-0on triggers - Vertical → Horizontal: Vertical tabs automatically convert to horizontal orientation on mobile
Tablet (≤768px)
- Full width: The tab list stretches to fill available width
- Font capping: Text size is capped at
text-smfor consistent readability
Desktop (>768px)
- Unchanged: Standard
w-fitbehavior with all existing variant/size styling
Vertical Tabs on Mobile
When using orientation="vertical", the root component detects mobile screens (≤640px) and automatically switches to horizontal orientation:
<!-- Renders vertically on desktop, horizontally on mobile -->
<Tabs orientation="vertical" value="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
</Tabs>
Many Tabs (Overflow)
When there are too many tabs to fit on screen, the list scrolls horizontally on mobile. The scrollbar is hidden for a cleaner appearance, but scroll functionality is preserved via touch/drag:
<TabsList variant="underline">
<TabsTrigger variant="underline" value="t1">Dashboard</TabsTrigger>
<TabsTrigger variant="underline" value="t2">Analytics</TabsTrigger>
<TabsTrigger variant="underline" value="t3">Reports</TabsTrigger>
<TabsTrigger variant="underline" value="t4">Settings</TabsTrigger>
<TabsTrigger variant="underline" value="t5">Billing</TabsTrigger>
<TabsTrigger variant="underline" value="t6">Team</TabsTrigger>
</TabsList>
Dropdown Conversion on Mobile
For screens where even scrolling tabs feel cluttered, you can convert tabs to a Select dropdown on mobile. Pass dropdownOnMobile and items to the Tabs root:
<script lang="ts">
import type { TabItem } from "@kareyes/aether";
const tabs: TabItem[] = [
{ value: "general", label: "General" },
{ value: "appearance", label: "Appearance" },
{ value: "notifications", label: "Notifications" },
];
</script>
<Tabs value="general" dropdownOnMobile items={tabs}>
<TabsList>
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="appearance">Appearance</TabsTrigger>
<TabsTrigger value="notifications">Notifications</TabsTrigger>
</TabsList>
<TabsContent value="general">General content</TabsContent>
<TabsContent value="appearance">Appearance content</TabsContent>
<TabsContent value="notifications">Notifications content</TabsContent>
</Tabs>
On desktop, tabs render normally. On mobile (≤640px), the TabsList is hidden and replaced by a Select dropdown. The items array provides the options for the dropdown. The TabsContent panels still work as usual since the value binding is shared.
API Reference
Tabs (Root)
The root tabs container.
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
- | Currently active tab value (bindable) |
orientation |
"horizontal" | "vertical" |
"horizontal" |
Tab orientation (vertical auto-converts to horizontal on mobile ≤640px) |
items |
TabItem[] |
[] |
Tab items for dropdown mode (requires dropdownOnMobile) |
dropdownOnMobile |
boolean |
false |
Convert tabs to a Select dropdown on mobile (≤640px) |
onValueChange |
(value: string) => void |
- | Callback when active tab changes |
class |
string |
- | Additional CSS classes |
TabItem
Type definition for dropdown items:
interface TabItem {
value: string;
label: string;
disabled?: boolean;
}
TabsList
Container for tab triggers.
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
"default" | "underline" | "pills" | "solid" | "segmented" |
"default" |
Visual style variant |
size |
"sm" | "default" | "lg" |
"default" |
Size of the tabs list |
class |
string |
- | Additional CSS classes |
TabsTrigger
Individual tab trigger button.
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
- | Unique value for this tab |
variant |
"default" | "underline" | "pills" | "solid" | "segmented" |
"default" |
Visual style variant (should match TabsList) |
size |
"sm" | "default" | "lg" |
"default" |
Size of the trigger (should match TabsList) |
disabled |
boolean |
false |
Whether the tab is disabled |
class |
string |
- | Additional CSS classes |
TabsContent
Content panel for each tab.
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
- | Value matching the associated trigger |
animation |
"none" | "fade" | "slide" | "scale" |
"fade" |
Animation effect when content appears |
padding |
"none" | "sm" | "default" | "lg" |
"default" |
Top padding of content area |
class |
string |
- | Additional CSS classes |
Advanced Examples
Controlled Tabs
<script lang="ts">
let activeTab = $state("account");
function handleTabChange(value: string) {
console.log("Tab changed to:", value);
activeTab = value;
}
</script>
<Tabs value={activeTab} onValueChange={handleTabChange}>
<TabsList>
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">Account content</TabsContent>
<TabsContent value="password">Password content</TabsContent>
</Tabs>
<p>Current tab: {activeTab}</p>
Disabled Tabs
<TabsList>
<TabsTrigger value="enabled">Enabled</TabsTrigger>
<TabsTrigger value="disabled" disabled>Disabled</TabsTrigger>
</TabsList>
Custom Styling
<TabsList class="bg-blue-100 dark:bg-blue-900">
<TabsTrigger
value="custom"
class="data-[state=active]:bg-blue-500 data-[state=active]:text-white"
>
Custom Styled
</TabsTrigger>
</TabsList>
Mixed Variants
<!-- Pills list with large size -->
<TabsList variant="pills" size="lg">
<TabsTrigger variant="pills" size="lg" value="tab1">Tab 1</TabsTrigger>
<TabsTrigger variant="pills" size="lg" value="tab2">Tab 2</TabsTrigger>
</TabsList>
<!-- Underline list with small size -->
<TabsList variant="underline" size="sm">
<TabsTrigger variant="underline" size="sm" value="tab1">Tab 1</TabsTrigger>
<TabsTrigger variant="underline" size="sm" value="tab2">Tab 2</TabsTrigger>
</TabsList>
Complex Content
<Tabs value="dashboard">
<TabsList variant="underline">
<TabsTrigger variant="underline" value="dashboard">Dashboard</TabsTrigger>
<TabsTrigger variant="underline" value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="dashboard" animation="slide" padding="lg">
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader>
<CardTitle>Total Revenue</CardTitle>
<CardDescription>January 2024</CardDescription>
</CardHeader>
<CardContent>
<div class="text-2xl font-bold">$45,231.89</div>
</CardContent>
</Card>
<!-- More cards... -->
</div>
</TabsContent>
<TabsContent value="analytics" animation="scale">
<!-- Analytics charts and data -->
</TabsContent>
</Tabs>
Best Practices
Variant Consistency
Always use matching variants for TabsList and TabsTrigger:
<!-- ✅ Good -->
<TabsList variant="pills">
<TabsTrigger variant="pills" value="tab1">Tab 1</TabsTrigger>
</TabsList>
<!-- ❌ Bad -->
<TabsList variant="pills">
<TabsTrigger variant="underline" value="tab1">Tab 1</TabsTrigger>
</TabsList>
Size Consistency
Keep sizes consistent across list and triggers:
<!-- ✅ Good -->
<TabsList size="lg">
<TabsTrigger size="lg" value="tab1">Tab 1</TabsTrigger>
</TabsList>
<!-- ❌ Bad -->
<TabsList size="lg">
<TabsTrigger size="sm" value="tab1">Tab 1</TabsTrigger>
</TabsList>
Accessible Labels
Use clear, descriptive labels for tabs:
<!-- ✅ Good -->
<TabsTrigger value="account">Account Settings</TabsTrigger>
<!-- ❌ Bad -->
<TabsTrigger value="1">Tab 1</TabsTrigger>
Performance
For content-heavy tabs, consider lazy loading:
<script lang="ts">
let activeTab = $state("overview");
</script>
<TabsContent value="analytics">
{#if activeTab === "analytics"}
<ExpensiveAnalyticsComponent />
{/if}
</TabsContent>
Styling
CSS Variables
The component uses the following CSS custom properties:
--background- Default background color--foreground- Default text color--muted- Muted background color--muted-foreground- Muted text color--primary- Primary accent color--primary-foreground- Primary text color--border- Border color--ring- Focus ring color
Tailwind Classes
You can customize any component with Tailwind classes:
<TabsList class="bg-gradient-to-r from-blue-500 to-purple-500">
<TabsTrigger class="text-white hover:text-gray-200">Tab</TabsTrigger>
</TabsList>
Accessibility
The Tabs component is built on bits-ui primitives which provide:
- Keyboard Navigation: Arrow keys to navigate between tabs, Space/Enter to activate
- Focus Management: Proper focus handling and visible focus indicators
- ARIA Attributes: Correct ARIA roles, states, and properties
- Screen Reader Support: Announces tab state and content properly
Keyboard Shortcuts
Tab- Move focus into/out of the tab listArrow Left/Right- Navigate between tabs (horizontal orientation)Arrow Up/Down- Navigate between tabs (vertical orientation)Space/Enter- Activate focused tabHome- Move to first tabEnd- Move to last tab
Type Exports
All variant types are properly exported for TypeScript users:
TabItemTabsListVariantTabsListSizeTabsTriggerVariantTabsTriggerSizeTabsContentAnimationTabsContentPadding