Skip to content

Tabs

Switch between related panels in the same view. Selected = 2px bar plus weight (vertical: the bar at the start edge); Contained raises the selected tab in a track.

  • Free core, MIT
  • Version 2.0.0-rc.1
  • APG pattern: Tabs

Examples

These are the real React components. Use them with a mouse, touch or keyboard, and switch the theme and density for every example at once.

Default

Your name, email and bio.

Show the code

Default, code

const Example = ({ manual = false, disabled = false, variant, orientation }: { manual?: boolean; disabled?: boolean; variant?: 'line' | 'contained'; orientation?: 'horizontal' | 'vertical' }) => (
  <Tabs keyboardActivation={manual ? 'manual' : 'automatic'} disabledKeys={disabled ? ['security'] : []} variant={variant} orientation={orientation}>
    <TabList aria-label="Account settings">
      <Tab id="profile">Profile</Tab>
      <Tab id="notifications">Notifications</Tab>
      <Tab id="security">Security</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="notifications"><p>Choose which emails you get.</p></TabPanel>
    <TabPanel id="security"><p>Two-factor authentication and devices.</p></TabPanel>
  </Tabs>
);

<Example />

Manual activation

Your name, email and bio.

Show the code

Manual activation, code

const Example = ({ manual = false, disabled = false, variant, orientation }: { manual?: boolean; disabled?: boolean; variant?: 'line' | 'contained'; orientation?: 'horizontal' | 'vertical' }) => (
  <Tabs keyboardActivation={manual ? 'manual' : 'automatic'} disabledKeys={disabled ? ['security'] : []} variant={variant} orientation={orientation}>
    <TabList aria-label="Account settings">
      <Tab id="profile">Profile</Tab>
      <Tab id="notifications">Notifications</Tab>
      <Tab id="security">Security</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="notifications"><p>Choose which emails you get.</p></TabPanel>
    <TabPanel id="security"><p>Two-factor authentication and devices.</p></TabPanel>
  </Tabs>
);

<Example manual />

Disabled tab

Your name, email and bio.

Show the code

Disabled tab, code

const Example = ({ manual = false, disabled = false, variant, orientation }: { manual?: boolean; disabled?: boolean; variant?: 'line' | 'contained'; orientation?: 'horizontal' | 'vertical' }) => (
  <Tabs keyboardActivation={manual ? 'manual' : 'automatic'} disabledKeys={disabled ? ['security'] : []} variant={variant} orientation={orientation}>
    <TabList aria-label="Account settings">
      <Tab id="profile">Profile</Tab>
      <Tab id="notifications">Notifications</Tab>
      <Tab id="security">Security</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="notifications"><p>Choose which emails you get.</p></TabPanel>
    <TabPanel id="security"><p>Two-factor authentication and devices.</p></TabPanel>
  </Tabs>
);

<Example disabled />

Vertical

Your name, email and bio.

Show the code

Vertical, code

const Example = ({ manual = false, disabled = false, variant, orientation }: { manual?: boolean; disabled?: boolean; variant?: 'line' | 'contained'; orientation?: 'horizontal' | 'vertical' }) => (
  <Tabs keyboardActivation={manual ? 'manual' : 'automatic'} disabledKeys={disabled ? ['security'] : []} variant={variant} orientation={orientation}>
    <TabList aria-label="Account settings">
      <Tab id="profile">Profile</Tab>
      <Tab id="notifications">Notifications</Tab>
      <Tab id="security">Security</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="notifications"><p>Choose which emails you get.</p></TabPanel>
    <TabPanel id="security"><p>Two-factor authentication and devices.</p></TabPanel>
  </Tabs>
);

<Example orientation="vertical" />

Contained

Your name, email and bio.

Show the code

Contained, code

const Example = ({ manual = false, disabled = false, variant, orientation }: { manual?: boolean; disabled?: boolean; variant?: 'line' | 'contained'; orientation?: 'horizontal' | 'vertical' }) => (
  <Tabs keyboardActivation={manual ? 'manual' : 'automatic'} disabledKeys={disabled ? ['security'] : []} variant={variant} orientation={orientation}>
    <TabList aria-label="Account settings">
      <Tab id="profile">Profile</Tab>
      <Tab id="notifications">Notifications</Tab>
      <Tab id="security">Security</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="notifications"><p>Choose which emails you get.</p></TabPanel>
    <TabPanel id="security"><p>Two-factor authentication and devices.</p></TabPanel>
  </Tabs>
);

<Example variant="contained" />

Icons and a badge

Your name, email and bio.

Show the code

Icons and a badge, code

const IconsExample = ({ position = 'start' as 'start' | 'top' }) => (
  <Tabs>
    <TabList aria-label={position === 'top' ? 'Sections, icons above' : 'Sections'}>
      <Tab id="profile" icon={User} iconPosition={position}>Profile</Tab>
      <Tab id="inbox" icon={Bell} iconPosition={position} badge={3} badgeLabel="unread">Inbox</Tab>
      <Tab id="settings" icon={Settings} iconPosition={position}>Settings</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="inbox"><p>Three unread messages.</p></TabPanel>
    <TabPanel id="settings"><p>Preferences for this workspace.</p></TabPanel>
  </Tabs>
);

<IconsExample />

Icons above the label

Your name, email and bio.

Show the code

Icons above the label, code

