Canvas Design System
Main Site Tokens

Tabs

Tab navigation for switching between content panels. Supports underline, pill, and segmented variants.

When to Use

Use Tabs when: Switching between related views at the same hierarchy level, such as content categories, settings sections, or filter groups. Tabs keep users in context without navigating to a new page.
Don't use when: The user needs to compare content across tabs simultaneously. For sequential steps, use a stepper. For collapsible content, use Accordion.

Underline (Default)

<?= ws_tabs([
    ['id' => 'overview', 'label' => 'Overview', 'active' => true],
    ['id' => 'details', 'label' => 'Details'],
    ['id' => 'resources', 'label' => 'Resources', 'count' => 5],
]) ?>

Pill Variant

<?= ws_tabs($items, ['variant' => 'pill']) ?>

Segmented Variant

<?= ws_tabs($items, ['variant' => 'segmented']) ?>

With Icons & Gating

Tabs can include leading icons, badge counts, gated (locked) states, and disabled states.

<?= ws_tabs([
    ['id' => 'info', 'label' => 'Info', 'icon' => 'info', 'active' => true],
    ['id' => 'premium', 'label' => 'Premium', 'icon' => 'star', 'gated' => true],
    ['id' => 'disabled', 'label' => 'Disabled', 'disabled' => true],
]) ?>

Sizes

Small

Medium (default)

Large

Parameters

Tabs Options

ParameterTypeDefaultDescription
idstringauto-generatedContainer element ID
variantstringunderlineunderline | pill | segmented
sizestringmdsm | md | lg
scrollablebooltrueEnable horizontal scroll on mobile
classstring''Additional CSS classes

Item Options

ParameterTypeDefaultDescription
idstringauto-generatedTab identifier (used in data-tab)
labelstringTab label text
iconstringnullMaterial icon name
countintnullBadge count number
activeboolfalseInitially selected tab
disabledboolfalseDisabled state
gatedboolfalseShows lock icon (premium gating)

Anatomy

.ws-tabs — Outer container
.ws-tabs__nav — Tab button row (role=tablist)
.ws-tabs__btn — Individual tab button
.ws-tabs__icon — Optional leading icon
.ws-tabs__label — Tab label text
.ws-tabs__count — Badge count
.ws-tabs__lock — Gated lock icon

CSS Classes

ClassPurpose
.ws-tabsBase tabs styles
.ws-tabs--underlineUnderline variant (default)
.ws-tabs--pillPill variant
.ws-tabs--segmentedSegmented variant
.ws-tabs--sm / --md / --lgSize modifiers
.ws-tabs--scrollableHorizontal scroll on overflow
.ws-tabs__btn.is-activeActive/selected tab
.ws-tabs__btn.is-disabledDisabled tab
.ws-tabs__btn.is-gatedGated/locked tab

Accessibility

The tabs component follows the WAI-ARIA Tabs pattern.

ARIA Attributes

ElementAttributeValue
.ws-tabs__navroletablist
.ws-tabs__btnroletab
.ws-tabs__btnaria-selectedtrue | false
.ws-tabs__btnaria-controlsID of associated panel
.ws-tabs__btntabindex0 (active) | -1 (inactive)
.ws-tabs__btn.is-disabledaria-disabledtrue
Tab panelroletabpanel
Tab panelaria-labelledbyID of associated tab button

Keyboard Navigation

KeyBehavior
TabMoves focus into the tablist, then to the active tab's panel content
/ Moves focus between tabs. Wraps from last to first (and vice versa). Activates the focused tab.
HomeMoves focus to the first non-disabled tab
EndMoves focus to the last non-disabled tab
Enter / SpaceActivates the focused tab (if using manual activation mode)

Implementation Notes

Do: Disabled tabs receive aria-disabled="true" and are skipped during arrow key navigation. Gated tabs remain focusable so screen readers can announce the locked state.
Don't: Don't use display: none to hide inactive panels — use hidden attribute or aria-hidden so panel content is properly excluded from the accessibility tree while remaining discoverable.

Files

FilePurpose
includes/components/helpers.phpws_tabs() helper function
includes/components/tabs.phpTabs template
includes/components/components.cssTabs CSS (.ws-tabs rules)
includes/components/components.jsTab switching logic