Introduction

A single source of truth for design decisions, reusable patterns, and token-driven components.

This documentation site covers the foundational layers of the WV Design System: brand primitives, semantic aliases, mapped component tokens, and responsive typography and spacing. Every visual decision flows from a structured token architecture that supports light and dark modes across desktop, tablet, and mobile breakpoints.

Token Architecture

The system is built on four collections, each with a strict dependency chain:

  1. Brand/Value — Raw primitives (hex colors, spacing scale, font sizes). No semantic meaning.
  2. Alias — Semantic intent layer. Maps brand values to roles like Primary, Secondary, Neutral, Information, Success, Error.
  3. Mapped (light/dark) — The only layer components consume. Provides Surface, Text, Border, and Icon tokens for each theme mode.
  4. Responsive (Desktop/Mobile/Tablet) — Typography scales, jumper spacing, grid settings, and fixed padding per breakpoint.

Getting Started

Browse the sidebar to explore token definitions, component documentation, reusable patterns, and system-level principles. Every component page follows a consistent structure: overview, accessibility, construction specs, variants, usage guidelines, and code examples.

/* Reference a mapped token in CSS */
.card {
  background: var(--surface-default-container);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-m);
  padding: var(--padding-l);
  color: var(--text-body);
}

Color Tokens

Color is organized into brand primitives, semantic aliases, and mapped tokens for light and dark modes.

Primary (Orange)

50#fff1eb
100#ffe2d4
200#ffc7ae
300#ffa984
400#f78958
500#e96620
600#d14000
700#b11500
800#8b0000
900#620000
1000#3f0000

Secondary (Blue)

50#eef6ff
100#daebff
200#b1d4ff
300#84b8fd
400#599bf2
500#307edf
600#0063c7
700#004cac
800#00378e
900#00256d
1000#001347

Neutral (Gray)

0#ffffff
10#fdfdfd
25#fafafa
50#f5f5f5
100#e9e9e9
200#d0d0d0
300#b3b3b3
400#979797
500#7d7d7d
600#666666
700#555555
800#464646
900#393939
1000#2b2b2b
1100#181818

Status Colors

Information (Status Blue)

50#eff7f9
100#dcedf3
200#b6d9e4
300#8cc1d3
400#63a9bf
500#3a90a9
600#097892
700#006177
800#004f62

Success (Status Green)

50#f1f7f2
100#e0eee3
200#c1dcc8
300#a0c7a9
400#7fb28c
500#609c71
600#438558
700#2a6e42
800#155930

Error (Status Red)

50#fff1f1
100#ffe1e1
200#ffc7c7
300#ffa9ab
400#f88c90
500#ed6f76
600#de515e
700#ca3247
800#b10831

Mapped Surface Tokens

Global

Surface tokens for page backgrounds, layout containers, action states, and brand colors under Surface/Global/.

Light Mode
page#fafafa
default container#fdfdfd
bottom level container#d0d0d0
middle level container#e9e9e9
top level container#f5f5f5
action#e96620
action-hover#f78958
action-pressed#d14000
action-on#ffffff
disabled#f5f5f5
indicator bg#ffe2d4
brand primary#e96620
brand secondary#0063c7
brand tertiary#ffffff
Dark Mode
page#2b2b2b
default container#393939
bottom level container#464646
middle level container#555555
top level container#666666
action (50%)Primary/500 @ 50%
action-hover (50%)Primary/400 @ 50%
action-pressed (50%)Primary/600 @ 50%
action-on#393939
disabled#2b2b2b
indicator bg (50%)Primary/100 @ 50%
brand primary#e96620
brand secondary#307edf
brand tertiary#181818

Status

Surface tokens for status feedback under Surface/Status/.

Light Mode
information#eff7f9
information indicator bg#dcedf3
success#f1f7f2
success indicator bg#e0eee3
error#fff1f1
error indicator bg#ffe1e1
Dark Mode
information#00313e
information indicator bg (50%)Information/100 @ 50%
success#003617
success indicator bg (50%)Success/100 @ 50%
error#570013
error indicator bg (50%)Error/100 @ 50%

Button

Surface tokens for button backgrounds under Surface/Button/.

Light Mode
primary-bg#e96620
primary-bg-hover#f78958
primary-bg-pressed#d14000
primary-bg-disabled#f5f5f5
Dark Mode
primary-bg (50%)Primary/500 @ 50%
primary-bg-hover (50%)Primary/400 @ 50%
primary-bg-pressed (50%)Primary/600 @ 50%
primary-bg-disabled#2b2b2b

Secondary

Surface tokens for secondary-branded areas under Surface/Secondary/.

Light Mode
secondary surface#eef6ff
bottom#daebff
middle#84b8fd
top#307edf
Dark Mode
secondary surface#00256d
bottom#00378e
middle#004cac
top#307edf

Mapped Text Tokens

Default

Text color tokens under Text/Default/. Action tokens (previously in a separate Action sub-group) are now part of Default.

Light Mode
headings#393939
body#464646
captions#666666
placeholder#7d7d7d
primary nav#2b2b2b
primary footer#2b2b2b
action#e96620
action-hover#f78958
action-pressed#d14000
action-disabled#979797
action-on#ffffff
action-on-hover#fff1eb
action-on-pressed#ffc7ae
Dark Mode
headings#fafafa
body#d0d0d0
captions#b3b3b3
placeholder#666666
primary nav#ffffff
primary footer#ffffff
action#e96620
action-hover#f78958
action-pressed#d14000
action-disabled#7d7d7d
action-on#ffe2d4
action-on-hover#fff1eb
action-on-pressed#ffc7ae

Status

Text color tokens for status messaging under Text/Status/.

Light Mode
information#097892
information-hover#3a90a9
success#438558
success-hover#609c71
error#de515e
error-hover#ed6f76
Dark Mode
information#8cc1d3
information-hover#63a9bf
success#a0c7a9
success-hover#7fb28c
error#ffa9ab
error-hover#f88c90

Button

Text color tokens for buttons under Text/Button/.

Light Mode
primary#ffffff
primary-hover#fff1eb
primary-pressed#ffc7ae
primary-disabled#979797
primary-outline-pressed#d14000
Dark Mode
primary#ffe2d4
primary-hover#fff1eb
primary-pressed#ffc7ae
primary-disabled#7d7d7d
primary-outline-pressed#d14000

Mapped Border Tokens

Global

Border color tokens under Border/Global/.

Light Mode
default#e9e9e9
accent#d0d0d0
action#f78958
action-hover#ffa984
action-pressed#e96620
outline#e96620
disabled#e9e9e9
Dark Mode
default#555555
accent#464646
action (50%)Primary/500 @ 50%
action-hover (50%)Primary/400 @ 50%
action-pressed (50%)Primary/600 @ 50%
outline (50%)Primary/500 @ 50%
disabled#464646

Status

Border color tokens for status feedback under Border/Status/.

Light Mode
information#3a90a9
success#609c71
error#f88c90
Dark Mode
information#004050
success#054722
error (50%)Error/900 @ 50%

Button

Border color tokens for buttons under Border/Button/.

Light Mode
primary#f78958
primary-hover#ffa984
primary-pressed#e96620
primary-outline#e96620
primary-outline-hover#ffa984
primary-outline-pressed#b11500
Dark Mode
primary (50%)Primary/500 @ 50%
primary-hover (50%)Primary/400 @ 50%
primary-pressed (50%)Primary/600 @ 50%
primary-outline (50%)Primary/500 @ 50%
primary-outline-hover (50%)Primary/300 @ 50%
primary-outline-pressed (50%)Primary/600 @ 50%

Width

Border width tokens under Border/Width/. Same values for light and dark modes.

TokenReferenceValue
Width/action{Border Width.m}2px
Width/default{Border Width.s}1px

Mapped Icon Tokens

Default

Icon color tokens under Icon/Default/. Action tokens (previously in a separate Action sub-group) are now part of Default.

Light Mode
body#464646
captions#666666
placeholder#7d7d7d
primary nav#2b2b2b
primary footer#2b2b2b
action#e96620
action-hover#f78958
action-pressed#d14000
action-disabled#979797
action-on#ffffff
action-on-hover#fff1eb
action-on-pressed#ffc7ae
Dark Mode
body#e9e9e9
captions#b3b3b3
placeholder#666666
primary nav#ffffff
primary footer#ffffff
action#e96620
action-hover#f78958
action-pressed#d14000
action-disabled#7d7d7d
action-on#ffe2d4
action-on-hover#fff1eb
action-on-pressed#ffc7ae

Status

Icon color tokens for status feedback under Icon/Status/.

Light Mode
information#097892
information-hover#3a90a9
success#438558
success-hover#609c71
error#de515e
error-hover#ed6f76
Dark Mode
information#8cc1d3
information-hover#63a9bf
success#a0c7a9
success-hover#7fb28c
error#ffa9ab
error-hover#f88c90

Button

Icon color tokens for buttons under Icon/Button/.

Light Mode
primary#ffffff
primary-hover#fff1eb
primary-pressed#ffc7ae
primary-disabled#979797
primary-outline-pressed#d14000
Dark Mode
primary#ffe2d4
primary-hover#fff1eb
primary-pressed#ffc7ae
primary-disabled#7d7d7d
primary-outline-pressed#d14000

Brand

Brand icon color tokens under Icon/Brand/.

Light Mode
default#e96620
secondary default#0063c7
Dark Mode
default#e96620
secondary default#307edf

Shadow Colors (Brand Primitives)

Alpha-channel shadow color primitives derived from the gray scale. These are used by Mapped shadow tokens for light and dark mode elevation effects. Values pending final verification from Figma.

TokenBase GrayAlphaHex
shadow/0-a0Black0%#00000000
shadow/200-a10Gray 20010%#d0d0d01a
shadow/300-a10Gray 30010%#b3b3b31a
shadow/400-a10Gray 40010%#9797971a
shadow/1100-a10Gray 110010%#1818181a
shadow/1100-a50Gray 110050%#18181880

Mapped Shadow Tokens

Semantic shadow color tokens for elevation effects. Each token has a light and dark mode value referencing Brand shadow primitives.

TokenLight ModeDark ModeUsage
Shadow/Button{shadow.200-a10}{shadow.1100-a10}Button drop shadows
Shadow/L{shadow.1100-a50}{shadow.1100-a50}Large/prominent shadows
Shadow/M Default container{shadow.300-a10}{shadow.1100-a10}Default container shadows (cards, snackbars)

Spacing Tokens

A consistent spacing scale derived from the brand scale. Values follow a roughly doubling progression for the first 100, then increments of 8px.

Base Scale

scale-0
0px
scale-10
1px
scale-25
2px
scale-50
4px
scale-100
8px
scale-150
12px
scale-200
16px
scale-225
18px
scale-250
20px
scale-300
24px
scale-400
32px
scale-500
40px
scale-600
48px
scale-700
56px
scale-800
64px
scale-900
72px
scale-1000
80px
scale-1100
88px
scale-1200
96px
scale-1300
104px
scale-1400
112px
scale-2200
176px
scale-2400
192px
scale-4200
336px
scale-5600
448px
scale-9200
736px

Scale Reference

ApplicationVariable NameMultiplierValue (px)
Container width920092736
Container width560056448
Container width420042336
Scale240024192
Scale220022176
Scale140014112
Scale130013104
Scale12001296
Scale11001188
Scale10001080
Scale900972
Scale800864
Scale700756
Scale600648
Scale500540
Scale400432
Line height & Padding only3503.528
Scale300324
Font size only2502.520
Base font size only2252.2518
Scale200216
Font size (nav) only1751.7514
Font size (legal) & Padding only1501.512
Scale10018
Padding and Corner radius only50.54
Corner radius & Stroke only25.252
Corner radius & Stroke only10.101
Scale000

Jumper Tokens (Desktop / Tablet / Mobile)

Jumper tokens are responsive spacing values that shrink across breakpoints.

TokenDesktopTabletMobile
XL-L96px80px64px
L-M64px56px48px
M-S48px40px32px
S-XS32px32px24px
XS-XXS24px24px16px
XXS-XXXS16px16px8px
XXXS-Tiny8px8px4px
Tiny4px4px2px

Fixed Padding Tokens

Fixed padding tokens are static spacing values that remain constant across all breakpoints. They are used for internal component spacing such as gaps, padding, and margins.

TokenValueScale Reference
XXS2px{scale.25}
XS4px{scale.50}
S8px{scale.100}
M-S12px{scale.150}
M16px{scale.200}
L24px{scale.300}
XL32px{scale.400}

Grid Margins

