Underline Tabs

Underline Tabs are best used when switching between related content within a page.

const Example01 = () => {
    const [active, setActive] = React.useState(0)
    return (
        <TabGroup>
            <Tab onClick={() => setActive(0)} active={active === 0}>All</Tab>
            <Tab onClick={() => setActive(1)} active={active === 1}>Action Required</Tab>
            <Tab onClick={() => setActive(2)} active={active === 2}>Insights</Tab>
            <Tab onClick={() => setActive(3)} active={active === 3}>Settings</Tab>
        </TabGroup>
    )
}
render (Example01)

Underline tabs have a minimum width of 48px. The text and hover/active underline are always equal in width beyond this minimum.

With Badges

<TabGroup>
    <Tab>Ready To Bill <Tag compact badge>127</Tag></Tab>
    <Tab active>Action Required <Tag compact badge color="critical">25</Tag></Tab>
    <Tab>Insights</Tab>
    <Tab>Settings</Tab>
</TabGroup>

With Icons

<TabGroup>
    <Tab active><Icon name="info" />Action Requested</Tab>
    <Tab><Icon name="poll" /> Insights</Tab>
    <Tab><Icon name="settings" /> Settings</Tab>
</TabGroup>

Tab Action

Tab actions, represented with a button or icon, can be represented as a tab itself. This can be useful for non-navigational actions related to the tab content such as adding a new tab.

<TabGroup
    action={
        <Button size="xsmall" primary>Call to Action</Button>
    }
>
    <style>{`
      .icon-hov {
            cursor: pointer;
        }
        .icon-hov:hover svg { fill: #2270ee; }
    `}</style>
    <Tab active>Action Requested</Tab>
    <Tab> Insights</Tab>
</TabGroup>
<TabGroup
    action={
        <Icon name="add_circle" className="icon-hov fs-4 c-neutral-200" />
    }
>
    <style>{`
      .icon-hov {
            cursor: pointer;
        }
        .icon-hov:hover svg { fill: #2270ee; }
    `}</style>
    <Tab active>Action Requested</Tab>
    <Tab> Insights</Tab>
</TabGroup>

Divided Tabs

A vertical pipe (|) divider can be applied to both sides of an inactive tab. This is useful when there are many tabs on a page.

const DividedTabsExample = () => {
    const [active, setActive] = React.useState(0)
    return (
        <TabGroup
            verticallyDivided
            action={
                <Button size="xsmall" primary>Call to Action</Button>
            }
        >
            <Tab active={active == 0} onClick={() => setActive(0)}>All</Tab>
            <Tab active={active == 1} onClick={() => setActive(1)}>Action Required</Tab>
            <Tab active={active == 2} onClick={() => setActive(2)}>Insights</Tab>
            <Tab active={active == 3} onClick={() => setActive(3)}>Action Required</Tab>
            <Tab active={active == 4} onClick={() => setActive(4)}>Insights</Tab>
            <Tab active={active == 5} onClick={() => setActive(5)}>Action Required</Tab>
        </TabGroup>
    )
}
render (DividedTabsExample)

Fitted

<Card>
    <Card.Section>
        <TabGroup fitted divider={false}>
            <Tab active><Icon name="info" />Action Requested</Tab>
            <Tab><Icon name="poll" /> Insights</Tab>
            <Tab ><Icon name="settings" /> Settings</Tab>
        </TabGroup>
    </Card.Section>
    <Card.Section>
    This card has supporting text below as a natural lead-in to additional content.
    </Card.Section>
</Card>

Underline tabs can be fitted to a card. This pattern should only be used when there are 2 or 3 tabs.

Index Tabs

Index Tabs are best used when switching between tabs only affects content within the visual container its attached to. They can also be nested within an underline tab.

    <TabGroup style="index">
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
    </TabGroup>

Index tabs have a minimum width of 64px. The text and hover/active underline are always equal in width beyond this minimum.

With Badges

    <TabGroup style="index">
        <Tab>Ready To Bill <Tag compact badge>127</Tag></Tab>
        <Tab active>Action Required <Tag compact badge color="critical">25</Tag></Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
    </TabGroup>

With Icons

    <TabGroup style="index">
        <Tab active><Icon name="info" />Action Requested</Tab>
        <Tab><Icon name="poll" /> Insights</Tab>
        <Tab><Icon name="settings" /> Settings</Tab>
    </TabGroup>

Fitted

<Card padding="thin">
    <Card.Section>
        <TabGroup style="index" fitted divider={false}>
            <Tab>Action Required</Tab>
            <Tab>Insights</Tab>
            <Tab active>Settings</Tab>
        </TabGroup>
    </Card.Section>
    <Card.Section>
      Lorem ipsum dolar emit
    </Card.Section>
