components / tabs

Tabs

Keyboard-navigable view switching with compiled triggers and tokenized panels.

Overview

What it is

This section explains the intent of the component before the implementation details.

  • Tabs switch between related views that should stay available within the same page context.
  • The DK contract keeps trigger styling, active-state clarity, and panel rhythm consistent across presets.

Aliases

tabset

Decision guide

How to choose it

Use these notes to decide quickly whether this is the right DK component for the job.

Decision guide

  • Choose Tabs when the interaction should feel deliberate, documented, and reusable across product surfaces.
  • If the scenario is more specialized than the current Tabs contract, prefer a simpler component or build a dedicated workflow on top of the lower-level DK primitives.

Do

  • Keep the Tabs label, value, or status language direct enough to scan quickly.
  • Use the documented size and state recipes instead of inventing one-off variants in product code.

Do not

  • Do not hide the main purpose of Tabs behind decorative copy or overloaded surface treatments.
  • Do not stretch Tabs into a workflow it was not designed to handle just because the visuals are close.

Usage

When to use

Prefer these situations when choosing this component.

  • Use Tabs when the user is switching between closely related views or information groups.
  • Keep tab labels short and stable so people can scan the set quickly.

Usage

When not to use

These patterns are better served by a different component or a simpler surface.

  • Do not use Tabs when the content should be shown side by side or in a step sequence.
  • Do not overload tab labels with long explanatory text.

Anti-patterns

  • Using Tabs as a generic layout container instead of a purpose-built interaction or content surface.
  • Forking the documented recipe in product code instead of extending the shared DK contract.

Migration notes

  • Migrate legacy tabs usage by mapping existing variants and states onto the documented DK props before porting any custom styling.
  • Treat the docs examples as the reference contract for accessibility and event behavior during adoption.

Anatomy

Structural parts

The anatomy explains which pieces matter to the recipe and accessibility model.

tab list

list

Group of tab triggers.

tab trigger

tab

Single trigger selecting a panel.

tab panel

panel

Visible content for the selected tab.

API

Public interface

The docs contract distinguishes props, DOM events, and slots so integration behavior is explicit.

Props

NameTypeDefaultDescription
valuestringControlled selected tab value.
itemsArray<{ value: string; label: string; disabled?: boolean }>Tab trigger definitions.
panelsRecord<string, string>Panel content keyed by tab value in the current docs implementation.
orientation'horizontal' | 'vertical''horizontal'Chooses trigger layout orientation.
activation'automatic' | 'manual''automatic'Chooses whether focus movement activates tabs immediately.
size'sm' | 'md''md'Chooses the tab size recipe.
onChange(detail: { value: string | undefined }) => voidCallback fired when the active tab changes.
themeThemeContractOverrides the compiled DK theme used to resolve tokens and recipes for this component.

Events

NamePayloadDescription
change{ value: string | undefined }Fires when the component commits a new value.

Slots

NameDescription
defaultTabs is data-driven in the current docs contract and panels are supplied by value map.

Recipes

Variants, sizes, and states

These notes summarize the intended recipe surface rather than exposing raw implementation detail first.

Variants

  • horizontal: Default row of tab triggers
  • vertical: Stacked tab trigger layout for some side-panel cases

Sizes

  • sm: Compact tabs
  • md: Default tabs

States

  • rest: Unselected tab
  • selected: Active tab
  • disabled: Unavailable tab

Accessibility

Accessibility contract

This is the behavior the component promises to assistive tech and keyboard users today.

Semantics

  • Uses proper tablist, tab, and tabpanel semantics.

Keyboard

  • Arrow keys move focus between tabs and activation follows the chosen activation mode.

Screen readers

  • Active tab and associated panel relationship remain explicit.

Checklist

  • Verify the visible label or title still produces a clear accessible name.
  • Verify focus remains obvious in every supported state and size.
  • Verify disabled, selected, and error states do not rely on color alone.

Implementation

Tokens and slot vars

This section shows the representative compiled slot variables that the runtime consumes for the selected design system.

root

--dk-tabs-gap
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)

list

--dk-tabs-list-gap
clamp(0.563rem, 0.5156rem + 0.208vw, 0.703rem)
--dk-tabs-list-bg
#e5e8ed
--dk-tabs-list-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)

trigger

--dk-tabs-trigger-bg
transparent
--dk-tabs-trigger-fg
#16181c
--dk-tabs-trigger-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-tabs-trigger-block-size
36px
--dk-tabs-trigger-inline-padding
clamp(1.333rem, 1.2222rem + 0.494vw, 1.667rem)
--dk-tabs-trigger-font-size
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-tabs-trigger-font-weight
600
--dk-tabs-trigger-motion-duration
200ms

indicator

--dk-tabs-indicator-bg
#2072e4
--dk-tabs-indicator-thickness
2px

panel

--dk-tabs-panel-bg
#f2f5fb
--dk-tabs-panel-fg
#16181c
--dk-tabs-panel-radius
clamp(0.75rem, 0.6875rem + 0.278vw, 0.938rem)
--dk-tabs-panel-padding
clamp(1.778rem, 1.6296rem + 0.658vw, 2.222rem)

Implementation notes

  • Tabs is for view switching inside one context; use SegmentedControl when the change is more mode-like and compact.

Implementation checklist

  • Use the public component API first and only drop to lower-level primitives when the component contract genuinely does not fit.
  • Keep theme overrides token-driven so proofs and recipes stay meaningful.
  • Verify the shipped examples and proof fixtures still represent the real product scenario you are implementing.

Examples

Copy-paste examples

Each example is intentionally practical, grouped by starter, common pattern, and edge-case coverage.

Examples
3
Depth
baseline

Starter

1 example

starter

Starter: simple tabset

Switch between two related views.

Copy snippet

<Tabs items={[{ value: 'overview', label: 'Overview' }, { value: 'activity', label: 'Activity' }]} value="overview" panels={{ overview: 'Overview content', activity: 'Activity content' }} />

Common patterns

1 example

common

Common: manual activation

Use manual activation when changing panels is more expensive or disruptive.

Copy snippet

<Tabs activation="manual" items={tabItems} value="overview" panels={panels} />

Edge cases

1 example

edge

Edge: vertical trigger set

Vertical tabs can work in inspector or side-panel layouts.

Copy snippet

<Tabs orientation="vertical" items={tabItems} value="details" panels={panels} />

Verification

Curated proof fixtures

Proofs stay visible in the docs so the system shows what it can guarantee, not just what it can render.

horizontal-default (orientation=horizontal|size=sm)

pass

orientation=horizontal|size=sm

  • Contrast: 90.8 Lc
  • Target: 32px

vertical-default (orientation=vertical|size=sm)

pass

orientation=vertical|size=sm

  • Contrast: 90.8 Lc
  • Target: 32px

horizontal-default (orientation=horizontal|size=md)

pass

orientation=horizontal|size=md

  • Contrast: 90.8 Lc
  • Target: 36px

vertical-default (orientation=vertical|size=md)

pass

orientation=vertical|size=md

  • Contrast: 90.8 Lc
  • Target: 36px

horizontal-default (orientation=horizontal|size=lg)

pass

orientation=horizontal|size=lg

  • Contrast: 90.8 Lc
  • Target: 44px

vertical-default (orientation=vertical|size=lg)

pass

orientation=vertical|size=lg

  • Contrast: 90.8 Lc
  • Target: 44px

selected (orientation=horizontal|size=sm)

pass

orientation=horizontal|size=sm

  • Contrast: 90.8 Lc
  • Target: 32px

selected (orientation=vertical|size=sm)

pass

orientation=vertical|size=sm

  • Contrast: 90.8 Lc
  • Target: 32px

selected (orientation=horizontal|size=md)

pass

orientation=horizontal|size=md

  • Contrast: 90.8 Lc
  • Target: 36px

selected (orientation=vertical|size=md)

pass

orientation=vertical|size=md

  • Contrast: 90.8 Lc
  • Target: 36px

selected (orientation=horizontal|size=lg)

pass

orientation=horizontal|size=lg

  • Contrast: 90.8 Lc
  • Target: 44px

selected (orientation=vertical|size=lg)

pass

orientation=vertical|size=lg

  • Contrast: 90.8 Lc
  • Target: 44px