Tabs

A row of tabs. By default it is a tablist: one tab in the page's tab order, the arrow keys of its orientation moving between tabs, Enter or Space selecting.

A row of tabs. By default it is a tablist: one tab in the page's tab order, the arrow keys of its orientation moving between tabs, Enter or Space selecting. With as="nav" the same look is a navigation of buttons or links, the current one marked aria-current="page". The row can carry content of its own before and after the tabs - an icon, a toolbar, a "+" button.

import { Tab, Tabs } from '@soroush.tech/design-system/Tabs'

;<Tabs value={value} onChange={setValue} textColor="secondary" color="secondary" aria-label="Items">
  <Tab value="one" label="Item One" />
  <Tab value="two" label="Item Two" icon={<Icon name="folder" size="sm" />} />
  <Tab value="three" label="Item Three" disabled />
</Tabs>

Tabs holds its own value (defaultValue) or takes it (value / onChange). To show a panel for each tab, put the tabs in a TabContext as a TabList - it links each tab to its TabPanel:

import { Tab, TabContext, TabList, TabPanel } from '@soroush.tech/design-system/Tabs'

;<TabContext value={value}>
  <TabList onChange={setValue} aria-label="Items">
    <Tab label="Item One" value="1" />
    <Tab label="Item Two" value="2" />
  </TabList>
  <TabPanel value="1">...</TabPanel>
  <TabPanel value="2">...</TabPanel>
</TabContext>

The family: Tabs, Tab, TabContext, TabList, TabPanel and TabScrollButton.

Examples

Default

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args} />

Boxed

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'boxed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'sm',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args} />

Tonal

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'tonal',
  orientation: 'horizontal',
  color: 'primary',
  size: 'sm',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args} />

Enclosed

Classic

The content of Classic. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'enclosed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'classic',
}

<WithContent args={args}>
  <Tab value="images" label="Images" />
  <Tab value="classic" label="Classic" />
  <Tab value="videos" label="Videos" />
  <Tab value="stores" label="Stores" />
  <Tab value="settings" label="Settings" />
  <Tab value="tools" label="Tools" />
</WithContent>

Enclosed Vertical

Classic

The content of Classic. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'enclosed',
  orientation: 'vertical',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'classic',
}

<View width="28rem">
  <WithContent args={args}>
    <Tab value="images" label="Images" />
    <Tab value="classic" label="Classic" />
    <Tab value="videos" label="Videos" />
  </WithContent>
</View>

Enclosed With Content Before And After

Classic

The content of Classic. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'enclosed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Search',
  defaultValue: 'classic',
}

<WithContent
  args={{
    ...args,
    startContent: <Icon name="zoom_in" size="sm" />,
    endContent: (
      <Button size="sm" variant="text" aria-label="Add a view">
        +
      </Button>
    ),
  }}
>
  <Tab value="images" label="Images" />
  <Tab value="classic" label="Classic" />
  <Tab value="videos" label="Videos" />
</WithContent>

Divider

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<Flex flexDirection="column" gap={4}>
  {tabsVariantTokens.flatMap((variant) =>
    [true, false].map((hasDivider) => (
      <Tabs
        key={`${variant}-${hasDivider}`}
        {...args}
        aria-label={`${variant}, ${hasDivider ? 'with' : 'without'} a divider`}
        variant={variant}
        hasDivider={hasDivider}
        startContent={<Icon name="zoom_in" size="sm" />}
        endContent={
          <Button size="sm" variant="text" aria-label="Add a view">
            +
          </Button>
        }
      />
    ))
  )}
</Flex>

Vertical

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'vertical',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<View width="28rem">
  <WithContent args={args} />
</View>

Full Width

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: true,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args} />

Centered

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: true,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args} />

Icon Position

Top

The content of Top. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'top',
}

<WithContent args={{ ...args, 'aria-label': 'Icon by position' }}>
  <Tab value="top" label="Top" icon={<Icon name="table" size="sm" color="inherit" />} />
  <Tab
    value="start"
    label="Start"
    icon={<Icon name="folder" size="sm" color="inherit" />}
    iconPosition="start"
  />
  <Tab
    value="end"
    label="End"
    icon={<Icon name="link" size="sm" color="inherit" />}
    iconPosition="end"
  />
  <Tab
    value="bottom"
    label="Bottom"
    icon={<Icon name="code" size="sm" color="inherit" />}
    iconPosition="bottom"
  />
</WithContent>

Wrapped Labels

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<View width="18rem">
  <Tabs {...args} fullWidth aria-label="Wrapped">
    <Tab value="canvas" label="Canvas" />
    <Tab value="long" label="A label long enough to wrap" isWrapped />
  </Tabs>
