Menu

Site and documentation links

Search documentation

Type to filter pages and sections. Use the arrow keys to move and Enter to open.

    Tabs

    Layered sections of content where one panel shows at a time.

    Three deploys this week, all successful.
    View code
    import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@syncclouds/ui'
    
    export function TabsDemo() {
      return (
        <Tabs defaultValue="overview" className="w-full max-w-md">
          <TabsList aria-label="Project">
            <TabsTrigger value="overview">Overview</TabsTrigger>
            <TabsTrigger value="activity">Activity</TabsTrigger>
            <TabsTrigger value="settings">Settings</TabsTrigger>
          </TabsList>
          <TabsPanel value="overview">Three deploys this week, all successful.</TabsPanel>
          <TabsPanel value="activity">The last change was merged two hours ago.</TabsPanel>
          <TabsPanel value="settings">Region, alerts and the people who can deploy.</TabsPanel>
        </Tabs>
      )
    }

    Installation

    npm install @syncclouds/ui

    Usage

    import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@syncclouds/ui'
    <Tabs defaultValue="overview">
      <TabsList aria-label="Project">
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="activity">Activity</TabsTrigger>
      </TabsList>
      <TabsPanel value="overview">Overview content</TabsPanel>
      <TabsPanel value="activity">Activity content</TabsPanel>
    </Tabs>

    Anatomy

    Tabs
    ├── TabsList
    │   └── TabsTrigger
    └── TabsPanel

    Vertical

    With orientation="vertical", Up and Down move between tabs.

    Your name, photo and the email we use to reach you.
    View code
    import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@syncclouds/ui'
    
    const SECTIONS = [
      { value: 'profile', label: 'Profile', body: 'Your name, photo and the email we use to reach you.' },
      { value: 'security', label: 'Security', body: 'Password, two-step sign-in and active sessions.' },
      { value: 'notifications', label: 'Notifications', body: 'Which alerts you get, and where.' }
    ]
    
    /* Vertical tabs move with Up and Down instead of Left and Right. */
    export function TabsVertical() {
      return (
        <Tabs defaultValue="profile" orientation="vertical" className="flex w-full max-w-lg gap-6">
          <TabsList aria-label="Account settings" className="shrink-0">
            {SECTIONS.map(section => (
              <TabsTrigger key={section.value} value={section.value}>
                {section.label}
              </TabsTrigger>
            ))}
          </TabsList>
          {SECTIONS.map(section => (
            <TabsPanel key={section.value} value={section.value} className="pt-2">
              {section.body}
            </TabsPanel>
          ))}
        </Tabs>
      )
    }

    Manual activation

    Arrow keys move focus and Enter or Space selects. Use it when showing a panel is slow.

    Traffic for today.
    View code
    import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@syncclouds/ui'
    
    /* Manual activation: arrows move focus, Enter or Space selects. Useful
       when showing a panel is slow, such as loading a chart. */
    export function TabsManual() {
      return (
        <Tabs defaultValue="day" activationMode="manual" className="w-full max-w-md">
          <TabsList aria-label="Report range">
            <TabsTrigger value="day">Day</TabsTrigger>
            <TabsTrigger value="week">Week</TabsTrigger>
            <TabsTrigger value="month">Month</TabsTrigger>
          </TabsList>
          <TabsPanel value="day">Traffic for today.</TabsPanel>
          <TabsPanel value="week">Traffic for the last seven days.</TabsPanel>
          <TabsPanel value="month">Traffic for the last 30 days.</TabsPanel>
        </Tabs>
      )
    }

    Without the theme

    The code view.
    View code
    import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@syncclouds/ui'
    
    const TABS = ['Code', 'Preview', 'Tests']
    
    export function TabsUnstyled() {
      return (
        <Tabs defaultValue="Code" className="w-full max-w-md">
          <TabsList aria-label="View" className="flex gap-4 border-b border-zinc-200 dark:border-zinc-700">
            {TABS.map(tab => (
              <TabsTrigger
                key={tab}
                value={tab}
                className="-mb-px cursor-pointer border-b-2 border-transparent pb-2 text-sm font-medium text-zinc-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 data-[state=active]:border-indigo-600 data-[state=active]:text-zinc-900 dark:text-zinc-400 dark:data-[state=active]:border-indigo-400 dark:data-[state=active]:text-zinc-100"
              >
                {tab}
              </TabsTrigger>
            ))}
          </TabsList>
          {TABS.map(tab => (
            <TabsPanel key={tab} value={tab} className="pt-4 text-sm text-zinc-700 dark:text-zinc-300">
              The {tab.toLowerCase()} view.
            </TabsPanel>
          ))}
        </Tabs>
      )
    }

    Accessibility

    Follows the WAI-ARIA Tabs pattern.

    KeysWhat they do
    TabMoves focus to the selected tab, then into the panel.
    Arrow Left, Arrow RightMoves between tabs, wrapping at the ends. Up and Down when vertical.
    Home, EndMoves to the first or last tab.
    Enter, SpaceSelects the focused tab in manual mode.
    • Panels are in the tab order by default, which the APG suggests when a panel has no focusable content. Pass tabIndex={-1} to a panel that starts with a control.

    API reference

    Every part also accepts the props of the element it renders, including className and ref.

    Tabsrenders <div>

    Holds the selected tab. Give it either value and onValueChange, or defaultValue.

    PropTypeDefaultDescription
    valuestringnoneThe selected tab, when you control it.
    onValueChange(value: string) => voidnoneCalled when another tab is selected.
    defaultValuestringnoneThe tab selected at first, when the tabs control themselves.
    orientation"horizontal" | "vertical""horizontal"Sets which arrow keys move between tabs.
    activationMode"automatic" | "manual""automatic"automatic: arrow keys select as they move. manual: Enter or Space selects.
    Data attributeValues
    data-orientation"horizontal" | "vertical"

    TabsListrenders <div>

    The tablist. Name it with aria-label.

    Data attributeValues
    data-orientation"horizontal" | "vertical"

    TabsTriggerrenders <button>

    One tab.

    PropTypeDefaultDescription
    valuestringnoneRequired. Matches a TabsPanel value.
    disabledbooleanfalseSkipped by the arrow keys.
    asChildbooleanfalseUse your own element.
    Data attributeValues
    data-state"active" | "inactive"
    data-disabledpresent when disabled

    TabsPanelrenders <div>

    The content for one tab. Hidden panels stay in the DOM with the hidden attribute.

    PropTypeDefaultDescription
    valuestringnoneRequired. Matches a TabsTrigger value.
    Data attributeValues
    data-state"active" | "inactive"
    Built by Syncclouds. Syncclouds UI is in development.