Toggle group
A row of toggles 8 apart — the deck tool’s library filters (All · Covers · Sections · Content · Charts · Case studies), its chart-type pills, and the site’s blog categories.
Beta · Actions · packages/css/src/components/toggle-group.css
.pui-toggle-group — toggles space/8 apart, never 0; exactly one on for type single.
Sizes across, states down.
What it is for
Section titled “What it is for”- Variant outline / filled (the two Toggle forms) × Size small / medium
- Five exposed toggles — edit labels and states on the instance; Has toggle 4 / Has toggle 5 show the fourth and fifth (three by default)
- The first toggle is on by default; for type single keep exactly one on
- Gap is space/8 and never 0 — a connected row of buttons is Button group, not this
- Two to seven items; past seven it is a Select or a Combobox
Use when a set of filters or modes that sit together and share one answer (chart type, a category) or several (tags).
Do not use when the choices are actions (Button group); the choices open views (Tabs); one setting on / off (Switch); the list is long (Select, Combobox).
Relates to Toggle, Toggle - filled, Button group, Tabs list, Chip.
Anti-patterns a group built by looping Button with a manual active class · mixed outline and filled toggles · zero gap · a group with nothing on and no ‘All’.
Keywords: toggle group, filters, pills, segmented, single, multiple, category, mode
Figma — page Toggle: Toggle group (4 variants)
shadcn: toggle-group
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens toggle-group.css.
<div class="pui-toggle-group" role="group" aria-label="Library filters"> <button class="pui-toggle" aria-pressed="true">All</button> …</div>Rule — two to seven items; past seven it is a Select or a Combobox.
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”| ink on ground | reads | floor | the matrix calls it | |
|---|---|---|---|---|
#544d4d on #f2f2f2 |
7.36 | 4.5 | text.muted on surface.sunken (promised) | ok |
#544d4d on #ffffff |
8.24 | 4.5 | text.muted on surface.page (promised) | ok |
#ffffff on #544d4d |
8.24 | 4.5 | Toggle label when on (promised) | ok |
#0b0202 on #ffffff |
20.51 | 4.5 | text.default on surface.page (promised) | ok |
4 distinct pair(s) rendered, 0 that axe calls a violation.
The focus ring
Section titled “The focus ring”10 of 10 focus stop(s) in this block meet WCAG 2.4.13 on screen — the same crop screenshotted focused and unfocused, and the difference measured.
Keyboard
Section titled “Keyboard”| role | tab stops | what the pattern requires | |
|---|---|---|---|
group .pui-toggle-group |
6 | — | — |
group .pui-toggle-group |
4 | — | — |
What the consumer’s script owes. as Button group: role=“group” takes none of its children’s keys, so every toggle keeps its own tab stop and its own Space. Nothing to write.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.