</View>

Disabled

Canvas

The content of Canvas. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<WithContent args={args}>
  <Tab value="canvas" label="Canvas" />
  <Tab value="system" label="Design system" disabled />
  <Tab value="common" label="Common components" />
</WithContent>

Navigation

const args: Partial<TabsProps> = {
  variant: 'boxed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Left panel',
  defaultValue: 'layers',
}

<Tabs {...args} as="nav">
  <Tab value="layers" label="Layers" href="#layers" />
  <Tab value="assets" label="Assets" href="#assets" />
  <Tab value="variables" label="Variables" href="#variables" />
</Tabs>

Content Before And After

Light

The content of Light. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'boxed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'sm',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'light',
}

<WithContent
  args={{
    ...args,
    'aria-label': 'Themes',
    startContent: <Icon name="groups" size="sm" />,
    endContent: (
      <Button size="sm" variant="text" aria-label="Add a theme">
        +
      </Button>
    ),
  }}
>
  <Tab value="light" label="Light" />
  <Tab value="dark" label="Dark" />
</WithContent>

With Panels

The canvas.

const { value: _value, defaultValue: _defaultValue, ...args } = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<TabContext defaultValue="canvas">
  <TabList {...args} />
  <TabPanel value="canvas" p={2}>
    <Typography>The canvas.</Typography>
  </TabPanel>
  <TabPanel value="system" p={2}>
    <Typography>The design system.</Typography>
  </TabPanel>
  <TabPanel value="common" p={2}>
    <Typography>Common components.</Typography>
  </TabPanel>
</TabContext>

Closable

const args: Partial<TabsProps> = {
  variant: 'boxed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'sm',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'canvas',
}

<EditorTabs {...args} />

Secondary Action

const args: Partial<TabsProps> = {
  variant: 'boxed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'sm',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Views',
  defaultValue: 'light',
}

<Tabs {...args} aria-label="Themes">
  <Tab value="light" label="Light" secondaryAction={<ThemeMenu name="Light" />} />
  <Tab value="dark" label="Dark" secondaryAction={<ThemeMenu name="Dark" />} />
</Tabs>

Scrolling

Component 1

The content of Component 1. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'scroll',
  scrollButtons: 'auto',
  'aria-label': 'Views',
  defaultValue: 0,
}

<View width="28rem" maxWidth="100%">
  <WithContent args={{ ...args, 'aria-label': 'Components' }}>{manyTabs}</WithContent>
</View>

Scroll Buttons Always

Component 1

The content of Component 1. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'scroll',
  scrollButtons: 'always',
  'aria-label': 'Views',
  defaultValue: 0,
}

<View width="28rem" maxWidth="100%">
  <WithContent args={{ ...args, 'aria-label': 'Components' }}>{manyTabs}</WithContent>
</View>

Visible Scrollbar

Component 1

The content of Component 1. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'scroll',
  scrollButtons: 'never',
  hasVisibleScrollbar: true,
  'aria-label': 'Views',
  defaultValue: 0,
}

<View width="28rem" maxWidth="100%">
  <WithContent args={{ ...args, 'aria-label': 'Components' }}>{manyTabs}</WithContent>
</View>

Overflow Menu

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'menu',
  overflowLabel: 'More tabs',
  'aria-label': 'Views',
  defaultValue: 0,
}

<OverflowRow {...args} />

Overflow Menu Vertical

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'vertical',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'menu',
  overflowLabel: 'More tabs',
  'aria-label': 'Views',
  defaultValue: 0,
}

<OverflowRow {...args} />

Overflow Menu Enclosed

const args: Partial<TabsProps> = {
  variant: 'enclosed',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'menu',
  overflowLabel: 'More tabs',
  'aria-label': 'Views',
  defaultValue: 0,
}

<OverflowRow
  {...args}
  endContent={
    <Button size="sm" variant="text" aria-label="New tab">
      +
    </Button>
  }
/>

Vertical Scrolling

Component 1

The content of Component 1. Choose another tab and this changes with it.

const args: Partial<TabsProps> = {
  variant: 'underline',
  orientation: 'vertical',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'scroll',
  'aria-label': 'Views',
  defaultValue: 0,
}

<View width="28rem" height="16rem" display="flex">
  <WithContent args={{ ...args, 'aria-label': 'Components' }}>{manyTabs}</WithContent>
</View>

Customized