Page margins are composed of a Global Margin (the outer gutter) and a Col Margin (the outermost column's margin). On a fixed layout, the total side margin is Global Margin + Col Margin. On a fluid layout, the margin is Global Margin only, allowing content to extend to the column edges.

TokenDesktopTabletMobile
Grid / Global Margin176px80px16px
Grid / Col Margin16px16px8px
Total fixed margin (per side)192px96px24px

Usage

.section {
  padding-top: var(--jumper-L-M);
  padding-bottom: var(--jumper-M-S);
  margin-left: var(--grid-global-margin);
  margin-right: var(--grid-global-margin);
  gap: var(--space-300);
}

Corner Radius (Mapped)

Component-specific corner radius tokens in the Mapped collection. These reference Alias Corner Radius values and are identical across light and dark modes. Card radius values pending Figma verification.

TokenReferenceResolved ValueUsage
Corner Radius/M button{Corner Radius.xxl}48pxButtons (pill shape)
Corner Radius/M field{Corner Radius.m}8pxInput fields
Corner Radius/S checkbox{Corner Radius.s}4pxCheckboxes
Corner Radius/S alerts{Corner Radius.s}4pxAlert containers
Corner Radius/XL round{scale.1300}104pxFully round elements
Corner Radius/Button{Corner Radius.xxl}48pxButton components
Corner Radius/Field{Corner Radius.m}8pxForm field components
Corner Radius/Alert{Corner Radius.s}4pxAlert component
Corner Radius/S card{Corner Radius.s}4pxSmall cards
Corner Radius/M card{Corner Radius.m}8pxMedium cards
Corner Radius/L card{Corner Radius.l}16pxLarge cards
Corner Radius/Round{scale.1300}104pxFully round

Typography Tokens

All type is set in Lato. Icons use Font Awesome 6 Free. Body M (16px) is the baseline reading size. Recommended max-width for body text is 60-75 characters per line for readability.

Overview

To maintain consistency with the grid and spacing increments, the same scaling increments are used for the type scale, with few exceptions, for ease of readability. Medium is the default or baseline size for any context.

Typographic Scale

GroupNameMultiplierSize (px)Line Height (px)
HeadingsHero86476
HeadingsH1 XXL75664
HeadingsH2 XL64856
HeadingsH3 L54048
HeadingsH4 M43240
HeadingsH5 S32428
HeadingsH6 XS2.52024
BodyBody XXL43244
BodyBody XL32440
BodyBody L2.52032
BodyBody L Subhead2.52032
BodyBody M21626
BodyBody S1.751420
BodyBody XS1.51216

Heading Scale (Desktop)

Hero Heading
64px / 76pxBlack (900)letter-spacing: -2%
H1 XXL Heading
56px / 64pxBlack (900)letter-spacing: -1%
H2 XL Heading
48px / 56pxBlack (900)letter-spacing: -0.5%
H3 L Heading
40px / 48pxBlack (900)letter-spacing: 0%
H4 M Heading
32px / 40pxBlack (900)letter-spacing: 0%
H5 S Heading
24px / 28pxBlack (900)letter-spacing: 0%
H6 XS Heading
20px / 24pxBold (700)letter-spacing: -0.5%

Body Scale (Desktop)

Body XXL text sample
32px / 44pxBlack (900)letter-spacing: -1.25%
Body XL text sample
24px / 40pxBlack (900)letter-spacing: 0%
Body L text sample
20px / 32pxRegular (400)letter-spacing: 0%
Body L Subhead text sample
20px / 32pxBold (700)letter-spacing: 0%
Body M text sample. The brown fox jumped over the tall fence. The cat came running after.
16px / 24pxRegular (400)letter-spacing: 1%
Body S text sample. Small paragraph text size for fine print (think legal, form hints/assistive text, captions).
14px / 20pxRegular (400)letter-spacing: 0%
Body XS / Caption text sample
12px / 14pxRegular (400)letter-spacing: 1%

Heading + Body Pairings (Mobile)

Each heading size pairs with a corresponding body size for visual hierarchy. Mobile heading sizes are smaller than their desktop counterparts.

H6 XS Heading
Body XS pairs with H6 XS. The brown fox jumped over the tall fence. The cat came running after.
H6 XS (16/20) + Body XS (12/16)
H5 S Heading
Body S pairs with H5 S. The brown fox jumped over the tall fence. The cat came running after.
H5 S (18/24) + Body S (14/18)
H4 M Heading
Body M pairs with H4 M. The brown fox jumped over the tall fence. The cat came running after.
H4 M (20/28) + Body M (16/26)
H3 L Heading
Body L pairs with H3 L. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H3 L (24/32) + Body L (20/32)
H3 L Heading
Body L Subhead pairs with H3 L. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H3 L (24/32) + Body L Subhead (20/32)
H2 XL Heading
Body XL pairs with H2 XL. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H2 XL (28/36) + Body XL (20/32)
H1 XXL Heading
Body XXL pairs with H1 XXL. The brown fox jumped over the tall fence. The cat came running after.
H1 XXL (32/40) + Body XXL (24/36)
Hero Heading
Body XXL pairs with Hero. The brown fox jumped over the tall fence. The cat came running after.
Hero (40/48) + Body XXL (24/32)

Heading + Body Pairings (Desktop)

Desktop heading sizes scale up from mobile. The paired body sizes also shift accordingly.

H6 XS Heading
Body S pairs with H6 XS on desktop. The brown fox jumped over the tall fence. The cat came running after.
H6 XS (20/24) + Body S (14/18)
H5 S Heading
Body M pairs with H5 S on desktop. The brown fox jumped over the tall fence. The cat came running after.
H5 S (24/28) + Body M (16/26)
H4 M Heading
Body M pairs with H4 M on desktop. The brown fox jumped over the tall fence. The cat came running after.
H4 M (32/40) + Body M (16/26)
H3 L Heading
Body L pairs with H3 L on desktop. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H3 L (40/48) + Body L (20/32)
H3 L Heading
Body L Subhead pairs with H3 L on desktop. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H3 L (40/48) + Body L Subhead (20/32)
H2 XL Heading
Body XL pairs with H2 XL on desktop. The brown fox jumped over the tall fence. The cat came running after. And then the whole farm began to join in the fun.
H2 XL (48/56) + Body XL (24/32)
H1 XXL Heading
Body XXL pairs with H1 XXL on desktop. The brown fox jumped over the tall fence. The cat came running after.
H1 XXL (56/64) + Body XXL (32/40)
Hero Heading
Body XXL pairs with Hero on desktop. The brown fox jumped over the tall fence. The cat came running after.
Hero (64/76) + Body XXL (32/40)

Text Link Scale

Text links inherit the body size at their corresponding scale but use a heavier weight.

XS Link text sample
12px / 16pxBold (700)letter-spacing: 1%
S Link text sample
14px / 20pxBold (700)letter-spacing: 0%
M Link text sample
16px / 24pxBold (700)letter-spacing: 1%

Responsive Comparison

StyleDesktop (size / line)Mobile (size / line)
Hero64 / 7640 / 48
H1 XXL56 / 6432 / 40
H2 XL48 / 5628 / 36
H3 L40 / 4824 / 32
H4 M32 / 4020 / 28
H5 S24 / 2818 / 24
H6 XS20 / 2416 / 20
Body XXL32 / 4424 / 36
Body XL24 / 4020 / 32
Body L20 / 3220 / 32
Body L Subhead20 / 3220 / 32
Body M16 / 2616 / 26
Body S14 / 2014 / 20
Body XS12 / 1612 / 16

Font Weight Reference

Font weights are stored as numeric values in both the Brand primitives and Responsive collection. Composite typography definitions reference these via Responsive variable paths.

Weight NameNumeric ValueUsed In
Regular400Body L, Body M, Body S, Body XS, Notification Alert Body, Notification Snackbar Body
Bold700H6 XS, Body L Subhead, XS Link, S Link, M Link, Notification Alert Heading, Notification Snackbar Heading
Black900Hero, H1-H5, Body XXL, Body XL
Solid900FA6 icons (S icons, M icons, L icons, Notification Snackbar Icon)

Letter Spacing Reference

ValueApplied To
-2%Hero
-1.25%Body XXL
-1%H1 XXL
-0.5%H2 XL, H6 XS
0%H3 L, H4 M, H5 S, Body XL, Body L, Body S
1%Body M, Body XS, XS Link, M Link

Type Scale: Light vs Dark

The type scale uses the same sizes and weights in both modes. Only the text and background colors change between light and dark themes.

Body Copy Comparison

Light Mode
Body XS. The brown fox jumped over the tall fence. The cat came running after.
Body S. The brown fox jumped over the tall fence. The cat came running after.
Body M. The brown fox jumped over the tall fence. The cat came running after.
Body L. The brown fox jumped over the tall fence. The cat came running after.
Body XL. The brown fox jumped over the tall fence. The cat came running after.
Body XXL. The brown fox jumped over the tall fence.
Dark Mode
Body XS. The brown fox jumped over the tall fence. The cat came running after.
Body S. The brown fox jumped over the tall fence. The cat came running after.
Body M. The brown fox jumped over the tall fence. The cat came running after.
Body L. The brown fox jumped over the tall fence. The cat came running after.
Body XL. The brown fox jumped over the tall fence. The cat came running after.
Body XXL. The brown fox jumped over the tall fence.

Heading Scale Comparison

Light Mode
H6 XS Heading
The brown fox jumped over the tall fence. The cat came running after.
H5 S Heading
The brown fox jumped over the tall fence. The cat came running after.
H4 M Heading
The brown fox jumped over the tall fence. The cat came running after.
H3 L Heading
The brown fox jumped over the tall fence. The cat came running after.
H2 XL Heading
The brown fox jumped over the tall fence.
H1 XXL
The brown fox jumped over the tall fence.
Hero
The brown fox jumped over the tall fence.
Dark Mode
H6 XS Heading
The brown fox jumped over the tall fence. The cat came running after.
H5 S Heading
The brown fox jumped over the tall fence. The cat came running after.
H4 M Heading
The brown fox jumped over the tall fence. The cat came running after.
H3 L Heading
The brown fox jumped over the tall fence. The cat came running after.
H2 XL Heading
The brown fox jumped over the tall fence.
H1 XXL
The brown fox jumped over the tall fence.
Hero
The brown fox jumped over the tall fence.

Notification Typography

Component-specific typography tokens for Alert and Snackbar notification components. These values are consistent across Desktop, Mobile, and Tablet breakpoints.

Alert

ElementFont SizeLine HeightFont Weight
Heading14px20px700 (Bold)
Body14px18px400 (Regular)
Icon16px20px-

Snackbar

ElementFont SizeLine HeightFont Weight
Heading16px26px700 (Bold)
Body16px26px400 (Regular)
Icon16px26px900 (Solid)

Notification Width

TokenValueNotes
Notification/Width/modal736pxMax-width for snackbar (matches --card-xl)
Notification/Width/embeddedTBDNeeds verification from Figma. Alerts are inline and fill container width.

Shadow Blur Sizes (Responsive)

Blur radius values for shadow effects, defined in the Responsive collection. Values are consistent across breakpoints. Pending final verification from Figma.

TokenValue (px)Scope
Shadow/XS2EFFECT_FLOAT
Shadow/S4EFFECT_FLOAT
Shadow/M8EFFECT_FLOAT
Shadow/L16EFFECT_FLOAT
Shadow/default4EFFECT_FLOAT

Alert

Informs users of a process outcome or system event. Compact, inline feedback for form validation results, status changes, and contextual messages.

Overview

The Alert is a compact, inline notification surface used to communicate outcomes or events. For example, if a user submits a form, the alert provides feedback about an error that occurred or confirms the form was completed successfully. Four status variants (Default, Success, Error, Information) control the color of all elements within the alert. The component contains an optional status icon, an optional bold heading, body copy, an optional text link (reusing the Text Link S component), and an optional close button. All optional elements can be included or omitted independently.

Variants

Default

Neutral container with default border. Icon and heading use the headings color, body and close use the body color. Suitable for general feedback or neutral messages.

All options
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link
No link
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
No icon
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link
No heading
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link

Success

Green background and border. All text, icons, and the close button use the success color. Use for positive confirmations such as saves, completions, or sends.

All options
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link

Error

Red background and border. All text, icons, and the close button use the error color. Use for failures or issues that need user attention.

All options
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link

Information

Teal background and border. All text, icons, and the close button use the information color. Use for informational notices or system-level messages.

All options
Alert Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text blocks.
Link

Accessibility Considerations

WCAG 2.1 AA target. Alerts are inline, persistent elements and should not auto-dismiss. They must be perceivable by assistive technology.

Keyboard Interactions

KeyAction
TabMoves focus to the link or close button inside the alert
Enter / SpaceActivates the focused close button or link

ARIA and Semantics

  • Use role="status" for non-critical updates (default, success, information) so screen readers announce content politely.
  • Use role="alert" for error alerts to assert immediate attention.
  • Include aria-live="polite" (or "assertive" for errors) so content is announced when it appears dynamically.
  • The close button must have aria-label="Dismiss" since it contains only an icon.
  • If the alert appears after a user action (form submit, etc.), focus management should move the user near the alert.

Construction

The alert is a compact single-row layout. The text link reuses the Text Link S component with status type classes for color matching.

Scale and Spacing

PropertyValueToken
Container padding12px--padding-s-m
Container border radius4px--radius-s
Container border width1pxFixed
Main gap (icon-text to close)8px--padding-s
Icon-to-text gap8px--padding-s
Text internal gap (heading / body / link)2px--padding-xxs
Icon container width14pxFixed
Close button width16pxFixed

Styles

ElementFont / Size
HeadingLato Bold (700), 16px/26px, -0.08px tracking
BodyLato Regular (400), 14px/20px
Link textLato Bold (700), 14px/20px (Text Link S)
Link chevronFA6 Solid (900), 14px/20px (Text Link S icon)
Status iconFA6 Solid (900), 16px/26px, 0.4px tracking
Close iconFA6 Solid (900), 16px/26px, 0.4px tracking

Color Tokens

ElementDefaultSuccessErrorInformation
Container bg--surface-default-container--surface-success--surface-error--surface-information
Container border--border-default--border-success--border-error--border-information
Icon--text-headings--text-status-success--text-status-error--text-status-information
Heading--text-headings--text-status-success--text-status-error--text-status-information
Body text--text-body--text-status-success--text-status-error--text-status-information
Link text + chevron--text-body--text-status-success--text-status-error--text-status-information
Close icon--text-body--text-status-success--text-status-error--text-status-information

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.alertContainerDefault statusRoot element. 12px padded, 4px rounded, bordered.
.alert-successModifierApplies success colors to all elements (bg, border, text, icons).
.alert-errorModifierApplies error colors to all elements.
.alert-informationModifierApplies information colors to all elements.
.alert-icon-textLayoutFlex row for icon and text block. Fills available space.
.alert-iconElementOptional. 14px-wide icon container. Omit to hide.
.alert-textLayoutFlex column for heading, body, and link. 2px gap.
.alert-headingElementOptional. Bold title line.
.alert-bodyElementBody copy in Body S (14px).
.text-link.text-link-sSub-componentOptional. Reuses the Text Link S component. Add .type-success, .type-error, or .type-information to match the alert status.
.alert-closeElementOptional. Dismiss button. Requires aria-label="Dismiss".

For Designers

Do

Use alerts for inline, persistent feedback tied to a specific action or context, such as form validation or account status.

Don't

Don't use alerts for transient notifications. Use the Snackbar component for brief, non-blocking feedback instead.

Do

Match the status variant to the message type so color communicates meaning at a glance.

Don't

Don't use the Error variant for informational messages. Reserve red for actual failures or issues requiring action.

Spacing Guidance

ContextPropertyValueToken
Container internalpadding12px--padding-s-m
Icon-text to closegap8px--padding-s
Icon to textgap8px--padding-s
Text block internalgap2px--padding-xxs
Link text to chevrongap8px--padding-s
Alert to surrounding contentmargin16-24px--padding-m / --padding-l

Usage Examples

Default with All Options

<div class="alert" role="status" aria-live="polite">
  <div class="alert-icon-text">
    <div class="alert-icon">
      <i class="fa-solid fa-circle-info"></i>
    </div>
    <div class="alert-text">
      <div class="alert-heading">Changes saved</div>
      <div class="alert-body">Your profile has been updated.</div>
      <a class="text-link text-link-s" href="#">
        <span class="link-label">Link</span>
        <i class="link-icon fa-solid fa-chevron-right"></i>
      </a>
    </div>
  </div>
  <button class="alert-close" aria-label="Dismiss">
    <i class="fa-solid fa-xmark"></i>
  </button>
</div>

Error Alert

<div class="alert alert-error" role="alert" aria-live="assertive">
  <div class="alert-icon-text">
    <div class="alert-icon">
      <i class="fa-solid fa-circle-exclamation"></i>
    </div>
    <div class="alert-text">
      <div class="alert-heading">Submission failed</div>
      <div class="alert-body">Please correct the errors and try again.</div>
      <a class="text-link text-link-s type-error" href="#">
        <span class="link-label">Link</span>
        <i class="link-icon fa-solid fa-chevron-right"></i>
      </a>
    </div>
  </div>
  <button class="alert-close" aria-label="Dismiss">
    <i class="fa-solid fa-xmark"></i>
  </button>
</div>

Minimal (No Icon, No Link, No Close)

<div class="alert alert-success" role="status" aria-live="polite">
  <div class="alert-icon-text">
    <div class="alert-text">
      <div class="alert-body">Registration complete.</div>
    </div>
  </div>
</div>

Breadcrumbs

A horizontal navigation pattern that shows the user's current location within a site hierarchy. Each crumb is a clickable link except the last item, which represents the current page.

Overview

Breadcrumbs help users understand where they are in a multi-level navigation structure and provide quick access to parent pages. The component uses Small Link (S) typography (Lato Bold, 14px/18px) with --text-body as the default color. On hover, all elements within a crumb (text, icons, separator) change to --text-action-hover. A chevron-right icon (fa-chevron-right) separates each crumb. The first crumb includes a home icon (fa-house). The last crumb (current page) displays a persistent underline and has no trailing separator.

Variants

Assembled Breadcrumb

Full breadcrumb trail with home icon, intermediate links, and current page.

First Item

Includes a home icon (left), label text, and a chevron-right separator (right).

Default Link
Hover Link
Focus Link
Disabled Link

Middle Item

Label text with a chevron-right separator. No home icon.

Default Link
Hover Link
Focus Link
Disabled Link

Last Item (Current Page)

Label text only, with a persistent underline. No trailing chevron separator.

Default Link
Hover Link
Focus Link
Disabled Link

Accessibility Considerations

WCAG 2.1 AA target. Breadcrumbs expose hierarchy and current location; focus order follows the visual trail left to right.

Keyboard Interactions

KeyAction
Tab / Shift+TabMoves focus between crumb links
EnterActivates the focused link (parent pages only)

ARIA and Semantics

  • Wrap the trail in <nav aria-label="Breadcrumb"> (or another concise label).
  • Add aria-current="page" to the last (current) item.
  • Home icon and chevron separators use aria-hidden="true".
  • Disabled crumbs use <span> instead of <a> so they are not in the tab order.
  • Focus indicator: 2px solid --border-action, 6px outline offset from the crumb, 10px radius (--radius-focus-ring), matching the Small Link focus treatment in Figma.

Construction

Typography

RoleFontWeightSize / Line HeightToken
Crumb labelLatoBold (700)14px / 18pxBody/Text Links/S (--body-s-size, --body-s-line)
Icons (home, chevron)FA6 Solid90014px / 18pxBody/Icons/S icon

Color Tokens

ElementStateToken
Text + icons (non-current)Default--text-body
Text + icons (non-current)Hover--text-action-hover
Current page labelDefault--text-body + underline (.link-label)
Current page labelHover--text-action-hover + underline
Disabled backgroundDisabled--border-default
Disabled text + iconsDisabled--text-body

Focus Ring

PropertyValueToken
Border width2px--border-width-m-action
Border color--border-action#F78958
Border radius10px--radius-focus-ring
Offset6pxMatches Figma Small Link / breadcrumb crumb focus inset

Spacing

PropertyValueToken
Gap between crumbs8px--padding-s
Internal gap (icon, label, chevron)8px--padding-s
Disabled horizontal padding2pxFixed
Disabled border-radius4px--radius-s

Application and Context

For Developers

Attribute / ClassTypeDescription
.breadcrumbContainerApplied to <nav>. Flex row, 8px gap, centered items.
.breadcrumb-itemCrumbApplied to <a> (or <span> when disabled). Inline-flex, 8px internal gap, body color by default.
.breadcrumb-iconElementFont Awesome icon inside a crumb (home, chevron).
.currentModifierLast item (current page). Adds persistent underline to .link-label.
.link-labelElementRequired on current item. Wrap the current page text for underline styling.
.is-disabledStateDisabled crumb on <span>; gray background, no pointer events.
aria-current="page"AttributeOn the current page crumb.
aria-hidden="true"AttributeOn decorative <i> icons.

For Designers

Do

Always start the breadcrumb trail with the home icon on the first item to provide a consistent anchor point.

Don't

Don't omit the home icon from the first breadcrumb item. Users rely on it as a visual indicator of the navigation root.

Do

Mark the last item as the current page with a persistent underline to clearly indicate the user's location.

Don't

Don't make the current page item look identical to the other crumbs. The underline distinguishes it as non-navigable context.

Do

Keep breadcrumb labels short and descriptive. Use page titles or category names, not full sentences.

Don't

Don't use breadcrumbs for flat navigation structures. They are intended for hierarchical, multi-level page structures only.

Usage Examples

Standard Breadcrumb

<nav class="breadcrumb" aria-label="Breadcrumb">
  <a class="breadcrumb-item" href="/home">
    <i class="breadcrumb-icon fa-solid fa-house" aria-hidden="true"></i>
    <span>Home</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </a>
  <a class="breadcrumb-item" href="/components">
    <span>Components</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </a>
  <a class="breadcrumb-item current" href="/components/button" aria-current="page">
    <span class="link-label">Button</span>
  </a>
</nav>

Breadcrumb with Disabled Item

<nav class="breadcrumb" aria-label="Breadcrumb">
  <a class="breadcrumb-item" href="/home">
    <i class="breadcrumb-icon fa-solid fa-house" aria-hidden="true"></i>
    <span>Home</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </a>
  <span class="breadcrumb-item is-disabled">
    <span>Archived</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </span>
  <a class="breadcrumb-item current" href="/archived/report" aria-current="page">
    <span class="link-label">Report</span>
  </a>
</nav>

Button

Triggers an action or event. The primary mechanism for user-initiated operations across dialogs, forms, cards, and toolbars.

Overview

Buttons communicate actions users can take. They use the primary (orange) action color to establish visual hierarchy. Two variants exist: Primary (filled) for the most important action, and Outline (bordered) for secondary actions. Four sizes (S, M, L, XL) allow density control across layouts. Each button supports optional leading and trailing icons via Font Awesome 6.

Variants

Primary (Filled)

Solid background with a lighter border. White text and icons on the default state. Used for the highest-priority action in a view.

Default
Hover
Focus
Pressed
Disabled

Outline (Bordered)

Transparent background with a colored border. Text and icons use the action color. Used for secondary actions or when paired alongside a Primary button.

Default
Hover
Focus
Pressed
Disabled

Sizes

Four density levels control vertical padding and font size.

Small
Medium
Large
XLarge

Accessibility Considerations

WCAG 2.1 AA target. Uses the native <button> element with implicit role="button".

Keyboard Interactions

KeyAction
EnterActivates the button
SpaceActivates the button
TabMoves focus to next focusable element
Shift+TabMoves focus to previous focusable element

Focus Management

  • Focus ring: 2px solid border, offset 6px outward from the button edge, matching the button's pill radius (48px).
  • Primary focus ring uses Border/Button/Primary/border (#f78958).
  • Outline focus ring uses Border/Button/Primary/outline (#e96620).
  • Focus state does not change background or text color from default.

Disabled State

  • Uses aria-disabled="true" to remain in tab order for discoverability.
  • Text and icon color: Text/Action/action-disabled (#979797). Reduced contrast but still legible against both filled (#f5f5f5) and page (#fafafa) backgrounds.

Construction

Scale and Spacing

PropertySMLXLToken
Padding top/bottom8px8px12px16pxFixed/padding/S, S, M-S, M
Padding left/right16px24px32px32pxFixed/padding/M, L, XL, XL
Internal gap8px8px8px8pxFixed/padding/S

Styles

PropertyValueToken
Border radius48px (pill)Corner Radius/M button
Border width2pxBorder/Width/M action
Font (S)Lato Regular 14px/18pxBody/Body S
Font (M, L)Lato Regular 16px/26pxBody/Body M
Font (XL)Lato Regular 20px/32pxBody/Body L
Icon font (S)FA6 Solid 14px/18pxBody/Icons/S icon
Icon font (M, L)FA6 Solid 16px/26pxBody/Icons/M icon
Icon font (XL)FA6 Solid 20px/32pxBody/Icons/L icon
Icon width18pxFixed

Color Tokens: Primary Variant

StateBackgroundBorderTextIcons
DefaultSurface/Button/Primary/bg
#e96620
Border/Button/Primary/border
#f78958
Text/Button/Primary/text
#ffffff
Icons/Button/Primary/icon
#ffffff
HoverSurface/Button/Primary/bg-hover
#f78958
Border/Button/Primary/action-hover
#ffa984
Text/Button/Primary/text-hover
#fff1eb
Icons/Button/Primary/icon-hover
#fff1eb
Focus(same as default)(same as default)(same as default)(same as default)
PressedSurface/Button/Primary/bg-pressed
#d14000
Border/Button/Primary/action-pressed
#e96620
Text/Button/Primary/text-pressed
#ffc7ae
Icons/Button/Primary/icon-pressed
#ffc7ae
DisabledSurface/Button/Primary/bg-disabled
#f5f5f5
Border/Action/disabled
#e9e9e9
Text/Button/Primary/text-disabled
#979797
Icons/Button/Primary/icon-disabled
#979797

Color Tokens: Outline Variant

StateBackgroundBorderTextIcons
DefaultSurface/Global/page
#fafafa
Border/Button/Primary/outline
#e96620
Text/Action/action
#e96620
Icons/Action/action
#e96620
Hover(same as default)Border/Button/Primary/outline border-hover
#ffa984
Text/Action/action-hover
#f78958
Icons/Action/action-hover
#f78958
Focus(same as default)(same as default)(same as default)(same as default)
Pressed(same as default)Border/Button/Primary/outline border-pressed
#b11500
Text/Button/Primary/outline text-pressed
#d14000
Icons/Button/Primary/outline icon-pressed
#d14000
Disabled(same as default)Border/Action/disabled
#e9e9e9
Text/Action/action-disabled
#979797
Icons/Action/action-disabled
#979797

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.btn-primary / .btn-outlineVariant class.btn-primaryVisual variant: filled or bordered.
.btn-s / .btn-m / .btn-l / .btn-xlSize class.btn-mDensity: S (8px/16px pad, Body S), M (8px/24px, Body M), L (12px/24px, Body M), XL (16px/24px, Body L).
.btn-iconChild elementWrap icon characters (Font Awesome unicode) in a span with this class. Optional leading and/or trailing position.
disabled / .is-disabledStateNative disabled attribute or .is-disabled class for non-button elements. Use aria-disabled="true" when keeping in tab order.

For Designers

Do

Use Primary for the single most important action in a view. Pair with Outline for secondary actions.

Don't

Don't place multiple Primary buttons in the same context. Hierarchy is lost when everything is emphasized.

Do

Use XL for hero sections only. Use L sparingly for prominent CTAs below a hero. Use M as the standard across the board. Use S for tight spaces or secondary buttons when absolutely necessary.

Don't

Don't mix button sizes within the same action group. Consistent density reinforces visual rhythm.

Spacing Guidance

ContextTokenValue
Gap between button and adjacent buttonFixed/padding/S8px
Gap between icon and label (internal)Fixed/padding/S8px
Button group to form field aboveJumper/S-XS32px (desktop) / 16px (mobile)

Usage Examples

Basic

<button class="btn btn-primary btn-m" type="button">Submit</button>
<button class="btn btn-outline btn-m" type="button">Cancel</button>

With Icons

<button class="btn btn-primary btn-m" type="button">
  <span class="btn-icon">&#xf05a;</span>
  Button
  <span class="btn-icon">&#xf05a;</span>
</button>

All Sizes

<button class="btn btn-primary btn-s" type="button">Small</button>
<button class="btn btn-primary btn-m" type="button">Medium</button>
<button class="btn btn-primary btn-l" type="button">Large</button>
<button class="btn btn-primary btn-xl" type="button">XLarge</button>

Disabled

<button class="btn btn-primary btn-m" disabled aria-disabled="true">
  <span class="btn-icon">&#xf05a;</span>
  Unavailable
</button>

<button class="btn btn-outline btn-m" disabled aria-disabled="true">
  <span class="btn-icon">&#xf05a;</span>
  Unavailable
</button>

Button Group

A segmented control that groups related actions into a single horizontal strip, allowing users to choose between options or toggle views.

Overview

Button groups combine two or more related actions into a connected row of segments. Each segment behaves like an individual button but shares a container border and divider treatment that communicates they belong together. Two types exist: Labeled (icon + text) and Icon-only (compact, icon without text). Items support four states: Default, Hover, Selected, and Disabled. The component uses role="group" with an aria-label describing the group purpose.

Variants

Labeled (Icon + Text)

Each segment contains a leading icon and a text label. The full-width variant used when labels are needed to clarify the action.

Default
Hover
Selected
Disabled
Mixed

Icon-only

Compact variant where each segment contains only an icon. Use when space is tight and the icons are universally understood. Every icon-only item must have an aria-label.

Default
Hover
Selected
Disabled

Accessibility Considerations

WCAG 2.1 AA target. The group container communicates that the buttons are related.

Keyboard Interactions

KeyAction
TabMoves focus into/out of the group. Each item is a separate tab stop.
Enter / SpaceActivates the focused item (selects it).

ARIA Requirements

  • The container must have role="group" and an aria-label describing the group purpose.
  • The selected item should have aria-pressed="true" (toggle behavior) or be managed via aria-current for navigation.
  • Icon-only items must include aria-label on each button.
  • Disabled items use aria-disabled="true" to remain discoverable.

Focus Management

  • Focus indicator: 2px solid outline in --surface-action, inset 2px. Contained within the group border.
  • Focus does not change background or text color from the current state.

Construction

Scale and Spacing

PropertyLabeledIcon-onlyToken
Padding (vertical)8px12pxFixed/padding/S / M-S
Padding (horizontal)16px12pxFixed/padding/M / M-S
Gap (icon to label)8pxn/aFixed/padding/S
Divider width2pxBorder/Width/M action
Container border1pxBorder/Width/M default
Container radius4pxCorner Radius/S

Styles

PropertyValueToken
Font familyLato Regularfont
Font size16pxBody M/font size
Line height24pxBody M/line height
Letter spacing0.16px (0.01em)Body M
Icon fontFA6 Free Solid, 16pxBody M/M icons/font
Icon width16pxFixed

Color Tokens

StateBackgroundBorder / DividerText / Icon
Default Surface/Global/page
#fafafa
Border/Global/default
#e9e9e9
Text/Default/body
#464646
Hover Surface/Action/action-hover
#f78958
Border/Action/action-hover
#ffa984
Text/Action/action-on-hover
#fff1eb
Selected Surface/Action/action
#e96620
Border/Action/action
#f78958
Text/Action/action-on
#ffffff
Disabled Surface/Action/disabled
#f5f5f5
Border/Action/disabled
#e9e9e9
Text/Action/action-disabled
#979797

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.btn-groupContainerWraps all items. Provides outer border, radius, and overflow clipping.
.btn-group-itemChild elementEach segment inside the group. Use <button> elements.
.btn-group-icon-onlyModifierApplied to .btn-group-item for icon-only segments. Sets uniform padding.
.selectedState classMarks the currently active/selected item.
role="group"ARIA roleRequired on the container to communicate the grouping relationship.
aria-labelAttributeRequired on the container. Also required on each icon-only item.
aria-pressedAttribute"false"Set to "true" on the selected item when used as a toggle group.

For Designers

Do

Use button groups for related actions that benefit from visual proximity, such as view toggles (list/grid), alignment options, or filter sets.

Don't

Don't use a button group for unrelated actions. If the actions serve different purposes, use individual buttons instead.

Do

Use the labeled variant when the action needs explanation. Use icon-only when space is tight and icons are universally recognized.

Don't

Don't mix labeled and icon-only items within the same group. Keep all segments consistent.

Spacing Guidance

ContextSpacingToken
Between button group and adjacent content24px--space-300
Between stacked button groups16px--space-200
Button group within a toolbar16px gap--space-200

Usage Examples

Labeled Group

<div class="btn-group" role="group" aria-label="View options">
  <button class="btn-group-item" type="button">
    <i class="btn-group-icon fa-solid fa-list"></i> List
  </button>
  <button class="btn-group-item selected" type="button"
          aria-pressed="true">
    <i class="btn-group-icon fa-solid fa-grip"></i> Grid
  </button>
  <button class="btn-group-item" type="button">
    <i class="btn-group-icon fa-solid fa-table"></i> Table
  </button>
</div>

Icon-only Group

<div class="btn-group" role="group" aria-label="Actions">
  <button class="btn-group-item btn-group-icon-only"
          type="button" aria-label="Mail">
    <i class="btn-group-icon fa-solid fa-envelope"></i>
  </button>
  <button class="btn-group-item btn-group-icon-only"
          type="button" aria-label="Info">
    <i class="btn-group-icon fa-solid fa-circle-info"></i>
  </button>
  <button class="btn-group-item btn-group-icon-only"
          type="button" aria-label="Favorite">
    <i class="btn-group-icon fa-solid fa-heart"></i>
  </button>
</div>

With Selected and Disabled Items

<div class="btn-group" role="group" aria-label="Alignment">
  <button class="btn-group-item selected" type="button"
          aria-pressed="true">
    <i class="btn-group-icon fa-solid fa-align-left"></i> Left
  </button>
  <button class="btn-group-item" type="button">
    <i class="btn-group-icon fa-solid fa-align-center"></i> Center
  </button>
  <button class="btn-group-item" type="button"
          disabled aria-disabled="true">
    <i class="btn-group-icon fa-solid fa-align-right"></i> Right
  </button>
</div>

Checkbox

A form control that allows users to select one or more options from a set. Pairs a 24x24 indicator box with an optional text label.

Overview

The checkbox is a binary selection control. When checked, it displays a Font Awesome check icon inside an action-colored fill. When unchecked, it renders as a bordered empty square. The component supports five states (default, hover, focus, disabled, error) across both selected and unselected types. An optional label sits to the right with an 8px gap.

Variants

Unselected

Empty checkbox with action-colored border and page-colored background. Shows the available selection target.

Default
Hover
Focus
Disabled
Error

Selected

Filled checkbox with a white check icon. The action-colored background confirms the selection.

Default
Hover
Focus
Disabled
Error

With Label

Checkbox paired with a Body M label (Lato Regular, 16px/26px) at an 8px gap.

Unchecked
Checked
Disabled
Error

Accessibility Considerations

WCAG 2.1 AA target. Checkboxes must be operable by keyboard and clearly communicate their checked/unchecked state to assistive technology.

Keyboard Interactions

KeyAction
SpaceToggles the checkbox between checked and unchecked
TabMoves focus to the next focusable element
Shift+TabMoves focus to the previous focusable element

ARIA and Semantics

  • Use native <input type="checkbox"> inside a <label> for automatic association. This provides the checked/unchecked state to screen readers without extra ARIA.
  • When the label wraps the input, no for/id pairing is needed.
  • For checkbox groups, wrap in a <fieldset> with a <legend> describing the group.
  • Error state should be paired with aria-describedby pointing to an error message element and aria-invalid="true" on the input.
  • Disabled checkboxes use the native disabled attribute, which removes them from tab order.
  • Focus ring: 2px solid --border-action, 3px offset, 4px border-radius.

Construction

Scale and Spacing

PropertyValueToken
Checkbox size24 x 24px--space-300
Border width1px--space-10
Border radius4px--radius-s
Icon size16px--body-m-size
Checkbox-to-label gap8px--space-100
Focus ring offset3pxn/a
Focus ring width2pxn/a

Styles

PropertyValue
Icon fontFont Awesome 6 Free (Solid, weight 900)
Icon glyph\f00c (check)
Icon letter-spacing0.45px
Label fontLato Regular (400), 16px/26px
Label color--text-body (#464646)

Color Tokens

StateBackgroundBorderIcon
Default (unchecked)--surface-default-container--border-actionn/a
Default (checked)--surface-action--border-action--text-action-on (white)
Hover--surface-action-hover--border-action-hover--text-action-hover
Focus(same as default)(same as default)(same as default)
Disabled--surface-disabled--border-disabled--text-action-disabled
Error--surface-error--border-error--text-status-error

Application and Context

For Developers

Attribute / ClassTypeDescription
.form-checkboxContainerWrapping <label>. Flex row with 8px gap.
input[type="checkbox"]ElementNative checkbox with appearance: none for custom styling.
checkedAttributeNative checked state. Fills background and shows icon via ::after.
disabledAttributeNative disabled. Grays out and removes from tab order.
.has-errorModifierAdded to the <input>. Applies error surface/border colors.
.is-disabledModifierVisual-only disabled state for documentation previews.
.show-hoverModifierForces hover appearance for documentation previews.
.show-focusModifierForces focus ring for documentation previews.

For Designers

Do

Use checkboxes when users can select zero, one, or multiple options from a list.

Don't

Don't use a checkbox for a binary on/off setting where a toggle switch communicates instant effect.

Do

Always provide a visible label. If space prevents it, use aria-label on the input.

Don't

Don't use a checkbox as a single required acknowledgment without clear context (e.g., "I agree to..." needs the full statement).

Spacing Guidance

ContextPropertyValueToken
Checkbox to labelgap8px--space-100
Between stacked checkboxesgap / margin-bottom8-16px--space-100 to --space-200
Checkbox group to next fieldmargin-bottom24px--space-300

Usage Examples

Single Checkbox with Label

<label class="form-checkbox">
  <input type="checkbox">
  I agree to the Terms of Service
</label>

Checkbox Group

<fieldset>
  <legend>Select your interests</legend>
  <label class="form-checkbox">
    <input type="checkbox" name="interests" value="design">
    Design
  </label>
  <label class="form-checkbox">
    <input type="checkbox" name="interests" value="dev">
    Development
  </label>
  <label class="form-checkbox">
    <input type="checkbox" name="interests" value="research">
    Research
  </label>
</fieldset>

Error State with Message

<label class="form-checkbox">
  <input type="checkbox" class="has-error"
         aria-invalid="true"
         aria-describedby="cb-err">
  Accept privacy policy
</label>
<span id="cb-err" class="input-hint hint-error">
  You must accept to continue.
</span>

Disabled Checkbox

<label class="form-checkbox">
  <input type="checkbox" disabled>
  This option is unavailable
</label>

Icon Button

A compact button containing only an icon, used for actions where the icon alone communicates intent.

Overview

Icon buttons are a density-optimized variant of the standard button. They remove the text label and use uniform padding on all sides so the button renders as a circle. Two variants exist: Primary (filled) and Outline (bordered). Three sizes (S, M, L) match the standard button size scale. Icons are rendered via Font Awesome 6 Free (Solid weight). Because there is no visible label, every Icon Button must have an aria-label.

Variants

Primary (Filled)

Solid orange background with a lighter border. White icon. Same color token set as the standard Primary button.

Default
Hover
Focus
Pressed
Disabled

Outline (Bordered)

Page-color background with an orange border. Icon uses the action color. Same color token set as the standard Outline button.

Default
Hover
Focus
Pressed
Disabled

Accessibility Considerations

WCAG 2.1 AA target. Because there is no visible text label, an accessible name is required.

Keyboard Interactions

KeyAction
EnterActivates the button
SpaceActivates the button
TabMoves focus to next focusable element

ARIA Requirements

  • Every icon button must include aria-label describing the action (e.g., aria-label="More information").
  • If the icon button toggles state, use aria-pressed to communicate the current value.
  • Focus ring: same 2px solid, 6px outward offset, pill radius as the standard button.

Construction

Scale and Spacing

PropertySMLToken
Padding (all sides)8px12px16pxFixed/padding/S, M-S, M

Styles

StateColorToken
Border radius48px (pill, renders as circle)Corner Radius/M button
Border width2pxBorder/Width/M action
Icon font (S, M)FA6 Solid 16px/26pxBody/Icons/M icon
Icon font (L)FA6 Solid 20px/32pxBody/Icons/L icon
Icon width16pxFixed

Color Tokens

Identical to the standard Button component. See the Button color token tables for the full Primary and Outline state mappings.

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.btn-icon-onlyModifier classAdded alongside .btn and a variant/size class. Overrides padding to be uniform on all sides and sets icon line-height to 1, producing a circle.
aria-labelAttributeRequired. Provides accessible name since there is no visible text.

For Designers

Do

Use icon buttons for well-understood actions (close, search, menu toggle) where the icon alone is unambiguous.

Don't

Don't use icon buttons for actions that require explanation. If users might not recognize the icon, use a standard button with a text label.

Usage Examples

Basic

<button class="btn btn-primary btn-icon-only btn-m"
        type="button"
        aria-label="More information">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>

<button class="btn btn-outline btn-icon-only btn-m"
        type="button"
        aria-label="More information">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>

All Sizes

<button class="btn btn-primary btn-icon-only btn-s" aria-label="Info">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>
<button class="btn btn-primary btn-icon-only btn-m" aria-label="Info">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>
<button class="btn btn-primary btn-icon-only btn-l" aria-label="Info">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>

Disabled

<button class="btn btn-primary btn-icon-only btn-m"
        disabled aria-disabled="true" aria-label="Info">
  <i class="btn-icon fa-solid fa-circle-info"></i>
</button>

Input

A single-line text field that combines a label, input field with optional icons, and hint text into a complete form control.

Overview

The Input component is a composed form control made up of three sub-elements: a Label (with optional info icon and required indicator), a Field (with optional leading and trailing icons), and optional Hint Text. It supports four interaction states (default, hover, focus, disabled) and three validation states (error, success, information). The Input is also used within the Forms pattern. All field text uses Lato Regular at Body M (16px/26px). Hint text uses Body S (14px/18px). Icons use Font Awesome 6 Solid at 16px.

Variants

Label

The label sits above the field. It pairs text with an optional trailing info icon. The required variant positions a red asterisk to the left of the label text (per WCAG best practice).

Default
Required

Field States

The field supports four interaction states combined with two content states: Pre-filled (placeholder text visible, field is empty) and Not Pre-filled (user has entered text). In the pre-filled state, icons and text use --text-placeholder to signal the field awaits input.

Pre-filled (Placeholder)

When the field is empty, placeholder text and icons display in --text-placeholder.

Default
Hover
Focus
Disabled

Not Pre-filled (Typed Text)

Once the user has entered text, icons and text use --text-body or the current interaction-state color.

Default
Hover
Focus
Disabled

Validation States

Validation feedback colors the field border, trailing icon, and hint text to indicate status. The trailing icon changes to fa-circle-check for success and fa-circle-xmark for error and information.

Default
Hint text
Error
Hint text
Error Hover
Hint text
Success
Hint text
Success Hover
Hint text
Information
Hint text
Info Hover
Hint text

Assembled Input

The full Input component with Label, Field, and Hint Text.

Hint text

Accessibility Considerations

WCAG 2.1 AA target. Every input must be associated with a visible label for assistive technology.

Keyboard Interactions

KeyAction
TabMoves focus into the input field. Disabled fields are skipped automatically.
Any characterEnters text at the cursor position (standard native input behavior).
Backspace / DeleteRemoves characters before / after cursor.

ARIA and Semantics

  • Use <label for="id"> or wrap the input in a <label> to associate the label with the field.
  • Add aria-required="true" to required fields. The visual asterisk is decorative via CSS ::before.
  • Connect hint/validation text via aria-describedby pointing to the hint element's id.
  • Set aria-invalid="true" on the input when in an error state.
  • Leading and trailing icons are decorative and must include aria-hidden="true".
  • Use the native disabled attribute on <input> to remove disabled fields from tab order automatically.

Construction

The Input is composed of three sub-elements stacked vertically: Label, Field, and Hint Text. All three sit inside a flex-column container (.input-group) with 8px gap.

Scale and Spacing

PropertyValueToken
Field padding (vertical)8px--padding-s
Field padding (horizontal)12px--padding-s-m
Field icon-to-text gap8px--padding-s
Field min-width336pxFixed
Field border width1pxBorder/Width/M default
Field border radius8px--radius-m
Focus ring width2pxBorder/Width/M action
Focus ring offset-5px (inset)Fixed
Focus ring radius12pxFixed
Label-to-field gap8px--padding-s
Field-to-hint gap4px--padding-xs
Label icon gap8px--padding-s

Styles

ElementFont / Size
Label textLato Regular (400), 16px/26px, 0.16px tracking
Label iconFA6 Solid (900), 16px/26px, 0.4px tracking
Field text (typed)Lato Regular (400), 16px/26px, 0.16px tracking
Field placeholderLato Regular (400), 16px/26px, 0.16px tracking
Field iconsFA6 Solid (900), 16px/26px, 0.4px tracking
Hint textLato Regular (400), 14px/18px

Color Tokens

ElementDefaultHoverFocusDisabled
Field border--border-accent--border-action-hover--border-action--border-disabled
Field background--surface-default-container--surface-default-container--surface-default-container--surface-disabled
Text / icons (typed)--text-body--text-action-hover--text-body--text-action-disabled
Text / icons (placeholder)--text-placeholder--text-action-hover--text-placeholder--text-action-disabled
Placeholder text--text-placeholder--text-placeholder
Focus ring--border-action (2px)
Label text--text-body
Hint text--text-body
ElementErrorError HoverSuccessSuccess HoverInformationInfo Hover
Field border--border-error--border-error--border-success--border-success--border-information--border-information
Field bg--surface-default-container--surface-error--surface-default-container--surface-success--surface-default-container--surface-information
Hint text--text-status-error--text-status-error--text-status-success--text-status-success--text-status-information--text-status-information
Status icon--text-status-error--text-status-error--text-status-success--text-status-success--text-status-information--text-status-information

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.input-groupContainerFlex column, 8px gap. Wraps label, field, and hint.
.input-labelLabelApplied to <label>. Flex row, 8px icon gap, body color.
.label-requiredModifierAdds red asterisk via ::before. Requires position: relative on the label.
.label-iconElementInfo icon inside the label. FA Solid, inherits color.
.form-fieldField containerDefault stateFlex row, 8px gap, 8px/12px padding, 1px border, 8px radius.
.is-placeholderStateApplies placeholder color to icons when the field is empty (pre-filled). Remove when the user has typed text.
.field-iconElementLeading or trailing icon inside the field. FA Solid, 16px.
.field-status-iconModifierApplied to trailing icon for validation. Colors the icon per status.
.is-disabledStateDisabled field. Also set the native disabled attribute on the input element.
.field-errorValidationError border + hover bg. Pair with .hint-error and .field-status-icon.
.field-successValidationSuccess border + hover bg. Pair with .hint-success and .field-status-icon.
.field-informationValidationInformation border + hover bg. Pair with .hint-information and .field-status-icon.
.input-hintElementHint or validation message below the field. Body S, body color by default.
.hint-errorModifierColors hint with --text-status-error.
.hint-successModifierColors hint with --text-status-success.
.hint-informationModifierColors hint with --text-status-information.

For Designers

Do

Always pair an input field with a visible label. Placeholder text alone is not a substitute for a label.

Don't

Don't rely on placeholder text as the primary label. It disappears once the user starts typing and is not reliably read by screen readers.

Do

Use hint text to provide context or formatting guidance (e.g., "Enter your first name"). Keep it brief and actionable.

Don't

Don't use hint text for critical instructions. If the information is essential, place it in the label or nearby body text instead.

Do

Show validation messages immediately after the user leaves the field or submits the form. Use the appropriate status color.

Don't

Don't rely solely on color to communicate validation status. The border, icon, and text message work together to convey the state.

Spacing Guidance

ContextPropertyValueToken
Input group internalgap8px / 4px--padding-s (label-to-field) / --padding-xs (field-to-hint)
Field internalpadding8px 12px--padding-s / --padding-s-m
Field icon to textgap8px--padding-s
Label icon to textgap8px--padding-s
Between stacked inputsgap24px--space-300
Last input to submitgap24px--space-300

Usage Examples

Basic Input with Label and Hint

<div class="input-group">
  <label class="input-label" for="fname">
    First name
    <i class="label-icon fa-solid fa-circle-question" aria-hidden="true"></i>
  </label>
  <div class="form-field">
    <i class="field-icon fa-solid fa-user" aria-hidden="true"></i>
    <input type="text" id="fname" placeholder="First name">
    <i class="field-icon fa-solid fa-circle-xmark" aria-hidden="true"></i>
  </div>
  <span class="input-hint">Hint text</span>
</div>

Required Input

<div class="input-group">
  <label class="input-label label-required" for="email">
    Email
    <i class="label-icon fa-solid fa-circle-question" aria-hidden="true"></i>
  </label>
  <div class="form-field">
    <i class="field-icon fa-solid fa-envelope" aria-hidden="true"></i>
    <input type="email" id="email" placeholder="Email address"
           aria-required="true">
    <i class="field-icon fa-solid fa-circle-xmark" aria-hidden="true"></i>
  </div>
</div>

Input with Error Validation

<div class="input-group">
  <label class="input-label" for="email-err">Email</label>
  <div class="form-field field-error">
    <i class="field-icon fa-solid fa-envelope" aria-hidden="true"></i>
    <input type="email" id="email-err" placeholder="Email"
           aria-invalid="true" aria-describedby="email-err-hint">
    <i class="field-icon field-status-icon
              fa-solid fa-circle-xmark" aria-hidden="true"></i>
  </div>
  <span class="input-hint hint-error"
        id="email-err-hint">Please enter a valid email.</span>
</div>

Input with Success Validation

<div class="input-group">
  <label class="input-label" for="email-ok">Email</label>
  <div class="form-field field-success">
    <i class="field-icon fa-solid fa-envelope" aria-hidden="true"></i>
    <input type="email" id="email-ok" placeholder="Email"
           aria-describedby="email-ok-hint">
    <i class="field-icon field-status-icon
              fa-solid fa-circle-check" aria-hidden="true"></i>
  </div>
  <span class="input-hint hint-success"
        id="email-ok-hint">Email verified successfully.</span>
</div>

Selector Accordion

Overview

A collapsible dropdown list that organises selectable options under a category header. Clicking the header expands a panel of options; clicking an option selects it.

The Selector Accordion is built from two sub-components: a Category header (with label and chevron icon) and Option items. The category header toggles between collapsed and expanded states. When expanded, options appear in a connected list below the header. The last option receives bottom border-radius to close the visual container. Options can be unselected, selected, or disabled. The category header and all options share a consistent set of interaction states (default, hover, pressed, disabled). Category text uses Body M (16px/26px). Option text uses Body S (14px/18px). The chevron icon uses Font Awesome 6 Solid.

Variants

Category Header (Collapsed)

The category header in its collapsed state. A chevron-down icon indicates the selector accordion can be expanded.

Default
Hover
Pressed
Focus
Disabled

Category Header (Expanded)

When expanded, the category header loses its bottom border-radius, connecting visually to the options below. The chevron rotates 180 degrees.

Default
Label
Label
Label
Hover (header)
Label
Label
Label
Pressed (header)
Label
Label
Label
Focus (header)
Label
Label
Label

Option States (Drop-down options that are unselected)

Individual option rows within an expanded selector accordion. Options use Body S typography. These show the unselected variant.

Default
Label
Label
Hover
Label
Label
Pressed
Label
Label
Focus
Label
Disabled
Label
Label

Option States (Drop-down options that are selected)

Options in the selected state, indicating the currently chosen value.

Selected
Label
Label
Selected + Hover
Label
Label
Selected + Pressed
Label
Label
Selected + Focus
Label

Last Option Focus

The last option in a selector accordion receives bottom border-radius on its focus ring using --radius-focus-ring (10px) to match the container shape.

Focus (last, selected)
Label

Assembled Selector Accordion

The full Selector Accordion component with a label, an info icon, and a dropdown. The category header toggles expanded/collapsed on click.

Collapsed
Label
Expanded
Label
Label
Label
Label

Working Demo

Click the category header to expand or collapse. Click an option to select it.

Choose a category

Accessibility Considerations

WCAG 2.1 AA target. The Selector Accordion should behave as a disclosure widget: the category header is a <button> that toggles visibility of the options panel.

Keyboard Interactions

KeyAction
TabMoves focus to the category header. When expanded, Tab moves through option items.
Enter / SpaceOn the category header: toggles expanded/collapsed. On an option: selects it.
EscapeCollapses the selector accordion and returns focus to the category header.

ARIA and Semantics

  • The category header must be a <button> with aria-expanded="true|false".
  • Use aria-controls on the header pointing to the options container's id.
  • Options should use role="option" or be native <button> elements.
  • The selected option should include aria-selected="true" or aria-current="true".
  • Disabled options must include the disabled attribute and aria-disabled="true".
  • The chevron icon is decorative: add aria-hidden="true".

Construction

The Selector Accordion is composed of a label row, a category header, and an options panel. The label sits above the header in a flex-column container with an 8px gap. Wrap the category button and the options panel in .sel-accordion-stack so the pair gets a definite width inside flex layouts (for example documentation state rows or the Filter pattern). The options panel stacks vertically below the header with no gap, creating a seamless bordered list.

Scale and Spacing

PropertyValueToken
Category padding (vertical)8px--padding-s
Category padding (horizontal)16px--padding-m
Category label-to-icon spacingspace-between, min gap 16pxCSS justify-content + gap: var(--jumper-XXS-XXXS)
Expand/collapse animation300ms ease-in-outHeight transition with ease-in-out easing
Category border width1pxBorder/Width/M default
Category border radius8px--radius-m
Option padding (vertical)8px--padding-s
Option padding (horizontal)16px--padding-m
Option border width1px (left, right, bottom)Border/Width/M default
Last option border radius (bottom)8px--radius-m
Label-to-header gap8px--jumper-XXXS-Tiny
Min width128px (desktop)--card-xs
Focus ring width2pxBorder/Width/M action
Focus ring offset (category)-5px (inset)Fixed
Focus ring radius (category, collapsed)10px--radius-focus-ring
Focus ring radius (category, expanded)10px 10px 0 0--radius-focus-ring (top corners only)
Focus ring offset (option)top: -4px, left/right/bottom: -5pxFixed
Focus ring radius (option)0px (rectangular)Fixed
Focus ring radius (last option)0 0 10px 10px--radius-focus-ring (bottom corners only)

Styles

ElementFont / Size
Label textLato Regular (400), 16px/26px, 0.16px tracking
Label iconFA6 Solid (900), 16px/26px, 0.4px tracking
Category textLato Regular (400), 16px/26px, 0.16px tracking
Category chevronFA6 Solid (900), 16px/26px, 0.4px tracking
Option textLato Regular (400), 14px/18px

Color Tokens for Category Header

ElementDefaultHoverPressedDisabled
Category bg --surface-default-container --surface-action-hover --surface-action-pressed --surface-disabled
Category border --border-default --border-action-hover --border-action-pressed --border-disabled
Category text / icon --text-body --text-action-on-hover --text-action-on-pressed --text-action-disabled
Focus ring --border-action (2px)

Color Tokens for Drop-Down Options

ElementDefaultHoverPressedSelectedSelected + HoverSelected + PressedDisabled
Option bg --surface-default-container --surface-action-hover --surface-action-pressed --surface-action --surface-action-hover --surface-action-pressed --surface-disabled
Option border --border-default --border-action-hover --border-action-pressed --border-action --border-action-hover --border-action-pressed --border-disabled
Option text --text-body --text-action-on-hover --text-action-on-pressed --text-action-on --text-action-on-hover --text-action-on-pressed --text-action-disabled
Focus ring --border-action (2px)

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.sel-accordion-groupContainerFlex column wrapper. Holds the label and the category + options block.
.sel-accordion-labelElementLabel row above the category header. Supports an optional .label-icon.
.sel-accordion-categoryElementCollapsedThe expandable header button. Must be a <button> with aria-expanded.
.sel-accordion-iconElementChevron icon inside the category header. Rotates 180deg when expanded.
.is-expandedStateApplied to the category header when the options panel is visible. Removes bottom border-radius and rotates the chevron.
.sel-accordion-dropdownContainerFlex column wrapper for option items. Hidden when collapsed.
.sel-accordion-optionElementAn individual option row. The last child automatically gets bottom border-radius.
.is-selectedStateMarks the currently selected option. Applies action background and white text.
.is-disabledStateDisabled state. Also set the native disabled attribute and aria-disabled="true".

For Designers

Do

Always pair the selector accordion with a visible label so users understand what category they are selecting from.

Don't

Don't nest selector accordions inside selector accordions. For multi-level hierarchies, consider a different navigation pattern.

Do

Keep option labels short and scannable. Users should quickly identify the right choice.

Don't

Don't use the Selector Accordion as a navigation element. It is designed for selecting values, not routing between pages.

Spacing Guidance

ContextRecommendationToken
Label above category8px gap--jumper-XXXS-Tiny
Internal category padding8px vertical, 16px horizontal--padding-s / --padding-m
Internal option padding8px vertical, 16px horizontal--padding-s / --padding-m
Selector Accordion in a form layout16px below preceding field--padding-m

Usage Examples

Basic Selector Accordion (collapsed)

<div class="sel-accordion-group">
  <div class="sel-accordion-label">Category <i class="label-icon fa-solid fa-circle-question"></i></div>
  <div>
    <button class="sel-accordion-category" type="button" aria-expanded="false" aria-controls="sel-accordion-options">
      <span>Select option</span>
      <i class="sel-accordion-icon fa-solid fa-chevron-down" aria-hidden="true"></i>
    </button>
    <div class="sel-accordion-dropdown" id="sel-accordion-options" hidden>
      <button class="sel-accordion-option" type="button">Option A</button>
      <button class="sel-accordion-option" type="button">Option B</button>
      <button class="sel-accordion-option" type="button">Option C</button>
    </div>
  </div>
</div>

Expanded with selected option

<div class="sel-accordion-group">
  <div class="sel-accordion-label">Category</div>
  <div>
    <button class="sel-accordion-category is-expanded" type="button" aria-expanded="true" aria-controls="sel-accordion-opts">
      <span>Category</span>
      <i class="sel-accordion-icon fa-solid fa-chevron-down" aria-hidden="true"></i>
    </button>
    <div class="sel-accordion-dropdown" id="sel-accordion-opts">
      <button class="sel-accordion-option" type="button">Option A</button>
      <button class="sel-accordion-option is-selected" type="button" aria-selected="true">Option B</button>
      <button class="sel-accordion-option" type="button">Option C</button>
    </div>
  </div>
</div>

Menu

Overview

A vertical list of navigational items used in sidebars or flyout panels. Each item contains an optional left icon, a text label, and an optional right arrow icon.

The Menu is a navigation component for mobile sidebars and flyout panels. Each menu item is a full-width row with consistent padding, a 2px bottom border, and support for left and right icons. Items can be unselected, selected (indicating the current page/section), or disabled. When assembled, items stack inside a bordered container with rounded corners. The first and last items receive top and bottom border-radius respectively.

Variants

Menu Item (Unselected)

Individual menu items in the unselected state across all interaction states.

Default
Hover
Pressed
Focus
Disabled

Menu Item (Selected)

Menu items in the selected state, indicating the currently active page or section.

Default
Hover
Pressed
Disabled

Assembled Menu

Menu items stacked in a bordered container. One item is selected. The container has rounded corners with 1px side/top borders. Each item's 2px bottom border separates rows; the last item has no bottom border.

Default

Working Demo

Click a menu item to select it. The previously selected item returns to its default state.

Accessibility Considerations

WCAG 2.1 AA target. The Menu should use <nav> with an aria-label for navigation context. Items should be focusable <button> or <a> elements.

Keyboard Interactions

KeyAction
TabMoves focus to the next menu item.
Shift + TabMoves focus to the previous menu item.
Enter / SpaceActivates the focused menu item (navigates or selects).

ARIA and Semantics

  • Wrap items in a <nav> element with a descriptive aria-label.
  • The selected item should include aria-current="page" to indicate the active page.
  • Disabled items must include the disabled attribute and aria-disabled="true".
  • Left and right icons are decorative: add aria-hidden="true".

Construction

The Menu is a <nav> container wrapping a vertical stack of menu item <button> or <a> elements. The container provides the outer border and border-radius. Each item provides its own 2px bottom border as a separator, except the last item.

Scale and Spacing

PropertyValueToken
Item padding (vertical)12px--padding-s-m
Item padding (horizontal)16px--padding-m
Icon-to-label gap8px--padding-s
Item bottom border width2pxBorder/Width/Action
Container border width1pxBorder/Width/Default
Container border radius8px--radius-m
Min width (standalone item)336pxFixed
Focus ring width2pxBorder/Width/Action
Focus ring offset-4px (inset)Fixed
Focus ring radius12pxFixed

Styles

ElementFont / Size
Item labelLato Regular (400), 16px/26px, 0.16px tracking
Left iconFA6 Solid (900), 16px/26px, 0.4px tracking
Right iconFA6 Solid (900), 16px/26px, 0.4px tracking

Color Tokens

ElementDefaultHoverPressedSelectedDisabled
Item bg --surface-default-container --surface-action-hover --surface-action-pressed --surface-action --surface-disabled
Item border --border-default --border-action-hover --border-action-pressed --border-action --border-disabled
Text / icons --text-body --text-action-on-hover --text-action-on-pressed --text-action-on --text-action-disabled
Focus ring --border-action (2px)

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.menu-navContainerBordered container with rounded corners. Use <nav> with aria-label.
.menu-itemElementUnselectedA single menu row. Use <button> or <a>.
.menu-item-iconElementIcon element (left or right). Uses Font Awesome 6 Solid.
.menu-item-labelElementText label. Flex-grows to fill remaining space.
.is-selectedStateMarks the currently active item. Applies action background and on-action text.
.is-disabledStateDisabled state. Also set the native disabled attribute and aria-disabled="true".

For Designers

Do

Use the Menu for sidebar or flyout navigation where each item leads to a distinct page or section.

Don't

Don't use the Menu for selecting form values. Use the Selector Accordion or a native select for form inputs.

Do

Keep labels short and descriptive. Use icons consistently across all items to reinforce scannability.

Don't

Don't mix items with and without icons in the same menu. Maintain visual consistency.

Spacing Guidance

ContextRecommendationToken
Internal item padding12px vertical, 16px horizontal--padding-s-m / --padding-m
Icon-to-label gap8px--padding-s
Menu in a sidebar layoutFill available width, 336px minFixed

Usage Examples

Basic Menu

<nav class="menu-nav" aria-label="Main navigation">
  <button class="menu-item" type="button">
    <i class="menu-item-icon fa-solid fa-house" aria-hidden="true"></i>
    <span class="menu-item-label">Dashboard</span>
    <i class="menu-item-icon fa-solid fa-arrow-right" aria-hidden="true"></i>
  </button>
  <button class="menu-item is-selected" type="button" aria-current="page">
    <i class="menu-item-icon fa-solid fa-user" aria-hidden="true"></i>
    <span class="menu-item-label">Profile</span>
    <i class="menu-item-icon fa-solid fa-arrow-right" aria-hidden="true"></i>
  </button>
  <button class="menu-item" type="button">
    <i class="menu-item-icon fa-solid fa-gear" aria-hidden="true"></i>
    <span class="menu-item-label">Settings</span>
    <i class="menu-item-icon fa-solid fa-arrow-right" aria-hidden="true"></i>
  </button>
</nav>

Progress Bar

A horizontal indicator that communicates the completion status of a task or process.

Overview

The progress bar shows how far along a process is by filling a horizontal track from left to right. It comes in two sizes: Desktop (8px track) and Mobile (4px track). An optional label row above the bar displays a text descriptor and a percentage value. The bar uses the action color for the fill against a light tinted track. Width of the fill is controlled via inline style or JavaScript.

Variants

Desktop (Default)

8px track height with a 20px/24px bold label. Use on desktop viewports.

With label
Completion: 80%
Without label
0%
Completion: 0%
100%
Completion: 100%

Mobile (Small)

4px track height with a 16px/20px bold label. Use on mobile viewports or in compact layouts.

With label
Completion: 80%
Without label

Accessibility Considerations

WCAG 2.1 AA target. Progress bars must communicate their current value to assistive technology.

Keyboard Interactions

KeyAction
n/aThe progress bar is not interactive and does not receive focus.

ARIA and Semantics

  • Use role="progressbar" on the track element.
  • Set aria-valuenow to the current percentage (e.g. 80).
  • Set aria-valuemin="0" and aria-valuemax="100".
  • Include aria-label describing the process (e.g. "Upload progress") when no visible label is present.
  • When a visible label exists, use aria-labelledby pointing to the label element's id.
  • For indeterminate progress (unknown completion), omit aria-valuenow and add an animation to the fill.

Construction

Scale and Spacing

PropertyDesktopMobileToken
Track height8px4pxFixed
Track border radius48px--radius-xxl
Label-to-track gap16px--jumper-XXS-XXXS
Label text-to-value gap8px--jumper-XXXS-Tiny

Styles

PropertyDesktopMobile
Label fontLato Bold (700), 20px/24px, -0.1px trackingLato Bold (700), 16px/20px, -0.08px tracking
Label color--text-headings (#393939)
Track background--surface-action-hover (#FFF1EB)
Fill color--surface-action (#E96620)
Fill border radius48px (--radius-xxl)

Color Tokens

ElementTokenValue
Label text--text-headings#393939
Track background--surface-action-hover#FFF1EB
Fill--surface-action#E96620

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.progress-barContainerDesktop sizeRoot flex-column wrapper. Full width of parent.
.progress-bar-smModifierSwitches to mobile size (4px track, 16px/20px label).
.progress-bar-labelElementOptional. Flex row containing text and value.
.progress-bar-label-textElementDescriptor text (e.g. "Completion:"). Non-wrapping.
.progress-bar-label-valueElementPercentage or status value (e.g. "80%").
.progress-bar-trackElementBackground track. Carries role="progressbar".
.progress-bar-fillElementFilled portion. Set width via inline style or JS.

For Designers

Do

Use progress bars for operations with a known completion percentage (uploads, multi-step forms, loading sequences).

Don't

Don't use a progress bar for indeterminate waits where completion is unknown. Use a spinner or skeleton instead.

Do

Pair the bar with a visible label so users understand what process is being tracked.

Don't

Don't use multiple progress bars stacked without clear labels distinguishing each process.

Spacing Guidance

ContextPropertyValueToken
Label to trackgap16px--jumper-XXS-XXXS
Label text to valuegap8px--jumper-XXXS-Tiny
Progress bar to adjacent contentmargin16-24px--jumper-XXS-XXXS / --jumper-XS-XXS

Usage Examples

Desktop with Label

<div class="progress-bar">
  <div class="progress-bar-label">
    <span class="progress-bar-label-text">Completion:</span>
    <span class="progress-bar-label-value">80%</span>
  </div>
  <div class="progress-bar-track"
       role="progressbar"
       aria-valuenow="80"
       aria-valuemin="0"
       aria-valuemax="100"
       aria-label="Completion">
    <div class="progress-bar-fill" style="width:80%"></div>
  </div>
</div>

Mobile without Label

<div class="progress-bar progress-bar-sm">
  <div class="progress-bar-track"
       role="progressbar"
       aria-valuenow="60"
       aria-valuemin="0"
       aria-valuemax="100"
       aria-label="Upload progress">
    <div class="progress-bar-fill" style="width:60%"></div>
  </div>
</div>

Desktop without Label

<div class="progress-bar">
  <div class="progress-bar-track"
       role="progressbar"
       aria-valuenow="45"
       aria-valuemin="0"
       aria-valuemax="100"
       aria-label="Processing">
    <div class="progress-bar-fill" style="width:45%"></div>
  </div>
</div>

Radio Button

A form control that allows users to select exactly one option from a mutually exclusive set. Pairs a 24px circular indicator with an optional text label.

Overview

The radio button is a single-select control used inside groups of two or more options. When selected, it displays a small white dot centered inside an action-colored fill. When unselected, it renders as a bordered empty circle. The component supports five states (default, hover, focus, disabled, error) across both selected and unselected types. An optional label sits to the right with an 8px gap. The container has 4px vertical padding to provide comfortable touch/click targets.

Variants

Unselected

Empty circle with action-colored border. Shows the available selection target.

Default
Hover
Focus
Disabled
Error

Selected

Filled circle with a centered white dot. The action-colored background confirms the selection.

Default
Hover
Focus
Disabled
Error

With Label

Radio button paired with a Body M label (Lato Regular, 16px/26px) at an 8px gap.

Unselected
Selected
Disabled
Error

Accessibility Considerations

WCAG 2.1 AA target. Radio buttons must be operable by keyboard and clearly communicate their selected/unselected state to assistive technology.

Keyboard Interactions

KeyAction
TabMoves focus into the radio group (to the selected item, or the first item if none selected)
Arrow Down / RightSelects the next radio button in the group
Arrow Up / LeftSelects the previous radio button in the group
SpaceSelects the focused radio button (if not already selected)

ARIA and Semantics

  • Use native <input type="radio"> inside a <label> for automatic association. All radios in a group must share the same name attribute.
  • Wrap the group in a <fieldset> with a <legend> describing the question or category.
  • For custom implementations, use role="radiogroup" on the container and role="radio" with aria-checked on each option.
  • Error state should be paired with aria-describedby pointing to an error message element and aria-invalid="true" on the input.
  • Disabled radio buttons use the native disabled attribute, which removes them from tab order.
  • Focus ring: 2px solid --border-action, 2px offset, fully rounded.

Construction

Scale and Spacing

PropertyValueToken
Radio button size24 x 24px--space-300
Inner dot size8 x 8px--space-100
Border width1px--space-10
Border radius104px (fully round)--radius-xxl / 104px
Radio-to-label gap8px--space-100
Container vertical padding4px--space-50
Focus ring offset2pxn/a
Focus ring width2pxn/a

Styles

PropertyValue
Inner dot8px circle, centered via absolute positioning
Label fontLato Regular (400), 16px/26px
Label color--text-body (#464646)

Color Tokens

StateBackgroundBorderDot
Default (unselected)--surface-default-container--border-actionn/a
Default (selected)--surface-action--border-actionwhite
Hover--surface-action-hover--border-action-hover--text-action-hover
Focus(same as default)(same as default)(same as default)
Disabled--surface-disabled--border-disabledwhite
Error--surface-error--border-error--text-status-error

Application and Context

For Developers

Attribute / ClassTypeDescription
.form-radioContainerWrapping <label>. Flex row with 8px gap and 4px vertical padding.
input[type="radio"]ElementNative radio with appearance: none for custom styling. All radios in a group share the same name.
checkedAttributeNative checked state. Fills background and shows inner dot via ::after.
disabledAttributeNative disabled. Grays out and removes from tab order.
.has-errorModifierAdded to the <input>. Applies error surface/border/dot colors.
.is-disabledModifierVisual-only disabled state for documentation previews.
.show-hoverModifierForces hover appearance for documentation previews.
.show-focusModifierForces focus ring for documentation previews.

For Designers

Do

Use radio buttons when users must select exactly one option from a mutually exclusive set of two or more choices.

Don't

Don't use radio buttons for multi-select scenarios. Use checkboxes instead.

Do

Always group radios inside a <fieldset> with a descriptive <legend> so the question context is clear.

Don't

Don't pre-select a radio option unless there is a sensible default. An empty initial state forces a deliberate choice.

Spacing Guidance

ContextPropertyValueToken
Radio to labelgap8px--space-100
Between stacked radiosgap / margin-bottom8-16px--space-100 to --space-200
Radio group to next fieldmargin-bottom24px--space-300
Container vertical paddingpadding-top / padding-bottom4px--space-50

Usage Examples

Radio Group with Fieldset

<fieldset>
  <legend>Preferred contact method</legend>
  <label class="form-radio">
    <input type="radio" name="contact" value="email">
    Email
  </label>
  <label class="form-radio">
    <input type="radio" name="contact" value="phone">
    Phone
  </label>
  <label class="form-radio">
    <input type="radio" name="contact" value="text">
    Text message
  </label>
</fieldset>

Pre-selected Option

<fieldset>
  <legend>Frequency</legend>
  <label class="form-radio">
    <input type="radio" name="freq" value="daily" checked>
    Daily
  </label>
  <label class="form-radio">
    <input type="radio" name="freq" value="weekly">
    Weekly
  </label>
</fieldset>

Error State with Message

<fieldset aria-describedby="rb-err">
  <legend>Select a plan</legend>
  <label class="form-radio">
    <input type="radio" name="plan" value="basic"
           class="has-error" aria-invalid="true">
    Basic
  </label>
  <label class="form-radio">
    <input type="radio" name="plan" value="pro"
           class="has-error" aria-invalid="true">
    Pro
  </label>
</fieldset>
<span id="rb-err" class="input-hint hint-error">
  Please select a plan to continue.
</span>

Disabled Radio

<label class="form-radio">
  <input type="radio" name="tier" value="enterprise" disabled>
  Enterprise (coming soon)
</label>

Snackbar

The Snackbar as a brief, modal notification to the user for things such as: A new fundraising campaign just launched, a new press release has been posted to the website, fundraising and sign-ups have opened for enrollment for Team World Vision, the user started setting up their account but hasn’t finished, etc.

Overview

The snackbar is a lightweight feedback surface at a minimum of 336px width. It contains an optional status icon, a bold heading, body copy, an optional "Learn More" text link, a close button, and an optional progress bar (using the Progress Bar SM component) along the bottom edge. Four status variants (Default, Success, Error, Information) control the icon and heading color while body text stays neutral. All optional elements (icon, link, close, progress bar) can be included or omitted independently.

Variants

Default

Action/brand color for icon and heading. Use for general confirmations or neutral status updates.

All options
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More
No link
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
No icon
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More
No Progress Bar
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More

Success

Green icon and heading for positive confirmations such as saves, completions, or sends.

All options
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More

Error

Red icon and heading for failures or destructive outcomes that need user attention.

All options
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More

Information

Teal icon and heading for informational notices or system-level messages.

All options
Snackbar Title
Lorem Ipsum, the standard dummy text for a lot of digital placeholder text.
Learn More

Accessibility Considerations

WCAG 2.1 AA target. Snackbars must not block interaction and should be perceivable by assistive technology without stealing focus.

Keyboard Interactions

KeyAction
TabMoves focus to the link or close button inside the snackbar
Enter / SpaceActivates the focused close button or link
EscapeDismisses the snackbar when it has focus

ARIA and Semantics

  • Use role="status" for non-critical updates (default, success, information) so screen readers announce content politely.
  • Use role="alert" for error snackbars to assert immediate attention.
  • Include aria-live="polite" (or "assertive" for errors) so content is announced when it appears.
  • The close button must have aria-label="Dismiss" since it contains no visible text.
  • If the snackbar auto-dismisses, the timeout should be at least 5 seconds. Provide a way to pause or extend.
  • The snackbar must not trap focus or prevent interaction with the rest of the page.

Construction

The snackbar composes several sub-elements. The embedded progress bar reuses the Progress Bar SM component. The text link reuses the Body Link M pattern from the Text Link component.

Scale and Spacing

PropertyValueToken
Container width100% / max var(--card-xl)--card-xl
Container border radius8px--radius-m
Container border width1pxFixed
Content padding16px--padding-m
Content-to-close gap16px--padding-m
Icon-to-text gap8px--padding-s
Heading / body / link gap8px--jumper-XXXS-Tiny
Icon container width20pxFixed
Close button container width28pxFixed
Progress bar height4pxProgress Bar SM

Styles

ElementFont / Size
HeadingLato Bold (700), 20px/24px, -0.1px tracking
BodyLato Regular (400), 16px/26px, 0.16px tracking
Link textLato Bold (700), 16px/26px, 0.16px tracking
Link chevronFA6 Solid (900), 16px/26px, 0.4px tracking
Status iconFA6 Solid (900), 20px/24px, 0.5px tracking
Close iconFA6 Solid (900), 20px/32px, 0.05px tracking

Color Tokens

ElementDefaultSuccessErrorInformation
Container bg--surface-default-container
Container border--border-default
Icon--text-action--text-status-success--text-status-error--text-status-information
Heading--text-action--text-status-success--text-status-error--text-status-information
Body text--text-body
Link text + chevron--text-action--text-status-success--text-status-error--text-status-information
Close icon--text-body
Progress track--surface-action-hover--surface-success--surface-error--surface-information
Progress fill--surface-action--text-status-success--text-status-error--text-status-information

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.snackbarContainerDefault statusRoot element. 100% wide, max-width --card-xl (736px), rounded, bordered.
.snackbar-successModifierApplies success color to icon and heading.
.snackbar-errorModifierApplies error color to icon and heading.
.snackbar-informationModifierApplies information color to icon and heading.
.snackbar-contentLayoutFlex row wrapping icon/text and close button. 16px padding.
.snackbar-icon-textLayoutFlex row for icon and text block.
.snackbar-iconElementOptional. 20px-wide icon container. Omit to hide.
.snackbar-textLayoutFlex column for heading, body, and link.
.snackbar-headingElementBold title. Color set by status modifier.
.snackbar-bodyElementBody copy in neutral color.
.text-link.text-link-mSub-componentOptional. Reuses the Body Link M component. Hover and focus states inherit automatically.
.snackbar-closeElementDismiss button. Requires aria-label="Dismiss".
.progress-bar.progress-bar-smSub-componentOptional. Embeds the Progress Bar SM at the bottom.

For Designers

Do

Use snackbars for brief, non-blocking feedback that confirms an action or surfaces a transient status update (campaign launched, enrollment opened, etc.).

Don't

Don't use snackbars for critical decisions that require user input. Use a dialog or modal instead.

Do

Match the status variant to the message type so color communicates meaning at a glance.

Don't

Don't stack multiple snackbars at once. Queue them so only one is visible at a time.

Spacing Guidance

ContextPropertyValueToken
Container internalpadding16px--padding-m
Icon to textgap8px--padding-s
Content to closegap16px--padding-m
Text block internalgap8px--jumper-XXXS-Tiny
Link text to chevrongap8px--padding-s
Snackbar to viewport edgeoffset16-24px--padding-m / --padding-l

Usage Examples

Default with All Options

<div class="snackbar" role="status" aria-live="polite">
  <div class="snackbar-content">
    <div class="snackbar-icon-text">
      <div class="snackbar-icon">
        <i class="fa-solid fa-circle-info"></i>
      </div>
      <div class="snackbar-text">
        <div class="snackbar-heading">Changes saved</div>
        <div class="snackbar-body">Your profile has been updated.</div>
        <a class="text-link text-link-m" href="#">
          <span class="link-label">Learn More</span>
          <i class="link-icon fa-solid fa-chevron-right"></i>
        </a>
      </div>
    </div>
    <button class="snackbar-close" aria-label="Dismiss">
      <i class="fa-solid fa-xmark"></i>
    </button>
  </div>
  <div class="progress-bar progress-bar-sm">
    <div class="progress-bar-track">
      <div class="progress-bar-fill" style="width:60%"></div>
    </div>
  </div>
</div>

Error without Progress Bar

<div class="snackbar snackbar-error" role="alert" aria-live="assertive">
  <div class="snackbar-content">
    <div class="snackbar-icon-text">
      <div class="snackbar-icon">
        <i class="fa-solid fa-circle-exclamation"></i>
      </div>
      <div class="snackbar-text">
        <div class="snackbar-heading">Upload failed</div>
        <div class="snackbar-body">File exceeds the 10 MB limit.</div>
      </div>
    </div>
    <button class="snackbar-close" aria-label="Dismiss">
      <i class="fa-solid fa-xmark"></i>
    </button>
  </div>
</div>

Minimal (No Icon, No Link, No Progress)

<div class="snackbar" role="status" aria-live="polite">
  <div class="snackbar-content">
    <div class="snackbar-icon-text">
      <div class="snackbar-text">
        <div class="snackbar-heading">Item deleted</div>
        <div class="snackbar-body">The record has been removed.</div>
      </div>
    </div>
    <button class="snackbar-close" aria-label="Dismiss">
      <i class="fa-solid fa-xmark"></i>
    </button>
  </div>
</div>

Tab Bar

A horizontal navigation element that shows users where they are in a multi-step process. Users can see completed steps, the current step, and remaining steps, and click any previous step to navigate back.

Overview

The Tab Bar is a linear, horizontal stepper that communicates progress through a multi-step workflow. Each tab represents one step. The selected (current) tab is visually distinguished with a bold label and an orange bottom border. Unselected tabs use regular-weight text with no bottom border. Two layout variants exist: Icons (each tab has an optional leading icon and a text label) and Numbers (each tab has a numeric prefix and a text label, no icon). Four interaction states apply per tab: Default, Hover, Focus, and Disabled.

Variants

Tab Item - Unselected

Regular-weight label, no bottom border highlight. Used for steps the user has not yet reached or for completed steps they can revisit.

Default
Hover
Focus
Disabled

Tab Item - Selected

Bold label with a 2px orange bottom border. Indicates the current active step in the process.

Default
Hover
Focus
Disabled

Assembled - Icons Variant

Each tab contains an icon and a text label. The full bar spans horizontally with a 1px bottom border. The second tab is shown as selected.

Icons

Assembled - Numbers Variant

Each tab uses a numeric prefix instead of an icon (e.g., "1. Name", "2. Address"). The second tab is shown as selected.

Numbers

Accessibility Considerations

WCAG 2.1 AA target. The Tab Bar uses a tablist role pattern so assistive technology can convey the number of steps, the current step, and allow keyboard navigation between them.

Keyboard Interactions

KeyAction
TabMoves focus into the tab bar, landing on the currently selected tab
Arrow Left / Arrow RightMoves focus between tabs within the bar
Enter / SpaceActivates the focused tab and navigates to that step
HomeMoves focus to the first tab
EndMoves focus to the last tab

ARIA and Semantics

  • The container should use role="tablist" to identify the group of tabs.
  • Each tab should use role="tab".
  • The selected tab should have aria-selected="true"; all others aria-selected="false".
  • Disabled tabs should have aria-disabled="true" and tabindex="-1" to remove them from the tab order.
  • Use aria-label on the tablist container to describe the process (e.g., "Registration steps").
  • Icons are decorative and must have aria-hidden="true".

Construction

The Tab Bar is a single-row flex container with individual tab items. Each tab item is a flex row combining an optional icon and a label. The selected state is indicated by bold text and a 2px bottom border.

Scale and Spacing

PropertyValueToken
Tab item padding8px--padding-s
Icon-to-label gap8px--padding-s
Bar bottom border1pxFixed
Selected bottom border2pxFixed
Focus ring width2pxFixed
Focus ring offset (unselected)-4px all sidesFixed
Focus ring offset (selected)-4px top/right/left, -6px bottomFixed
Focus ring radius4px--radius-s

Styles

ElementFont / Size
Label (unselected)Lato Regular (400), 16px/26px, 0.16px tracking
Label (selected)Lato Bold (700), 16px/26px, 0.16px tracking
IconFA6 Solid (900), 16px/26px, 0.4px tracking

Color Tokens

ElementDefaultHoverFocusDisabled
Tab backgroundtransparent--surface-action-hovertransparent--surface-disabled
Label text--text-body--text-action-hover--text-body--text-action-disabled
Icon--text-action--text-action-hover--text-action--text-action-disabled
Selected bottom border--border-action--border-action--border-action--border-disabled
Bar bottom border--border-default
Focus ring--border-action

Application and Context

For Developers

Attribute / ClassTypeDefaultDescription
.tab-barContainer-Root flex container with a 1px bottom border.
.tab-itemElementUnselectedIndividual tab. 8px padding, flex row with gap. Use <button> for interactivity.
.tab-item.selectedModifier-Marks the current step. Applies bold weight and orange bottom border.
.tab-item.is-disabledModifier-Disables interaction. Gray background, muted text/icon. Add disabled attribute.
.tab-iconElement-Optional. Leading icon inside a tab item. Use FA6 icon class.
.tab-labelElement-Text label. For the Numbers variant, include the number in the text (e.g., "1. Name").
role="tablist"ARIA-Apply to the .tab-bar container.
role="tab"ARIA-Apply to each .tab-item.
aria-selectedARIAfalseSet to "true" on the selected tab.

For Designers

Do

Use the Tab Bar for multi-step workflows where users benefit from knowing their position and can navigate back to previous steps.

Don't

Don't use the Tab Bar for primary site navigation. It is designed for sequential, in-page processes.

Do

Use the Icons variant when each step has a clear, recognizable icon that aids comprehension at a glance.

Don't

Don't mix the Icons and Numbers variants in the same tab bar. Pick one layout and apply it consistently.

Do

Disable future steps when a user must complete the current step before proceeding.

Don't

Don't disable all tabs. At minimum, the current step must remain interactive.

Spacing Guidance

ContextPropertyValueToken
Tab item internalpadding8px--padding-s
Icon to labelgap8px--padding-s
Tab bar to surrounding contentmargin16-24px--padding-m / --padding-l
Tab bar below content panelgap16-24px--padding-m / --padding-l

Usage Examples

Icons Variant

<div class="tab-bar" role="tablist" aria-label="Registration steps">
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-icon fa-solid fa-user" aria-hidden="true"></span>
    <span class="tab-label">Profile</span>
  </button>
  <button class="tab-item selected" role="tab" aria-selected="true" type="button">
    <span class="tab-icon fa-solid fa-location-dot" aria-hidden="true"></span>
    <span class="tab-label">Address</span>
  </button>
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-icon fa-solid fa-phone" aria-hidden="true"></span>
    <span class="tab-label">Contact</span>
  </button>
  <button class="tab-item is-disabled" role="tab" aria-selected="false" aria-disabled="true" tabindex="-1" type="button" disabled>
    <span class="tab-icon fa-solid fa-credit-card" aria-hidden="true"></span>
    <span class="tab-label">Billing</span>
  </button>
</div>

Numbers Variant

<div class="tab-bar" role="tablist" aria-label="Checkout steps">
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-label">1. Name</span>
  </button>
  <button class="tab-item selected" role="tab" aria-selected="true" type="button">
    <span class="tab-label">2. Address</span>
  </button>
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-label">3. Contact</span>
  </button>
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-label">4. Billing</span>
  </button>
  <button class="tab-item" role="tab" aria-selected="false" type="button">
    <span class="tab-label">5. Review</span>
  </button>
</div>

Toggle

A binary switch that lets users turn a setting on or off with immediate effect. Pairs a 48x24 track with a sliding 26x16 knob and an optional text label.

Overview

The toggle (also called a switch) is a binary control for settings that take effect immediately. Unlike a checkbox, which typically requires a form submission, a toggle communicates instant state change. When "on," the knob slides to the right and the track fills with the action color. When "off," the knob rests on the left with a neutral track. The component supports four states (default, hover, focus, disabled) across both on and off positions. An optional label sits to the right with an 8px gap.

Variants

Off (Unselected)

Neutral track with action-colored border. Knob rests at the left edge.

Default
Hover
Focus
Disabled

On (Selected)

Action-colored track with the knob slid to the right. Confirms the setting is active.

Default
Hover
Focus
Disabled

With Label

Toggle paired with a Body M label (Lato Regular, 16px/26px) at an 8px gap.

Off
On
Disabled

Accessibility Considerations

WCAG 2.1 AA target. The toggle must communicate its on/off state to assistive technology and be fully operable by keyboard.

Keyboard Interactions

KeyAction
SpaceToggles the switch between on and off
TabMoves focus to the next focusable element
Shift+TabMoves focus to the previous focusable element

ARIA and Semantics

  • Uses a visually hidden <input type="checkbox"> under the hood, which provides native checked/unchecked semantics to screen readers.
  • Add role="switch" to the input for AT that distinguishes switches from checkboxes. The checked state maps to aria-checked.
  • The wrapping <label> automatically associates the text with the control.
  • Disabled toggles use the native disabled attribute, which removes them from tab order.
  • Focus ring: 2px solid --border-action, -6px inset, 104px border-radius to match the track shape.

Construction

Scale and Spacing

PropertyValueToken
Track width48pxFixed
Track height24pxFixed
Knob size26 x 16pxFixed
Track border width2pxFixed
Border radius (track)104px (fully round)Fixed
Border radius (knob)100px (pill)Fixed
Knob OFF positionleft: 2px, top: 2pxFixed
Knob ON positionleft: 16px, top: 2pxFixed
Toggle-to-label gap8px--padding-s
Focus ring offset-6px (inset)Fixed
Focus ring width2pxFixed
Focus ring radius104pxFixed

Styles

PropertyValue
Knob shapePill (border-radius: 100px), absolutely positioned
Transition0.2s ease on background, border-color, transform
Label fontLato Regular (400), 16px/26px
Label color--text-body (#464646)

Color Tokens

StateTrack BackgroundTrack BorderKnob
Off (default)--surface-action-on2px --border-action--btn-icon-action
On (default)--surface-action2px --border-action--btn-icon-action-on
Hover (both)--surface-action-hover2px --border-action-hover--btn-icon-on-hover
Focus(same as default)(same as default)(same as default)
Disabled#f5f5f52px --border-default--btn-icon-action-disabled

Application and Context

For Developers

Attribute / ClassTypeDescription
.form-toggleContainerWrapping <label>. Flex row with 8px gap.
input[type="checkbox"]ElementVisually hidden native checkbox. Drives checked state via CSS sibling selectors.
.toggle-trackElementThe 48x24 pill-shaped track with 2px border. Styled via adjacent sibling of the hidden input.
.toggle-knobElementThe 26x16 pill-shaped knob, absolutely positioned inside the track. Slides from left: 2px (off) to left: 16px (on).
checkedAttributeNative checked. Fills track with action color and slides knob to the right.
disabledAttributeNative disabled. Grays out track and knob.
role="switch"AttributeRecommended. Tells screen readers this is a switch, not a checkbox.
.show-hoverModifier (track)Forces hover appearance on the track for documentation previews.
.show-focusModifier (track)Forces focus ring on the track for documentation previews.
.is-disabledModifier (track)Visual-only disabled state for documentation previews.
.show-checkedModifier (track)Forces checked appearance on the track for documentation previews.

For Designers

Do

Use a toggle for binary settings that take effect immediately (e.g., "Enable notifications," "Dark mode").

Don't

Don't use a toggle when the change requires a form submission to take effect. Use a checkbox instead.

Do

Always provide a visible label. Position it to the right of the toggle for left-to-right reading flow.

Don't

Don't place toggles in a long vertical list for multi-select scenarios. Use checkboxes for that pattern.

Spacing Guidance

ContextPropertyValueToken
Toggle to labelgap8px--space-100
Between stacked togglesgap / margin-bottom16-24px--space-200 to --space-300
Toggle group to next sectionmargin-bottom24px--space-300

Usage Examples

Basic Toggle with Label

<label class="form-toggle">
  <input type="checkbox" role="switch">
  <span class="toggle-track">
    <span class="toggle-knob"></span>
  </span>
  Enable notifications
</label>

Pre-checked Toggle

<label class="form-toggle">
  <input type="checkbox" role="switch" checked>
  <span class="toggle-track">
    <span class="toggle-knob"></span>
  </span>
  Dark mode
</label>

Disabled Toggle

<label class="form-toggle">
  <input type="checkbox" role="switch" disabled>
  <span class="toggle-track">
    <span class="toggle-knob"></span>
  </span>
  Feature unavailable
</label>

Settings Group

<fieldset>
  <legend>Notification preferences</legend>
  <label class="form-toggle">
    <input type="checkbox" role="switch" checked>
    <span class="toggle-track">
      <span class="toggle-knob"></span>
    </span>
    Email notifications
  </label>
  <label class="form-toggle">
    <input type="checkbox" role="switch">
    <span class="toggle-track">
      <span class="toggle-knob"></span>
    </span>
    Push notifications
  </label>
  <label class="form-toggle">
    <input type="checkbox" role="switch" checked>
    <span class="toggle-track">
      <span class="toggle-knob"></span>
    </span>
    SMS alerts
  </label>
</fieldset>

Toggle Selector

Overview

A pill-shaped binary selector that lets users switch between two mutually exclusive options. An animated indicator pill slides between sides to reinforce which option is active. This component is intended for use as a sub-component within larger patterns (e.g., search filters, view toggles).

The Toggle Selector pairs two text labels inside a rounded container. Clicking an inactive label slides the internal pill to that side. Unlike the Toggle (switch), which represents on/off state, this component presents two named choices.

Variants

Mobile (40px height)

Default viewport. Uses --body-s typography (14px / 20px).

Left Active
Right Active

Desktop (46px height)

Uses --body-m typography (16px / 24px, 0.16px letter-spacing).

Left Active
Right Active

Text States

Text labels change color based on their active/inactive status and the current interaction state.

StateActive Text (on pill)Inactive Text (off pill)
Default--text-action-on (white)--text-action (#e96620)
Hover--text-action-on-hover (#fff1eb)--text-action-hover (#f78958)
Pressed--text-action-on-pressed (#ffc7ae)--text-action-pressed (#d14000)

Text State Previews (Mobile)

Default
Hover
Pressed
Focus

Working Demo

Click either label to slide the selector. The animation uses a custom Bezier curve (cubic-bezier(0.4, 0, 0.58, 1)) at 300ms for a natural feel.

Mobile

Desktop

Accessibility

AttributeDetail
WCAG TargetAA (4.5:1 for text, 3:1 for non-text elements)
RoleContainer uses role="radiogroup"; each option uses role="radio"
ARIAActive option: aria-checked="true"; inactive: aria-checked="false"
KeyboardTab focuses the group; Arrow Left/Right switches between options; Enter/Space activates the focused option
Focus ring::after pseudo-element with inset: -5px, 2px solid --border-action, border-radius: 48px
Screen readerAnnounce option label and selected state. Container aria-label describes the group purpose.

Construction

Scale and Spacing

PropertyMobileDesktop
Container height40px46px
Container border2px solid --surface-action
Container radius48px
Pill inset (top/bottom/sides)1px
Pill border1px solid --border-action
Pill radius48px
Option padding (horizontal)12px16px
Font size14px (--body-s-size)16px (--body-m-size)
Line height20px (--body-s-line)24px (--body-m-line)
Letter spacing00.16px
Font weight700 (Bold)
Focus ring offset-5px inset, 2px solid --border-action

Styles

ElementDetail
Container shapePill (border-radius: 48px), overflow: clip
Pill shapePill (border-radius: 48px), absolutely positioned, covers 50% of the container width
Pill animationcubic-bezier(0.4, 0, 0.58, 1) at 300ms on left and right properties
Text animationcolor transitions at 300ms with the same Bezier curve
LayoutInline flex, two equal-width button children

Color Tokens

ElementTokenValue (Light)
Container background--surface-action-on#ffffff
Container border--surface-action#e96620
Pill background--surface-action#e96620
Pill border--border-action#f78958
Active text (default)--text-action-on#ffffff
Active text (hover)--text-action-on-hover#fff1eb
Active text (pressed)--text-action-on-pressed#ffc7ae
Inactive text (default)--text-action#e96620
Inactive text (hover)--text-action-hover#f78958
Inactive text (pressed)--text-action-pressed#d14000
Focus ring--border-action#f78958

Application & Context

For Developers

Class / AttributeRoleDescription
.toggle-selectorContainerOuter pill container. Add .right-active to slide the pill right.
.toggle-selector-desktopModifierApplies desktop sizing (46px height, body-m typography).
.right-activeStateSlides the pill to the right side. Toggled via JavaScript on click.
.toggle-selector-pillIndicatorThe sliding pill element. Must be the first child of the container.
.toggle-selector-optionButtonEach clickable text option. Use <button type="button">.
.is-activeState (option)Marks the currently selected option. Updates text color to on-action tokens.
.show-hoverPreview modifierForces hover text color for documentation previews.
.show-pressedPreview modifierForces pressed text color for documentation previews.
.show-focusPreview modifierForces focus ring for documentation previews.

For Designers

Do

Use the Toggle Selector for binary filtering or view switching where both options are always visible and labeled.

Don't

Don't use for on/off settings. Use the Toggle (switch) for boolean state changes.

Do

Keep both labels short and similar in length for balanced visual weight.

Don't

Don't use for more than two options. Use a Tab Bar or segmented control for three or more choices.

Spacing Guidance

ContextPropertyValueToken
Selector to adjacent contentmargin-bottom16-24px--space-200 to --space-300
Selector within a filter bargap16px--space-200
Selector to heading abovemargin-top8px--space-100

Usage Examples

Basic Toggle Selector (Mobile)

<div class="toggle-selector" role="radiogroup" aria-label="Search method">
  <span class="toggle-selector-pill"></span>
  <button class="toggle-selector-option is-active" type="button"
          role="radio" aria-checked="true">
    Search by Details
  </button>
  <button class="toggle-selector-option" type="button"
          role="radio" aria-checked="false">
    Search by Need
  </button>
</div>

Desktop Variant (Right Active)

<div class="toggle-selector toggle-selector-desktop right-active"
     role="radiogroup" aria-label="Search method">
  <span class="toggle-selector-pill"></span>
  <button class="toggle-selector-option" type="button"
          role="radio" aria-checked="false">
    Search by Details
  </button>
  <button class="toggle-selector-option is-active" type="button"
          role="radio" aria-checked="true">
    Search by Need
  </button>
</div>

JavaScript Initialization

document.querySelectorAll('.toggle-selector').forEach(selector => {
  const options = selector.querySelectorAll('.toggle-selector-option');
  options.forEach((opt, i) => {
    opt.addEventListener('click', () => {
      options.forEach(o => {
        o.classList.remove('is-active');
        o.setAttribute('aria-checked', 'false');
      });
      opt.classList.add('is-active');
      opt.setAttribute('aria-checked', 'true');
      selector.classList.toggle('right-active', i === 1);
    });
  });
});

Filter

A composed pattern for refining search results: a mode toggle (Details vs Need), optional filter controls, and primary actions. Mobile uses a compact bar with an expand affordance; desktop lays out criteria in a horizontal row.

Overview

The Filter pattern combines existing system components: the Toggle Selector (Search by Details / Search by Need), Buttons (outline and primary icon-only for filter open, primary icon-only for close, text buttons for Clear all and Close), and one Selector Accordion per criterion when Search by Details is active. Search by Need swaps the criteria region for a Button Group (.btn-group / .btn-group-item with .btn-group-icon), not accordions. Omit the optional info icon on Details criterion labels unless a specific field exposes contextual help.

Mobile (Figma size S, 336px max width): 40px toggle height, 14px bold labels on the selector, filter icon in an outline icon-only button when the panel is collapsed. Expanded state shows the same toggle, a primary icon-only close control, stacked criteria, and a row with Clear all (disabled when no changes) and Close.

Desktop (Figma size M): 46px toggle height, 16px bold labels on the selector, criteria in a horizontal row (narrow columns), and Clear all aligned to the trailing edge. Spacing follows jumper and padding tokens from the file.

Figma source: WV Design System 1.02 — Drop-down filter menu (component set; node 16318:5349).

Breakpoint: The desktop layout transitions to the mobile layout at 1103px (tablet max). See the Working Example below.

Variants

Mobile — Collapsed

Toggle selector plus outline filter icon. Full width is capped at --card-s (336px).

Mobile — Expanded (Details mode)

Close control replaces the filter icon. Criteria stack vertically with --padding-m between blocks.

Location
Gender
Age
Birth Month
Birth Day

Mobile — Expanded (Search by Need, "Waiting" selected)

The default state for Search by Need pre-selects "Waiting over 1 year" and shows its Snackbar. The Button Group stacks vertically with 1px bottom borders. The footer has a single "Close" button (btn-outline). Figma state=expanded, filter=need waiting, status=selected, viewport=mobile.

Children who have been waiting for a sponsor for 12 months or more.

Desktop — Details mode

Toggle centered above a criteria row sized to the Figma content width (1057px inside the 1105px frame). Criterion columns use flex: 1 with min-width: 128px on the accordion trigger so 128px is a minimum, not a fixed width. Gaps use --jumper-XS-XXS (24px). Clear all uses intrinsic button width; extra horizontal space goes to the criteria columns. Top padding --jumper-S-XS (32px) aligns the button with the triggers.

Location
Gender
Age
Birth Month
Birth Day

Desktop — Search by Need ("Waiting" selected) mode

The default state for Search by Need pre-selects "Waiting over 1 year." The Button Group shows the selected item at its intrinsic width (not stretched) and a Snackbar appears below it with descriptive text. All four icons use the action color (--btn-icon-action). The Button Group and Snackbar are grouped with a --jumper-XXS-XXXS (16px) gap. Figma state=expanded, filter=need waiting, viewport=desktop.

Children who have been waiting for a sponsor for 12 months or more.

Working Example

Interactive demo. Toggle between Details and Need; select accordion options or need buttons. Resize the browser below 1104px to see the mobile layout.

Location
Gender
Age
Birth Month
Birth Day

Accessibility Considerations

WCAG 2.1 AA target. The pattern exposes multiple controls; each interactive element must have an accessible name.

Keyboard and focus

  • Tab through the toggle options (buttons), filter/close icon buttons, each Selector Accordion category header and option, and footer actions in DOM order.
  • Toggle selector options should use role="radio" and aria-checked, or be implemented as a single tablist if you adopt that variant from the Toggle Selector page.
  • When Search by Details is active, each criterion uses the Selector Accordion disclosure pattern: category <button> with aria-expanded and aria-controls, options as <button> rows with aria-selected on the current value (see that component page).
  • When Search by Need is active, need criteria use the Button Group (.btn-group / .btn-group-item); follow that page for keyboard and selection semantics when you wire behavior.

ARIA

  • Wrap the panel in role="region" with aria-label (e.g. "Search filters") when it is a distinct landmark.
  • Icon-only buttons require aria-label (e.g. "Open filters", "Close filters").

Construction

Layout tokens

PropertyMobileDesktop
Panel max width--card-s (336px)1105px (Figma frame)
Panel padding--padding-m (16px)--jumper-XS-XXS (24px)
Panel border1px solid var(--border-default)"
Panel radius--radius-m"
Header gap (toggle to icon button)--padding-m"
Body gap (stack / row)--padding-m--jumper-XS-XXS
Label to category (Selector Accordion)--jumper-XXXS-Tiny (8px), per Selector Accordion spec"
Desktop criteria rowMax width 1057px; five criterion columns flex: 1 1 0 with min-width: 128px; accordion category min-width: 128px, width: 100%; gap --jumper-XS-XXS
Desktop Clear allflex: 0 0 auto; button intrinsic width; column padding-top: var(--jumper-S-XS) (32px)
Responsive breakpointmax-width: 743px; (--bp-mobile-max)min-width: 1104px (--bp-desktop-min)
Shadow (Snackbar)var(--shadow-default): 0px 8px 16px 0px with color --shadow-default-container (light: rgba(179,179,179,0.1), dark: rgba(24,24,24,0.1))"
Need Snackbar width100% (full width)--space-9200 (736px)

Components used

PartImplementation
Mode switch.toggle-selector + .toggle-selector-desktop on desktop; widths 260px / 310px per Toggle Selector spec.
Open / Close.btn.btn-outline.btn-icon-only.btn-s with fa-filter; .btn.btn-primary.btn-icon-only.btn-s with fa-xmark.
Details criterion rowSelector Accordion: .sel-accordion-group, .sel-accordion-label, .sel-accordion-stack (category + .sel-accordion-dropdown).
Need criteriaButton Group: .btn-group, .btn-group-item, .btn-group-icon. Mobile: stacked column with 1px bottom borders. Desktop: intrinsic-width horizontal row (not stretched). All icons use --btn-icon-action; selected items inherit the Button Group .selected state colors.
Need expanded (Snackbar)When a need option is selected, a Snackbar (.snackbar) appears below the Button Group with descriptive text. Grouped with --jumper-XXS-XXXS gap. Desktop width: --space-9200 (736px). Mobile: full width.
Actions.btn.btn-primary.btn-m; Clear all disabled per spec when no dirty filters.

Application and Context

For Developers

Class / regionRole
.doc-wideAllows an element to expand beyond the default 736px body column up to 80vw. Use on preview blocks, tables, or images that need more horizontal space.
.pattern-filterRoot panel; add .pattern-filter-mobile or .pattern-filter-desktop.
.pattern-filter-mobile-expandedApplies vertical gap between header and body on mobile expanded state.
.pattern-filter-headerRow: toggle + trailing icon button.
.pattern-filter-bodyCriteria and optional CTA row / column.
.pattern-filter-fieldWraps one .sel-accordion-group per criterion (spacing between fields uses body gap tokens above).
.sel-accordion-demoOptional (docs site): enables expand/collapse and option selection in the demo shell, same as the Selector Accordion page.
Criterion label iconDo not add .label-icon on filter criteria by default (matches Figma). Use it only when the product implements an active info control for that field.
.pattern-filter-cta-rowMobile footer buttons (flex row, wrap).
.pattern-filter-ctaDesktop trailing column for Clear all.
.toggle-selector-demoOptional: enables existing doc-site JS for pill animation (same as Toggle Selector page).
.pattern-filter-body--needApplied to .pattern-filter-body when "Search by Need" is active. Centers the Button Group on desktop, stacks vertically on mobile. Groups button group + Snackbar with --jumper-XXS-XXXS gap.
[data-filter-demo]Working Example wiring: enables toggle switching (Details/Need), need button selection, Snackbar display, and Clear all logic via app.js.
[data-filter-mode]On toggle buttons inside [data-filter-demo]. Values: details or need. Toggles visibility of the corresponding [data-filter-body].
[data-need]On .btn-group-item buttons. Values: waiting, orphan, risk, hunger. Click selects/deselects and shows the corresponding Snackbar description.

For Designers

Do

Use the Filter pattern when users need to narrow a dataset (e.g. sponsorship search) without leaving the current view.

Don't

Don't use this pattern for linear step-by-step flows; use the Tab Bar or a dedicated wizard.

Do

Keep criterion labels short and match typography to Input labels (Body M).

Don't

Don't invent new field chrome; stay on --border-default and shared radii for triggers.

Usage Examples

Desktop markup

At viewport widths of 1104px and above, the filter uses .pattern-filter-desktop. The toggle selector sits at the top, followed by a horizontal row of accordion fields and a "Clear all" button. When "Search by Need" is active, the body switches to a centered button group with a Snackbar.

<div class="pattern-filter pattern-filter-desktop"
     role="region" aria-label="Filters">

  <div class="toggle-selector toggle-selector-desktop" style="width:310px"
       role="radiogroup" aria-label="Search mode">
    <span class="toggle-selector-pill"></span>
    <button type="button" class="toggle-selector-option is-active"
            role="radio" aria-checked="true">Search by Details</button>
    <button type="button" class="toggle-selector-option"
            role="radio" aria-checked="false">Search by Need</button>
  </div>

  <!-- Search by Details body -->
  <div class="pattern-filter-body">
    <div class="pattern-filter-field">
      <div class="sel-accordion-group">
        <div class="sel-accordion-label" id="loc-lbl">Location</div>
        <div class="sel-accordion-stack">
          <button type="button" class="sel-accordion-category"
                  aria-expanded="false" aria-controls="loc-opts"
                  aria-labelledby="loc-lbl">
            <span>No preference</span>
            <i class="sel-accordion-icon fa-solid fa-chevron-down"
               aria-hidden="true"></i>
          </button>
          <div class="sel-accordion-dropdown is-collapsed" id="loc-opts"
               role="group" aria-label="Location options">
            <button type="button" class="sel-accordion-option is-selected"
                    aria-selected="true">No preference</button>
            <button type="button" class="sel-accordion-option">Kenya</button>
            <button type="button" class="sel-accordion-option">Nigeria</button>
          </div>
        </div>
      </div>
    </div>
    <!-- repeat .pattern-filter-field for Gender, Age, Birth Month, Birth Day -->
    <div class="pattern-filter-cta">
      <button type="button" class="btn btn-primary btn-m is-disabled"
              disabled aria-disabled="true">
        <span class="btn-label">Clear all</span>
      </button>
    </div>
  </div>

  <!-- Search by Need body (hidden by default) -->
  <div class="pattern-filter-body pattern-filter-body--need"
       style="display:none">
    <div class="btn-group" role="group" aria-label="Search by Need">
      <button type="button" class="btn-group-item selected"
              data-need="waiting">
        <i class="btn-group-icon fa-solid fa-calendar"
           aria-hidden="true"></i>
        Waiting over 1 year
      </button>
      <button type="button" class="btn-group-item" data-need="orphan">
        <i class="btn-group-icon fa-solid fa-child"
           aria-hidden="true"></i>
        Orphaned child
      </button>
      <button type="button" class="btn-group-item" data-need="risk">
        <i class="btn-group-icon fa-solid fa-circle-exclamation"
           aria-hidden="true"></i>
        Higher-risk area
      </button>
      <button type="button" class="btn-group-item" data-need="hunger">
        <i class="btn-group-icon fa-solid fa-bowl-rice"
           aria-hidden="true"></i>
        Hunger crisis area
      </button>
    </div>
    <div class="snackbar">
      <div class="snackbar-content">
        <div class="snackbar-icon-text">
          <div class="snackbar-text">
            <p class="snackbar-body">Children who have been waiting
               for a sponsor for 12 months or more.</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Mobile markup

At viewport widths of 1103px and below, the filter uses .pattern-filter-mobile. A header row holds the toggle selector (260px) and a collapse/expand icon button. The body stacks fields vertically and hides when collapsed.

<div class="pattern-filter pattern-filter-mobile pattern-filter-expanded"
     role="region" aria-label="Filters">

  <div class="pattern-filter-header">
    <div class="toggle-selector" style="width:260px"
         role="radiogroup" aria-label="Search mode">
      <span class="toggle-selector-pill"></span>
      <button type="button" class="toggle-selector-option is-active"
              role="radio" aria-checked="true">Search by Details</button>
      <button type="button" class="toggle-selector-option"
              role="radio" aria-checked="false">Search by Need</button>
    </div>
    <button type="button" class="btn btn-primary btn-icon-only btn-s"
            aria-label="Close filters">
      <i class="btn-icon fa-solid fa-xmark" aria-hidden="true"></i>
    </button>
  </div>

  <!-- Search by Details body -->
  <div class="pattern-filter-body">
    <div class="pattern-filter-field">
      <div class="sel-accordion-group">
        <div class="sel-accordion-label" id="m-loc-lbl">Location</div>
        <div class="sel-accordion-stack">
          <button type="button" class="sel-accordion-category"
                  aria-expanded="false" aria-controls="m-loc-opts"
                  aria-labelledby="m-loc-lbl">
            <span>No preference</span>
            <i class="sel-accordion-icon fa-solid fa-chevron-down"
               aria-hidden="true"></i>
          </button>
          <div class="sel-accordion-dropdown is-collapsed" id="m-loc-opts"
               role="group" aria-label="Location options">
            <button type="button" class="sel-accordion-option is-selected"
                    aria-selected="true">No preference</button>
            <button type="button" class="sel-accordion-option">Kenya</button>
            <button type="button" class="sel-accordion-option">Nigeria</button>
          </div>
        </div>
      </div>
    </div>
    <!-- repeat .pattern-filter-field for Gender, Age, Birth Month, Birth Day -->
  </div>

  <!-- Search by Need body (hidden by default) -->
  <div class="pattern-filter-body pattern-filter-body--need"
       style="display:none">
    <div class="btn-group" role="group" aria-label="Search by Need">
      <button type="button" class="btn-group-item selected"
              data-need="waiting">
        <i class="btn-group-icon fa-solid fa-calendar"
           aria-hidden="true"></i>
        Waiting over 1 year
      </button>
      <button type="button" class="btn-group-item" data-need="orphan">
        <i class="btn-group-icon fa-solid fa-child"
           aria-hidden="true"></i>
        Orphaned child
      </button>
      <button type="button" class="btn-group-item" data-need="risk">
        <i class="btn-group-icon fa-solid fa-circle-exclamation"
           aria-hidden="true"></i>
        Higher-risk area
      </button>
      <button type="button" class="btn-group-item" data-need="hunger">
        <i class="btn-group-icon fa-solid fa-bowl-rice"
           aria-hidden="true"></i>
        Hunger crisis area
      </button>
    </div>
    <div class="snackbar">
      <div class="snackbar-content">
        <div class="snackbar-icon-text">
          <div class="snackbar-text">
            <p class="snackbar-body">Children who have been waiting
               for a sponsor for 12 months or more.</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Responsive markup (recommended)

Use .pattern-filter-responsive instead of viewport-specific classes. A single markup auto-switches between desktop and mobile layouts at the 1103px breakpoint via CSS media queries.

<div class="pattern-filter pattern-filter-responsive pattern-filter-expanded"
     role="region" aria-label="Filters" data-filter-demo>

  <!-- Mobile header (hidden at >= 1104px) -->
  <div class="pattern-filter-header">
    <div class="toggle-selector" role="radiogroup"
         aria-label="Search mode">
      <span class="toggle-selector-pill"></span>
      <button type="button" class="toggle-selector-option is-active"
              role="radio" aria-checked="true"
              data-filter-mode="details">Search by Details</button>
      <button type="button" class="toggle-selector-option"
              role="radio" aria-checked="false"
              data-filter-mode="need">Search by Need</button>
    </div>
    <button type="button" class="btn btn-primary btn-icon-only btn-s"
            aria-label="Close filters" data-filter-collapse>
      <i class="btn-icon fa-solid fa-xmark" aria-hidden="true"></i>
    </button>
  </div>

  <!-- Desktop toggle (hidden at <= 1103px) -->
  <div class="pattern-filter-toggle-desktop">
    <div class="toggle-selector" role="radiogroup"
         aria-label="Search mode">
      <span class="toggle-selector-pill"></span>
      <button type="button" class="toggle-selector-option is-active"
              role="radio" aria-checked="true"
              data-filter-mode="details">Search by Details</button>
      <button type="button" class="toggle-selector-option"
              role="radio" aria-checked="false"
              data-filter-mode="need">Search by Need</button>
    </div>
  </div>

  <!-- Details body -->
  <div class="pattern-filter-body" data-filter-body="details">
    <div class="pattern-filter-field">
      <div class="sel-accordion-group"><!-- accordion --></div>
    </div>
    <!-- repeat .pattern-filter-field -->
    <div class="pattern-filter-cta">
      <button type="button" class="btn btn-primary btn-m is-disabled"
              disabled aria-disabled="true">
        <span class="btn-label">Clear all</span></button>
    </div>
  </div>

  <!-- Need body -->
  <div class="pattern-filter-body pattern-filter-body--need"
       data-filter-body="need" style="display:none">
    <div class="btn-group" role="group" aria-label="Search by Need">
      <button type="button" class="btn-group-item"
              data-need="waiting">
        <i class="btn-group-icon fa-solid fa-calendar"
           aria-hidden="true"></i>
        Waiting over 1 year
      </button>
      <!-- repeat for orphan, risk, hunger -->
    </div>
    <div class="snackbar" data-need-snackbar
         style="display:none"><!-- snackbar content --></div>
  </div>
</div>

Key CSS breakpoints

/* Mobile / Tablet */
@media (max-width: 1103px) {
  .pattern-filter-responsive .pattern-filter-toggle-desktop { display: none; }
  .pattern-filter-responsive .pattern-filter-header { display: flex; }
  .pattern-filter-responsive .pattern-filter-body { display: none; }
  .pattern-filter-responsive.pattern-filter-expanded .pattern-filter-body {
    display: flex;
  }
}

/* Desktop */
@media (min-width: 1104px) {
  .pattern-filter-responsive .pattern-filter-header { display: none; }
  .pattern-filter-responsive .pattern-filter-toggle-desktop { display: flex; }
  .pattern-filter-responsive .pattern-filter-body {
    display: flex; flex-direction: row;
  }
  .pattern-filter-responsive .pattern-filter-body.pattern-filter-body--need {
    flex-direction: column; align-items: center;
  }
}

Initialize demos (optional, docs site)

Add toggle-selector-demo on the toggle for pill animation (see Toggle Selector). Add sel-accordion-demo on each .sel-accordion-group for expand/collapse and option selection (see Selector Accordion). Add data-filter-demo on the root .pattern-filter to wire up mode toggling and need selection.

Forms

Reusable layout patterns for collecting user input consistently across the system.

Overview

Form patterns standardize how input groups, labels, validation messages, and submit actions are composed. The system provides five form primitives: Input (single-line text field), Text Area (multi-line), Checkbox, Radio Button, and Toggle Switch. Each primitive uses shared tokens for spacing, typography, and color to maintain visual consistency across any form layout.

Variants

Label

The label component pairs a text label with an optional trailing info icon. A required variant places a red asterisk to the left of the label for accessibility (not to the right, per WCAG best practice).

Default
Required

Input Field

A single-line text field with optional leading icon, trailing status icon, label, and hint text. Supports four validation states.

Default
Hint text
Hover
Focus
Disabled

Input Validation States

Validation feedback colors the field border and hint text. The trailing icon changes to indicate status.

Error
Hint text
Error Hover
Hint text
Success
Hint text
Success Hover
Hint text
Information
Hint text
Info Hover
Hint text

Text Area

A multi-line field for longer content. Includes a label block with optional hint copy above the field. Minimum height is 224px.

Default
Hint copy

Checkbox

A binary selection control. The checked state uses the action color with a white check icon. Label text sits to the right with 24px gap.

Checked
Unchecked

Radio Button

A single-choice control within a group. The selected state fills with the action color and shows a white dot. Label text sits to the right with 24px gap.

Selected
Unselected

Toggle Switch

A binary on/off control. The track fills with the action color when on. The knob slides left (off) to right (on). 64px wide, 32px tall, 24px knob.

On
Off

Accessibility Considerations

WCAG 2.1 AA target. All form controls must be programmatically associated with their labels.

Keyboard Interactions

KeyAction
TabMoves focus between form controls in DOM order.
SpaceToggles checkbox and toggle switch. Selects focused radio button.
EnterSubmits the form when a button has focus.
Arrow Up/DownCycles through radio buttons within a group.

ARIA and Semantics

  • Use native <label> with for attribute or wrap the control inside the label.
  • Error messages must use aria-describedby pointing to the hint element, plus aria-invalid="true" on the field.
  • Radio groups should be wrapped in a <fieldset> with a <legend>.
  • Toggle switches use role="switch" with aria-checked.
  • Disabled fields use the native disabled attribute (removes from tab order).

Focus Management

  • Input focus ring: 2px solid --border-action (#f78958), inset -5px, radius 10px (slightly larger than the field radius).
  • Checkbox and radio focus: 2px solid outline at 2px offset.
  • Toggle focus: 2px solid outline around the track at 2px offset.

Construction

Scale and Spacing

PropertyValueToken
Field padding (vertical)8pxFixed/padding/S
Field padding (horizontal)12pxFixed/padding/M-S
Field min-width336pxFixed
Field border radius8pxCorner Radius/M field
Field border width1pxBorder/Width/M default
Icon-to-text gap8pxFixed/padding/S
Label-to-field gap8pxFixed/padding/S
Checkbox/Radio-to-label gap24pxFixed/padding/L
Toggle track64 x 32pxFixed
Toggle knob24px circleFixed
Radio dot8px circleFixed
Text area min-height224pxFixed

Styles

PropertyValueToken
Label fontLato Regular 16px/26pxBody/Body M
Field textLato Regular 16px/26pxBody/Body M
Hint textLato Regular 14px/18pxBody/Body S
Icon fontFA6 Free Solid 16pxBody/Icons/M icon
Checkbox radius4pxCorner Radius/S checkbox
Radio / Toggle radius104px (full round)Corner Radius/XL round

Color Tokens

StateBackgroundBorderText / Icon
Default Surface/Global/default container
#fdfdfd
Border/Global/accent
#d0d0d0
Text/Default/body
#464646
Hover Surface/Global/default container
#fdfdfd
Border/Action/action-hover
#ffa984
Text/Action/action-hover
#f78958
Focus Surface/Global/default container
#fdfdfd
Border/Action/action
#f78958
Text/Default/body
#464646
Disabled Surface/Button/Primary/bg-disabled
#f5f5f5
Border/Action/disabled
#e9e9e9
Text/Action/action-disabled
#979797
Error Surface/Global/default container
#fdfdfd
Border/Status/error
#f88c90
Text/Status/error
#de515e
Success Surface/Global/default container
#fdfdfd
Border/Status/success
#609c71
Text/Status/success
#438558
Information Surface/Global/default container
#fdfdfd
Border/Status/information
#3a90a9
Text/Status/information
#097892
Checked / On Surface/Action/action
#e96620
Border/Action/action
#f78958
White (#ffffff)

Application and Context

For Developers

Class / AttributeTypeDescription
.input-groupContainerWraps label, field, and hint into a vertical stack (8px label-to-field, 4px field-to-hint).
.input-labelLabelHorizontal flex row for label text and optional info icon.
.label-requiredModifierAdded to .input-label. Renders a red asterisk to the left of the label via ::before.
.label-iconIconTrailing info icon inside .input-label. Uses Font Awesome 6 Solid.
.form-fieldField wrapperThe input container with icons, border, and background. Wrap a native <input> inside.
.field-error / .field-success / .field-informationModifierApplied to .form-field to set status border color.
.input-hintHint textBelow the field. Add .hint-error, .hint-success, or .hint-information for status color.
.form-textareaTextareaNative <textarea> element styled to match the field design.
.form-checkboxLabel wrapperWraps a native <input type="checkbox"> and its label text.
.form-radioLabel wrapperWraps a native <input type="radio"> and its label text.
.form-toggleLabel wrapperWraps a hidden checkbox, .toggle-track (with .toggle-knob), and label text.

For Designers

Do

Stack fields vertically with var(--space-300) gap. Group related fields inside a <fieldset>.

Don't

Don't place fields in multiple columns on mobile. Horizontal layouts only work at desktop widths with sufficient space.

Do

Place the primary action button aligned to the left, below the last field, with 24px spacing.

Don't

Don't rely solely on color to communicate validation state. Pair error/success border color with descriptive hint text.

Spacing Guidance

ContextSpacingToken
Between stacked fields24px--space-300
Label to field8px--padding-s
Field to hint text4px--padding-xs
Last field to submit button24px--space-300
Checkbox/Radio vertical stack16px--space-200

Usage Examples

Required Label

<label class="input-label label-required" for="fname">
  First name
  <i class="label-icon fa-solid fa-circle-question"></i>
</label>

Input with Label and Hint

<div class="input-group">
  <label class="input-label" for="fname">
    First name
    <i class="label-icon fa-solid fa-circle-question"></i>
  </label>
  <div class="form-field">
    <i class="field-icon fa-solid fa-user"></i>
    <input type="text" id="fname" placeholder="First name">
    <i class="field-icon fa-solid fa-circle-xmark"></i>
  </div>
  <span class="input-hint">Hint text</span>
</div>

Input with Error

<div class="input-group">
  <label class="input-label" for="email">Email</label>
  <div class="form-field field-error">
    <i class="field-icon fa-solid fa-envelope"></i>
    <input type="email" id="email" placeholder="Email"
           aria-invalid="true" aria-describedby="email-err">
    <i class="field-icon field-status-icon
              fa-solid fa-circle-xmark"></i>
  </div>
  <span class="input-hint hint-error"
        id="email-err">Please enter a valid email.</span>
</div>

Text Area

<div class="input-group">
  <label class="input-label" for="bio">Bio</label>
  <span class="input-hint"
        style="color:var(--text-placeholder)">Hint copy</span>
  <textarea class="form-textarea" id="bio"
            placeholder="Tell us about yourself"></textarea>
</div>

Checkbox Group

<fieldset>
  <legend>Preferences</legend>
  <label class="form-checkbox">
    <input type="checkbox" checked>
    <span>Email notifications</span>
  </label>
  <label class="form-checkbox">
    <input type="checkbox">
    <span>SMS notifications</span>
  </label>
</fieldset>

Radio Group

<fieldset>
  <legend>Size</legend>
  <label class="form-radio">
    <input type="radio" name="size" value="s">
    <span>Small</span>
  </label>
  <label class="form-radio">
    <input type="radio" name="size" value="m" checked>
    <span>Medium</span>
  </label>
  <label class="form-radio">
    <input type="radio" name="size" value="l">
    <span>Large</span>
  </label>
</fieldset>

Toggle Switch

<label class="form-toggle">
  <input type="checkbox" role="switch"
         aria-checked="true" checked>
  <span class="toggle-track">
    <span class="toggle-knob"></span>
  </span>
  <span>Dark mode</span>
</label>

Design Example

A prayer request card demonstrating form layout at Desktop and Mobile breakpoints. The card uses jumper spacing tokens so padding, gaps, and margins adapt responsively.

Desktop (736px)

We're here to pray for you

Let's walk this journey of hope, together.

Mobile (384px)

We're here to pray for you

Let's walk this journey of hope, together.

Navigation

Reusable navigation patterns including sidebars, breadcrumbs, and tab structures.

Overview

Navigation patterns define how users move between sections of an application. They include persistent sidebar navigation, breadcrumb trails for wayfinding, and tabbed interfaces for content switching.

Sidebar Pattern

  • Fixed position, full viewport height.
  • Categories sorted A-Z.
  • Links sorted A-Z within each category.
  • Active link highlighted with left border accent.

Breadcrumb Pattern

For the full component specification, states, and usage guidelines, see the Breadcrumbs component page.

  • Shows hierarchy with chevron-right (fa-chevron-right) separators inside each crumb.
  • First crumb includes a home icon (fa-house).
  • Current page (last crumb) has a persistent underline and aria-current="page".

Usage Examples

<nav class="breadcrumb" aria-label="Breadcrumb">
  <a class="breadcrumb-item" href="/home">
    <i class="breadcrumb-icon fa-solid fa-house" aria-hidden="true"></i>
    <span>Home</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </a>
  <a class="breadcrumb-item" href="/components">
    <span>Components</span>
    <i class="breadcrumb-icon fa-solid fa-chevron-right" aria-hidden="true"></i>
  </a>
  <a class="breadcrumb-item current" href="/components/button" aria-current="page">
    <span class="link-label">Button</span>
  </a>
</nav>

Accessibility

Every component and pattern in this system targets WCAG 2.1 AA compliance as a baseline.

Color Contrast

  • Normal text: minimum 4.5:1 contrast ratio against its background.
  • Large text (24px+ regular, 18.66px+ bold): minimum 3:1.
  • Non-text elements (icons, borders): minimum 3:1.

Keyboard Navigation

  • All interactive elements must be reachable via Tab.
  • Focus indicators must be visible. This system uses a 2px solid outline in --surface-action.
  • Modal components must trap focus while open.

Screen Reader Support

  • Use semantic HTML elements first (<button>, <nav>, <main>).
  • Apply ARIA roles only when native semantics are insufficient.
  • Dynamic content changes must use aria-live regions.

Disabled States

Disabled elements use --text-action-disabled and --surface-disabled. While contrast is intentionally reduced, the element must remain legible.

Responsive Design

The system targets three breakpoints: Mobile (384px), Tablet (800px), and Desktop (1504px). A minimum mobile reference of 320px is provided for designers but is not enforced as a development breakpoint.

Device Sizes and Breakpoints

DeviceViewport / Frame size (px)Min width breakpoint (px)Max width breakpoint (px)
Mobile384320743
Tablet8006401151
Desktop150411521919
Mega1920......

Approach

  • Primary design focus: Mobile and Desktop.
  • Tablet values are defined only where they deviate from Mobile.
  • Typography, jumper spacing, grid, and margins all adapt per breakpoint.

Grid System

PropertyDesktopTabletMobile
Columns1284
Col Width64px48px72px
Col Gutter32px32px16px
Col Margin16px16px8px
Global Margin176px80px16px
Row Gap32px16px16px
Body Max Width736px448px336px

Fixed vs Fluid Layouts

Fixed layout: Content does not expand when the viewport stretches beyond the design frame (1504px). The total side margin is Global Margin + Col Margin (e.g., 176 + 16 = 192px per side on desktop).

Fluid layout: Content extends to the edges of the outer columns. The margin consists of only the Global Margin, and containers stretch left and right as the viewport grows.

Readability

Body text (Body M, 16px) should be constrained to 60-75 characters per line. The grid's Body text.max width token (736px on Desktop, 448px on Tablet, 336px on Mobile) enforces this.