Usage

<div class="zui-tabs" data-orientation="horizontal">
<div class="zui-tabs-list" role="tablist" aria-label="Profile sections">
  <button class="zui-tabs-trigger" type="button" role="tab" aria-selected="true">Account</button>
  <button class="zui-tabs-trigger" type="button" role="tab" aria-selected="false">Security</button>
  <button class="zui-tabs-trigger" type="button" role="tab" aria-selected="false">Billing</button>
</div>

<div class="zui-tabs-content" role="tabpanel">Account settings</div>
</div>

Underline variant

Use variant="underline" on both TabsList and TabsTrigger when you want a simpler tab bar without the bordered container.

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

<Tabs defaultValue="overview">
<TabsList variant="underline" aria-label="Project sections">
  <TabsTrigger variant="underline" value="overview">Overview</TabsTrigger>
  <TabsTrigger variant="underline" value="activity">Activity</TabsTrigger>
  <TabsTrigger variant="underline" value="settings">Settings</TabsTrigger>
</TabsList>

<TabsContent value="overview">Overview panel</TabsContent>
<TabsContent value="activity">Recent activity</TabsContent>
<TabsContent value="settings">Project settings</TabsContent>
</Tabs>

Vertical tabs

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

<Tabs defaultValue="profile" orientation="vertical">
<TabsList aria-label="Settings sections">
  <TabsTrigger value="profile">Profile</TabsTrigger>
  <TabsTrigger value="notifications">Notifications</TabsTrigger>
  <TabsTrigger value="team">Team</TabsTrigger>
</TabsList>

<TabsContent value="profile">Profile preferences</TabsContent>
<TabsContent value="notifications">Notification preferences</TabsContent>
<TabsContent value="team">Team settings</TabsContent>
</Tabs>

Vertical underline tabs

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

<Tabs defaultValue="profile" orientation="vertical">
<TabsList variant="underline" aria-label="Settings sections">
  <TabsTrigger variant="underline" value="profile">Profile</TabsTrigger>
  <TabsTrigger variant="underline" value="notifications">Notifications</TabsTrigger>
  <TabsTrigger variant="underline" value="team">Team</TabsTrigger>
</TabsList>

<TabsContent value="profile">Profile preferences</TabsContent>
<TabsContent value="notifications">Notification preferences</TabsContent>
<TabsContent value="team">Team settings</TabsContent>
</Tabs>

Manual activation

Use activationMode="manual" when arrow keys should move focus without switching panels until Enter or Space.

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

<Tabs defaultValue="overview" activationMode="manual">
<TabsList aria-label="Project sections">
  <TabsTrigger value="overview">Overview</TabsTrigger>
  <TabsTrigger value="activity">Activity</TabsTrigger>
  <TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>

<TabsContent value="overview">Overview panel</TabsContent>
<TabsContent value="activity">Recent activity</TabsContent>
<TabsContent value="settings">Project settings</TabsContent>
</Tabs>

Controlled tabs

Use value with onValueChange when tab state should be controlled by application state.

import { useState } from 'react'
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

export function Example() {
const [tab, setTab] = useState('activity')

return (
  <Tabs value={tab} onValueChange={setTab}>
    <TabsList aria-label="Team sections">
      <TabsTrigger value="overview">Overview</TabsTrigger>
      <TabsTrigger value="activity">Activity</TabsTrigger>
      <TabsTrigger value="members">Members</TabsTrigger>
    </TabsList>

    <TabsContent value="overview">Team overview</TabsContent>
    <TabsContent value="activity">Recent activity</TabsContent>
    <TabsContent value="members">Member list</TabsContent>
  </Tabs>
)
}

Disabled tabs

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@mrmartineau/zui/react'

<Tabs defaultValue="design">
<TabsList aria-label="Release workflow">
  <TabsTrigger value="design">Design</TabsTrigger>
  <TabsTrigger value="build">Build</TabsTrigger>
  <TabsTrigger value="ship" disabled>Ship</TabsTrigger>
</TabsList>

<TabsContent value="design">Design review is complete.</TabsContent>
<TabsContent value="build">Build is in progress.</TabsContent>
<TabsContent value="ship">Shipping is locked until QA passes.</TabsContent>
</Tabs>

CSS custom properties

PropertyDefaultDescription
--zui-tabs-gapvar(--space-sm)Gap between the tab list and panel area
--zui-tabs-list-gapvar(--space-4xs)Gap between triggers
--zui-tabs-list-paddingvar(--space-4xs)Inner padding of the tab list container
--zui-tabs-list-radiusvar(--radius-lg)Border radius of the tab list
--zui-tabs-list-bordervar(--color-border)Border colour of the tab list
--zui-tabs-list-bgcolor-mix(in oklch, var(--color-text) 4%, var(--color-surface))Background of the tab list
--zui-tabs-trigger-radiuscalc(var(--zui-tabs-list-radius) - var(--space-5xs))Border radius of each trigger
--zui-tabs-trigger-padding-inlinevar(--space-sm)Horizontal padding inside each trigger
--zui-tabs-trigger-padding-blockvar(--space-3xs)Vertical padding inside each trigger
--zui-tabs-trigger-colorcolor-mix(in oklch, var(--color-text) 72%, transparent)Default trigger text colour
--zui-tabs-trigger-active-bgvar(--color-surface)Background of the active trigger
--zui-tabs-trigger-active-colorvar(--color-text)Text colour of the active trigger
--zui-tabs-trigger-active-shadowvar(--shadow-sm)Shadow applied to the active trigger
--zui-tabs-trigger-focus-ringoklch(from var(--color-theme) l c h / 0.35)Focus ring colour
--zui-tabs-content-paddingvar(--space-xs) 0Padding for the tab panel content

Notes

Theme

Copy this CSS to your project: