Tabs
Horizontal navigation between related panels. Each .tab carries a bottom-border that hairlines along with its siblings; the active tab swaps the hairline for a solid dark rule. Composes with an optional leading icon + an optional small inline badge. Values from Figma node 3817-106 (light) + 5934-1968 (dark). Two alignment axes: Horizontal (default — icon · label · badge in a row) and Vertical (.tab-vertical — icon stacks above, label-row below). Tabs compose with the canonical Badge component — drop a .badge.badge-label.badge-info inside any tab and the badge inherits the matching height per tab size automatically (sm → 12 px, md → 16 px, lg → 20 px) via cascade. Three sizes (lg 48 / md 40 default / sm 32) × four states (Default · Hover · Active · Disabled).
When to use
- Use to organise related content within the same view (Overview, Details, History)
- Keep tab labels short — 1–3 words
- Always show at least 2 tabs — a single tab is not a tab group
- Compose with Badge for notification counts on specific tabs
- Use for navigation between separate pages — use sidebar Links instead
- Show more than 7 tabs in a single row — consider consolidating
- Use
.tab-badge— it was removed in v2.20.0. Use.badge.badge-labelinstead - Mix horizontal and vertical tab layouts on the same page
DigiLawyer Patterns
No specific DigiLawyer patterns documented yet — to be added.
Anatomy
A Tabs component is a row of tab elements sharing a bottom hairline. The active tab shows a solid accent rule.
- 1Tabs wrapper —
.tabs. Flex row sharing a bottom hairline border. - 2Tab —
.tab. Individual tab button. Add size modifier. - 3Active indicator —
.tab.active. Swaps the hairline for a solid bottom border in--tab-active-bordercolour. - 4Icon slot —
.tab-icon. Optional leading icon, scales with size. - 5Badge —
.badge.badge-labelinside the tab. Size inherits from the parent tab size modifier.
Default
A .tabs row contains a flat list of .tab buttons. Mark the active tab with aria-selected="true" (preferred) or .is-active. Each tab can carry an optional leading .tab-icon and an optional inline .badge.badge-label.badge-info count pill (composes with the Badge component). Click a tab to switch panels — the demo below wires it up with a 6-line script.
<div class="tabs" role="tablist"> <button class="tab" role="tab" aria-selected="true"> <span class="tab-icon"><svg>…</svg></span> Label <span class="badge badge-label badge-info">text</span> </button> <button class="tab" role="tab">Label</button> </div>
Sizes
Three sizes per Figma symbols. .tab-lg uses Body 1 (16/24) Medium; default md uses Body 2 (14/24) Medium; .tab-sm drops to Caption 1 (12/16) Medium. The leading icon stays 16×16 across all sizes (it's an affordance, not a content scaler). Horizontal padding is --spacing-sm (12px) at every size.
States
Four states. Default and Disabled share a thin 10%-black hairline border; Hover lifts the bg subtly to #F6F6F6 (light) / #2D2D2D (dark) and darkens the text; Active swaps the hairline for a solid #2D2D2D (light) / #FFFFFF (dark) rule. Hover is browser-native via :hover; .is-hover forces it at rest for the matrix. Use aria-selected="true" for Active (recommended) — falls back to .is-active for non-interactive demos.
Alignment
Two axes per Figma node 5934-1968. Horizontal (default — no modifier) lays the icon · label · badge in a single row at tab heights 48 / 40 / 32 px. Vertical (.tab-vertical) stacks the icon above a label-row at heights 64 / 52 / 48 px. Each size must live in its own .tabs row — the container uses align-items: stretch, so mixing sizes in one row would stretch the small tabs to the height of the large ones. Icons scale per size: horizontal 20 / 16 / 14 · vertical 24 / 20 / 16 (lg / md / sm).
48 / 64
40 / 52
32 / 48
Full matrix
3 sizes × 4 states = 12 cells, mirroring Figma 1:1.
Responsive
The .tabs wrapper already handles horizontal scroll on narrow screens — no extra class needed. On viewports narrower than 744 px, overflow-x: auto activates and the scrollbar is hidden via scrollbar-width: none. Use as many tabs as the content requires; the strip scrolls silently.
Frame above simulates a 390 px mobile viewport — the tab strip scrolls horizontally with no visible scrollbar.
<div class="tabs" role="tablist"> <button class="tab tab-md active" role="tab">Label</button> … </div>
Accessibility
Follows the WAI-ARIA Tabs pattern.
| Attribute / Behaviour | Value | Notes |
|---|---|---|
| Tab list role | role="tablist" | Set on the .tabs wrapper |
| Tab role | role="tab" | Set on each .tab button |
| Active tab | aria-selected="true" | Set on the active tab; aria-selected="false" on all others |
| Tab panel | role="tabpanel" | The content area controlled by each tab |
| Panel association | aria-controls="panel-id" on tab + aria-labelledby="tab-id" on panel | Links each tab to its controlled panel |
| Keyboard — switch | ← / → | Move focus and activate tabs within the tablist |
API
| Class / attr | On | Description |
|---|---|---|
| .tabs | <div role="tablist"> | The row container — flex with shared bottom alignment. Set role="tablist" and aria-label to describe the group. |
| .tab | <button role="tab"> | Single tab. Flex container with three slots (icon · label · badge). Default size 40px (md), Body 2 (14/24) Medium typography. Pair aria-controls with the corresponding tabpanel. |
| .tab-lg | .tab | Large — 48px tall, Body 1 (16/24) Medium typography. Use as a top-level page nav. |
| (no size class) | .tab | Medium (default) — 40px tall, Body 2 (14/24) Medium typography. The canonical density. |
| .tab-sm | .tab | Small — 32px tall, Caption 1 (12/16) Medium typography. Use in tight surfaces (e.g. inside a card or modal). |
| .tab-icon | <span> | Optional leading slot. Wrap an inline SVG; sized 16×16 in all tab sizes. |
| .badge.badge-label.badge-info | <span> inside .tab | The canonical count pill — composes with the standalone Badge component. Auto-scales per parent tab size via cascade: 12 px tall in a .tab-sm, 16 px (default) in a .tab, 20 px in a .tab-lg. Halo border flips from white (light) to #1C1C1C (dark) like every Badge variant. The legacy .tab-badge alias has been retired in v2.22.10. |
| .tab-vertical | .tab | Alignment modifier — stacks the icon above the label-row instead of beside it. Tab heights become 64 / 52 / 48 px (lg / md / sm). Wrap label + badge in a .tab-label-row child so they ride together under the icon. Active-state bottom-border still sits at the bottom of the tab. |
| .tab-label-row | <span> | Inner row used inside a .tab-vertical to keep the label text + optional .badge side-by-side under the icon. Has no effect on horizontal tabs. |
| aria-selected="true" | .tab | Marks the currently active tab. Drives the bottom-border swap from hairline to solid --tab-active-border. |
| .is-active | .tab | Fallback when aria-selected can't be used (rare). Renders identically. |
| disabled / .is-disabled | .tab / <button> | Disables the tab and dims the label. Prefer the native HTML disabled on the <button>. |
| .is-hover | .tab | Documentation helper — forces the hover visual at rest for the Full matrix. Not needed in production. |