⚠️ Design system CSS not found. Check the path in cms/docs.config.js → designSystemPath, then re-run npm run docgen.

Tabs

Switch between related content without leaving the page

Website / Tabs
Download .md file
Open .md in new tab
On this page
  • Basic usage
  • JavaScript
  • Keyboard interactions
  • Accessibility
  • Usage rules
  • CSS reference
  • Use in another product

Tabs organise content into panels that the user switches between. The component requires JavaScript (assets/js/tabs.js) for interaction and follows the WAI-ARIA tabs pattern.


Basic usage

Panel 1 content

Panel 2 content

Panel 3 content

<div class="tabs" role="tablist" aria-label="Section name">
  <button class="tab is-active" type="button" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">Tab One</button>
  <button class="tab" type="button" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2">Tab Two</button>
  <button class="tab" type="button" role="tab" aria-selected="false" aria-controls="panel-3" id="tab-3">Tab Three</button>
</div>

<div class="tab-panel" id="panel-1" role="tabpanel" aria-labelledby="tab-1">
  Panel 1 content
</div>
<div class="tab-panel is-hidden" id="panel-2" role="tabpanel" aria-labelledby="tab-2">
  Panel 2 content
</div>
<div class="tab-panel is-hidden" id="panel-3" role="tabpanel" aria-labelledby="tab-3">
  Panel 3 content
</div>

JavaScript

Include assets/js/tabs.js on any page that uses tabs. The script automatically initialises all [role="tablist"] elements on the page.

<script src="/assets/js/tabs.js"></script>

No manual initialisation is required.


Keyboard interactions

Key Action
Tab Moves focus to the active tab, then to the panel
ArrowRight Moves focus to the next tab
ArrowLeft Moves focus to the previous tab
Home Moves focus to the first tab
End Moves focus to the last tab
Enter / Space Activates the focused tab (via click)

Accessibility

  • The tab list uses role="tablist" with a descriptive aria-label
  • Each tab uses role="tab" with aria-selected and aria-controls
  • Each panel uses role="tabpanel" with aria-labelledby
  • Hidden panels use (display: none) so they are removed from the tab order
  • Focus ring appears on focus-visible

Usage rules

Do:

  • Use unique IDs for tabs and panels on each page
  • Set one tab as is-active and aria-selected="true" by default
  • Provide a descriptive aria-label on the tablist

Don't:

  • Don't use tabs for sequential steps (use a stepper pattern instead)
  • Don't nest tablists inside tab panels

CSS reference

This section documents how the component is built. For usage, see the sections above.

Tokens

Token Default What it controls
Active tab text colour
Inactive tab text colour
Bottom border indicator

Selectors

Selector Purpose
Tab list container: flex row, bottom border
Individual tab button: padding, colour, cursor
.tab:hover Tab hover, text colour change
.tab.is-active Active tab: active colour, bold weight
.tab.is-active::after Active indicator, bottom border pseudo-element
.tab:focus-visible Tab focus ring
Panel content container, padding
a.tab Link-as-tab variant, text-decoration reset
a.tab:hover Link tab hover

Use in another product

The design system installs once per product:

npm install github:bydefaultstudio/design-system-dist#semver:^4.7.0

This component's styles ship in design-system.css. Its behaviour ships as dist/js/tabs.js — copy it into the product's served assets and include it once per page:

<script src="assets/js/tabs.js" defer></script>

In React, render this contract through the packaged adapter instead of writing the markup by hand:

import { Tabs } from '@bydefaultstudio/design-system/react';

The adapter renders the contract above and bridges this component's events to props — see React.

On this page
  • Basic usage
  • JavaScript
  • Keyboard interactions
  • Accessibility
  • Usage rules
  • CSS reference
  • Use in another product
Previous Breadcrumb
Next Accordion

Was this page helpful?

We use this feedback to improve our documentation.

Thanks for your feedback

Send feedback

© 2026 By Default