<ThemeProvider theme={customTheme}>
  <View width="30rem" maxWidth="100%">
    <Tabs
      aria-label="Customized"
      defaultValue={0}
      overflowBehavior="scroll"
      shouldShowScrollButtonsOnMobile
      slots={{ scrollButton: ArrowButton }}
    >
      {Array.from({ length: 10 }, (_, index) => (
        <Tab key={index} value={index} label={`Item ${index + 1}`} />
      ))}
    </Tabs>
  </View>
</ThemeProvider>

Enclosed Without Divider

The classic view - its tab shares this ground.

<TabContext defaultValue="classic">
  <TabList
    aria-label="Views"
    variant="enclosed"
    hasDivider={false}
    bg="default"
    selectedBg="paper"
  >
    <Tab value="images" label="Images" />
    <Tab value="classic" label="Classic" />
    <Tab value="videos" label="Videos" />
  </TabList>
  {['images', 'classic', 'videos'].map((value) => (
    <TabPanel key={value} value={value} bg="paper" p={3}>
      <Typography>The {value} view - its tab shares this ground.</Typography>
    </TabPanel>
  ))}
</TabContext>

Reference Structure

Item One

<ControlledTabContext />

Tab

Default

const args: Partial<TabProps> = { label: 'Canvas', disabled: false, isWrapped: false, value: 'canvas' }

<Tabs aria-label="Views" defaultValue="canvas" variant="boxed">
  <Tab {...args} />
  <Tab value="system" label="Design system" />
</Tabs>

Own Look

<Tabs aria-label="Files" defaultValue="tabs" variant="enclosed" color="primary">
  <Tab value="readme" label="README.md" />
  <Tab value="tabs" label="Tabs.tsx ●" color="error" />
  <Tab value="theme" label="theme.ts" selectedBg="default" bg="paper" />
  <Tab value="notes" label="notes" size="sm" textColor="secondary" />
</Tabs>

With Icon

const args: Partial<TabProps> = {
  label: 'Canvas',
  iconPosition: 'start',
  disabled: false,
  isWrapped: false,
  value: 'canvas',
}

<Tabs aria-label="Views" defaultValue="canvas">
  <Tab {...args} icon={icon('folder', 'sm')} />
  <Tab value="code" label="Code" icon={icon('code', 'sm')} iconPosition={args.iconPosition} />
</Tabs>

Secondary Action

const args: Partial<TabProps> = { label: 'Canvas', disabled: false, isWrapped: false, value: 'canvas' }

<Tabs aria-label="Views" defaultValue="canvas" variant="boxed" size="sm">
  <Tab
    {...args}
    secondaryAction={
      <Menu
        align="end"
        trigger={
          <Button size="sm" variant="text" aria-label="Canvas options">
            ⋮
          </Button>
        }
      >
        <MenuItem value="rename">Rename...</MenuItem>
        <MenuItem value="duplicate">Duplicate</MenuItem>
        <MenuDivider />
        <MenuItem value="close">Close</MenuItem>
      </Menu>
    }
  />
  <Tab value="system" label="Design system" />
</Tabs>

Icon Positions

iconPosition - top when unset, then each side
Icon only - no label; title names it
Mixed in one row - every label on the same line
Below the label - labels on the row's first line
<Scenarios>
  <Scenario title="iconPosition - top when unset, then each side">
    <Tabs aria-label="Icon positions" defaultValue="unset">
      <Tab value="unset" label="unset" icon={icon('table', 'sm')} />
      {tabIconPositionTokens.map((position) => (
        <Tab
          key={position}
          value={position}
          label={position}
          icon={icon('folder', 'sm')}
          iconPosition={position}
        />
      ))}
    </Tabs>
  </Scenario>
  <Scenario title="Icon only - no label; title names it">
    <Tabs aria-label="Icon only" defaultValue="files">
      <Tab value="files" title="Files" icon={icon('folder', 'sm')} />
      <Tab value="code" title="Code" icon={icon('code', 'sm')} />
      <Tab value="history" title="History" icon={icon('history', 'sm')} />
    </Tabs>
  </Scenario>
  <Scenario title="Mixed in one row - every label on the same line">
    <Tabs aria-label="Mixed" defaultValue="plain">
      <Tab value="plain" label="Plain" />
      <Tab value="start" label="Start" icon={icon('folder', 'sm')} iconPosition="start" />
      <Tab value="top" label="Top" icon={icon('table', 'sm')} />
      <Tab value="end" label="End" icon={icon('link', 'sm')} iconPosition="end" />
    </Tabs>
  </Scenario>
  <Scenario title="Below the label - labels on the row's first line">
    <Tabs aria-label="Icons below" defaultValue="files">
      <Tab value="files" label="Files" icon={icon('folder', 'sm')} iconPosition="bottom" />
      <Tab value="code" label="Code" icon={icon('code', 'md')} iconPosition="bottom" />
      <Tab value="history" label="History" icon={icon('history')} iconPosition="bottom" />
    </Tabs>
  </Scenario>