const IconsExample = ({ position = 'start' as 'start' | 'top' }) => (
  <Tabs>
    <TabList aria-label={position === 'top' ? 'Sections, icons above' : 'Sections'}>
      <Tab id="profile" icon={User} iconPosition={position}>Profile</Tab>
      <Tab id="inbox" icon={Bell} iconPosition={position} badge={3} badgeLabel="unread">Inbox</Tab>
      <Tab id="settings" icon={Settings} iconPosition={position}>Settings</Tab>
    </TabList>
    <TabPanel id="profile"><p>Your name, email and bio.</p></TabPanel>
    <TabPanel id="inbox"><p>Three unread messages.</p></TabPanel>
    <TabPanel id="settings"><p>Preferences for this workspace.</p></TabPanel>
  </Tabs>
);

<IconsExample position="top" />

Tokens

The 27 design tokens Tabs reads, with the value each has in the theme and density chosen above. Change a token and the component follows, in Figma and in code.

Show all 27 tokens
Tokens Tabs reads
TokenValue nowCSS variable
color.bg.canvas…--sd-color-bg-canvas
color.bg.muted…--sd-color-bg-muted
color.bg.subtle…--sd-color-bg-subtle
color.border.control…--sd-color-border-control
color.text.disabled…--sd-color-text-disabled
elevation.1…--sd-elevation-1
motion.duration.fast…--sd-motion-duration-fast
motion.easing.standard…--sd-motion-easing-standard
radius.full…--sd-radius-full
radius.sm…--sd-radius-sm
shape.radius-control…--sd-shape-radius-control
spacing.gap.xs…--sd-spacing-gap-xs
spacing.stack.md…--sd-spacing-stack-md
spacing.stack.sm…--sd-spacing-stack-sm
tabs.bg-hover…--sd-tabs-bg-hover
tabs.bg-pressed…--sd-tabs-bg-pressed
tabs.border…--sd-tabs-border
tabs.fg…--sd-tabs-fg
tabs.fg-pressed…--sd-tabs-fg-pressed
tabs.fg-selected…--sd-tabs-fg-selected
tabs.height…--sd-tabs-height
tabs.icon…--sd-tabs-icon
tabs.icon-selected…--sd-tabs-icon-selected
tabs.indicator…--sd-tabs-indicator
tabs.indicator-width…--sd-tabs-indicator-width
tabs.padding-x…--sd-tabs-padding-x
target.min…--sd-target-min

Install

The npm package isn’t published yet. When it is, this is the install line; until then, this page runs the same build of the package (from commit fa3abf8).

Install

npm i @superduperui/react @superduperui/tokens

Import

import { Tabs, TabList, Tab, TabPanel } from "@superduperui/react"

Props

Tabs also takes the props of the React Aria component it’s built on.

Tabs props
PropTypeWhat it does
variant'line' | 'contained'Default 'line'line (default): tabs over a rule, the selected one marked by a bar and a weight change; vertical tabs carry the bar at their start edge. contained: tabs in a track, the selected one raised on the canvas with a 3:1 edge (for a few short views of one thing, horizontal only).

When to use it

Use it for

  • 3 to 7 sibling sections of one object (Profile, Notifications, Security)
  • Vertical: more sections than fit across, or long labels, beside the panel
  • Contained: two to four short views of the same content (List, Board)

Use something else for

  • Navigating between pages: use navigation links with aria-current
  • Sequential steps: use Steps (Tier 2)

Accessibility

Screen readers announce "Profile, tab, selected, 1 of 3".

WCAG 2.2 AAA criteria the component meets in Comfortable density: 1.4.6, 1.4.9, 2.1.3, 2.3.2, 2.4.13, 2.5.5, 2.5.6, 2.3.3.

Compact density meets AA (2.5.8, 24px targets), not AAA.

What your product still has to do

  • Use keyboardActivation="manual" when panels are slow to load

Shared patterns it ships with: 2.4.8 (aria-current when used as page navigation).

This release’s test results

Test results for Tabs in this release
axe-core (WCAG A, AA, AAA and best practice)160 runs across every story, theme, density and width: 0 violations
Rendered text contrast595 text runs: 0 below 7:1, lowest 9.8:1
Target size160 targets: 0 under 44px in Comfortable
Focus appearance (2.4.13)36 focus stops in 3 browser engines: all pass
Keyboard scriptChromium 6 of 6, Firefox 6 of 6, WebKit 6 of 6

Manual passes with VoiceOver and NVDA haven’t been done yet.

Keyboard

Each step is scripted and run in Chromium, Firefox and WebKit.

Tabs keyboard steps
KeyWhat happens
TabTab enters the selected tab
ArrowRightRight moves to and selects the next tab
EndEnd moves to the last tab
HomeHome moves to the first tab
ArrowLeftLeft wraps to the last tab
TabTab leaves the tab list for the panel

In Figma

The Figma component is Tab, with Selected (False, True); State (Default, Hover, Pressed, Focus, Disabled); Icon position (Start, Top); Orientation (Horizontal, Vertical); Style (Line, Contained). The Figma library isn’t published yet.

Figma to code: Selected (False, True = Tabs defaultSelectedKey); State (Default, Hover, Pressed, Focus, Disabled = isDisabled); Label → children; Icon → icon when Show icon; Icon position → iconPosition (Start = "start", Top = "top"); Badge → badge when Show badge; Orientation (Horizontal, Vertical = Tabs orientation); Style (Line, Contained = Tabs variant).