</Card>

Max Width

Prevents a tab from exceeding 240px in length. When combined with the fitted property, can make tabs stretch to the maximum width.

    <TabGroup style="index" fitted maxWidth>
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
    </TabGroup>

Left Aligned Tab Text

By default, text is centered within a tab.

    <TabGroup style="index" fitted maxWidth textAlign="left">
        <Tab active>John Doe</Tab>
        <Tab>New Tab</Tab>
        <Tab>Insights</Tab>
    </TabGroup>

Divided Tabs

const DividedTabsExample2 = () => {
    const [active, setActive] = React.useState(0)
    return (
        <TabGroup
            style="index"
            verticallyDivided
            action={
                <Button size="xsmall" primary>Call to Action</Button>
            }
        >
            <Tab active={active == 0} onClick={() => setActive(0)}>All</Tab>
            <Tab active={active == 1} onClick={() => setActive(1)}>Action Required</Tab>
            <Tab active={active == 2} onClick={() => setActive(2)}>Insights</Tab>
            <Tab active={active == 3} onClick={() => setActive(3)}>Action Required</Tab>
            <Tab active={active == 4} onClick={() => setActive(4)}>Insights</Tab>
            <Tab active={active == 5} onClick={() => setActive(5)}>Action Required</Tab>
        </TabGroup>
    )
}
render (DividedTabsExample2)

With a Background

Tabs can take advantage of different background colors. The blue active state and dividers are removed, and inactive tabs switch text color.

<div className='box-demo'>
    <TabGroup style="index" dark divider={false}>
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
    </TabGroup>
    <style>{`
      .box-demo {
            background: #8C9CA5;
            padding: 20px 20px 0;
        }
      .tab-demo {
            background: white;
            width: calc(100% + 40px);
            height: 40px;
            margin: 0 -20px;
        }
    `}</style>
    <div className='tab-demo' />
</div>
<div className='box-demo-2'>
    <TabGroup style="index" dark verticallyDivided divider={false}>
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
    </TabGroup>
    <style>{`
      .box-demo-2 {
            background: #232323;
            padding: 20px 20px 0;
        }
      .tab-demo {
            background: white;
            width: calc(100% + 40px);
            height: 40px;
            margin: 0 -20px;
        }
    `}</style>
    <div className='tab-demo' />
</div>

Sizing

A taller variation of index tabs can be used for important navigation at the top of a page.

    <TabGroup style="index" size="large">
        <Tab active>Customers</Tab>
        <Tab>Available Inventory</Tab>
        <Tab>Analytics</Tab>
        <Tab>Marketing</Tab>
    </TabGroup>

Nested

If a page needs multiple levels of tabs, the index tab can be nested in a underline tab.

<div>
    <TabGroup>
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
    </TabGroup>
    <br/>
    <Card padding="thin">
        <Card.Section>
            <TabGroup style="index" divider={false}>
                <Tab>SubTab 1</Tab>
                <Tab>SubTab 2</Tab>
                <Tab active>SubTab 3</Tab>
                <Tab>SubTab 4</Tab>
                <Tab>SubTab 5</Tab>
            </TabGroup>
        </Card.Section>
        <Card.Section>
            Lorem ipsum dolar emit
        </Card.Section>
    </Card>
</div>

Responsive

Tabs will scroll horizontally when overflowed.

<div>
    <Headline className="m-b-3">Horizontal scrolling</Headline>
    <TabGroup>
        <Tab active>All</Tab>
        <Tab>Action Required</Tab>
        <Tab>Insights</Tab>
        <Tab>Settings</Tab>
        <Tab>More Items</Tab>
        <Tab>Sixth Item</Tab>
    </TabGroup>
    <br/>
    <Card padding="thin">
        <Card.Section>
            <TabGroup style="index" divider={false}>
                <Tab>SubTab 1</Tab>
                <Tab active>SubTab 2</Tab>
                <Tab>SubTab 3</Tab>
                <Tab>SubTab 4</Tab>
                <Tab>SubTab 5</Tab>
                <Tab>SubTab 6</Tab>
            </TabGroup>
        </Card.Section>
        <Card.Section>
            Lorem ipsum dolar emit
        </Card.Section>
    </Card>
</div>

Best Practices

  • Avoid tab names over 2 words long.
  • Should be used with 2–10 choices.
  • Only 1 tab should be active at a time.
  • Avoid the disabled state. If a user cannot use it, remove it from the tab.
  • Content within a tab should be mutually exclusive from another tab's content.
  • A visual divider should always be present. The default divider is a gray line.
  • On tab switch, the <title> should change to reflect the new content.
  • More UX practices: Tabs, Used Right (NN Group)

Importing

import { TabGroup, Tab } from '@servicetitan/design-system';