</Scenarios>

Icon Sizes

Tabs size="sm", iconPosition="start" - an icon of every size
Tabs size="md", iconPosition="start" - an icon of every size
Tabs size="lg", iconPosition="start" - an icon of every size
Tabs size="sm", iconPosition="top" - an icon of every size
Tabs size="md", iconPosition="top" - an icon of every size
Tabs size="lg", iconPosition="top" - an icon of every size
<Scenarios>
  {(['start', 'top'] as const).flatMap((position) =>
    tabSizes.map((tabSize) => (
      <Scenario
        key={`${position}-${tabSize}`}
        title={`Tabs size="${tabSize}", iconPosition="${position}" - an icon of every size`}
      >
        <Tabs
          aria-label={`${position} icons, ${tabSize} tabs`}
          size={tabSize}
          defaultValue="unset"
        >
          {iconSizes.map((iconSize) => (
            <Tab
              key={iconSizeName(iconSize)}
              value={iconSizeName(iconSize)}
              label={`icon ${iconSizeName(iconSize)}`}
              icon={icon(position === 'top' ? 'table' : 'folder', iconSize)}
              iconPosition={position}
            />
          ))}
        </Tabs>
      </Scenario>
    ))
  )}
</Scenarios>

Icons In Every Look

variant="underline"
variant="boxed"
fullWidth - the tabs share the row
orientation="vertical"
as="nav" - links
<Scenarios>
  {(['underline', 'boxed'] as const).map((variant) => (
    <Scenario key={variant} title={`variant="${variant}"`}>
      <Tabs aria-label={`${variant} with icons`} variant={variant} defaultValue="start">
        <Tab value="start" label="Start" icon={icon('folder', 'sm')} iconPosition="start" />
        <Tab value="top" label="Top" icon={icon('table', 'sm')} />
        <Tab value="plain" label="Plain" />
      </Tabs>
    </Scenario>
  ))}
  <Scenario title="fullWidth - the tabs share the row">
    <Tabs aria-label="Full width with icons" fullWidth defaultValue="start">
      <Tab value="start" label="Start" icon={icon('folder', 'sm')} iconPosition="start" />
      <Tab value="top" label="Top" icon={icon('table', 'sm')} />
      <Tab value="end" label="End" icon={icon('link', 'sm')} iconPosition="end" />
    </Tabs>
  </Scenario>
  <Scenario title='orientation="vertical"'>
    <Flex gap={4}>
      <Tabs aria-label="Vertical start icons" orientation="vertical" defaultValue="files">
        <Tab value="files" label="Files" icon={icon('folder', 'sm')} iconPosition="start" />
        <Tab value="code" label="Code" icon={icon('code', 'sm')} iconPosition="start" />
        <Tab
          value="history"
          label="History"
          icon={icon('history', 'sm')}
          iconPosition="start"
        />
      </Tabs>
      <Tabs aria-label="Vertical top icons" orientation="vertical" defaultValue="files">
        <Tab value="files" label="Files" icon={icon('folder', 'sm')} />
        <Tab value="code" label="Code" icon={icon('code', 'sm')} />
        <Tab value="history" label="History" icon={icon('history', 'sm')} />
      </Tabs>
    </Flex>
  </Scenario>
  <Scenario title='as="nav" - links'>
    <Tabs aria-label="Navigation with icons" as="nav" defaultValue="files">
      <Tab
        value="files"
        label="Files"
        href="#files"
        icon={icon('folder', 'sm')}
        iconPosition="start"
      />
      <Tab
        value="code"
        label="Code"
        href="#code"
        icon={icon('code', 'sm')}
        iconPosition="start"
      />
    </Tabs>
  </Scenario>
</Scenarios>

Icons With Every State

disabled
isWrapped - a long label on two lines
closeLabel - closable, with an icon
secondaryAction - a menu, with an icon
<Scenarios>
  <Scenario title="disabled">
    <Tabs aria-label="Disabled with icons" defaultValue="on">
      <Tab value="on" label="Enabled" icon={icon('folder', 'sm')} iconPosition="start" />
      <Tab
        value="off"
        label="Disabled"
        icon={icon('lock', 'sm')}
        iconPosition="start"
        disabled
      />
    </Tabs>
  </Scenario>
  <Scenario title="isWrapped - a long label on two lines">
    <Flex width="16rem">
      <Tabs aria-label="Wrapped with icons" fullWidth defaultValue="long">
        <Tab
          value="long"
          label="A label long enough to wrap"
          icon={icon('folder', 'sm')}
          iconPosition="start"
          isWrapped
        />
        <Tab value="top" label="Another long label here" icon={icon('table', 'sm')} isWrapped />
      </Tabs>
    </Flex>
  </Scenario>
  <Scenario title="closeLabel - closable, with an icon">
    <Tabs aria-label="Closable with icons" variant="boxed" defaultValue="button">
      <Tab
        value="button"
        label="Button"
        icon={icon('code', 'sm')}
        iconPosition="start"
        closeLabel="Close Button"
      />
      <Tab value="card" label="Card" icon={icon('table', 'sm')} closeLabel="Close Card" />
    </Tabs>
  </Scenario>
  <Scenario title="secondaryAction - a menu, with an icon">
    <Tabs aria-label="Secondary action with icons" variant="boxed" defaultValue="light">
      <Tab
        value="light"
        label="Light"
        icon={icon('visibility', 'sm')}
        iconPosition="start"
        secondaryAction={
          <Menu
            align="end"
            trigger={
              <Button size="sm" variant="text" aria-label="Light options">
                ⋮
              </Button>
            }
          >
            <MenuItem value="rename">Rename...</MenuItem>
          </Menu>
        }
      />
      <Tab value="dark" label="Dark" icon={icon('visibility', 'sm')} />
    </Tabs>
  </Scenario>
</Scenarios>

TabContext

Default

The layers panel.

const { defaultValue }: Partial<TabContextProps> = { defaultValue: 'layers' }

<TabContext key={String(defaultValue)} defaultValue={defaultValue}>
  <TabList aria-label="Left panel">
    {panelValues.map((value) => (
      <Tab key={value} value={value} label={labelOf(value)} />
    ))}
  </TabList>
  <Panels />
</TabContext>

Controlled Value

The layers panel.

const args: Partial<TabContextProps> = { defaultValue: 'layers' }

<Controlled {...args} />

TabList

Default

Layers of the page.

const args: Partial<TabListProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Left panel',
}

<TabContext defaultValue="layers">
  <TabList {...args} />
  <TabPanel value="layers" p={2}>
    <Typography>Layers of the page.</Typography>
  </TabPanel>
  <TabPanel value="assets" p={2}>
    <Typography>The page&apos;s assets.</Typography>
  </TabPanel>
</TabContext>

Vertical

Layers of the page.

const args: Partial<TabListProps> = {
  variant: 'underline',
  orientation: 'vertical',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  'aria-label': 'Left panel',
}

<TabContext defaultValue="layers">
  <Flex flexDirection="row" gap={2}>
    <TabList {...args} />
    <TabPanel value="layers" p={2}>
      <Typography>Layers of the page.</Typography>
    </TabPanel>
    <TabPanel value="assets" p={2}>
      <Typography>The page&apos;s assets.</Typography>
    </TabPanel>
  </Flex>
</TabContext>

Overflow Menu

The layers panel.

const args: Partial<TabListProps> = {
  variant: 'underline',
  orientation: 'horizontal',
  color: 'primary',
  size: 'md',
  fullWidth: false,
  isCentered: false,
  shouldSelectOnFocus: false,
  overflowBehavior: 'menu',
  overflowLabel: 'More panels',
  'aria-label': 'Left panel',
}

<TabContext defaultValue="layers">
  <View width="18rem" maxWidth="100%" overflow="hidden" style={{ resize: 'horizontal' }}>
    <TabList {...args}>
      {panelValues.map((value) => (
        <Tab key={value} value={value} label={value[0].toUpperCase() + value.slice(1)} />
      ))}
    </TabList>
  </View>
  {panelValues.map((value) => (
    <TabPanel key={value} value={value} p={2}>
      <Typography>The {value} panel.</Typography>
    </TabPanel>
  ))}
</TabContext>

TabPanel

Default

Layers of the page.

const args: Partial<TabPanelProps> = { shouldKeepMounted: false, p: 2, value: 'layers' }

<TabContext defaultValue="layers">
  <TabList aria-label="Left panel">
    <Tab value="layers" label="Layers" />
    <Tab value="assets" label="Assets" />
  </TabList>
  <TabPanel {...args} />
  <TabPanel {...args} value="assets">
    <Typography>The page&apos;s assets.</Typography>
  </TabPanel>
</TabContext>

TabScrollButton

Default

<TabScrollButton direction="end" orientation="horizontal" />