Button groups
Button groups should make related actions feel connected. Use them for compact command sets or mutually related choices, not as decoration around unrelated links. There is no generic segmented control: grouping happens through three concrete containers, each owned by the component that renders it.
When to use#
- Use a group for a compact command set or mutually related choices; never as decoration around unrelated links
- Use the connected cluster when the choices are positions in one sequence and exactly one is current — page numbers are the only case today
- Use a spaced row when the actions are related but independently meaningful — the home hero pair, filter chips
- Show selection state on the member, not the container:
tag-pill-activewitharia-pressedfor filters,aria-currentfor the current page - Give every group an
aria-labelnaming what the group does, on the<nav>or wrapping element
Implementation#
<nav class="filter-chip-list" aria-label="Filter by topic">
<button class="filter-chip filter-chip-button tag-pill-active" type="button" aria-pressed="true">All <span class="filter-chip-count">24</span></button>
<button class="filter-chip filter-chip-button" type="button" aria-pressed="false">Security <span class="filter-chip-count">9</span></button>
</nav>
<nav aria-label="Page numbers">
<ol class="pagination-pages">
<li><span class="pagination-button pagination-button-current" aria-current="page">1</span></li>
<li><a class="pagination-button" href="/en/blog/page/2/">2</a></li>
</ol>
</nav><button class="filter-chip filter-chip-button" type="button" data-table-filter-group="tag" data-table-filter-value="security" aria-pressed="false">Security</button>There is no shared group class. The three containers are home-hero-actions (layouts/index.html), filter-chip-list (rendered by partials/data-page/filter-row.html — see Filter row), and pagination-pages (rendered by partials/pagination.html — see Pagination). Styling is component classes in assets/css/site.css. Behaviour lives in assets/js/site.js: initTableLists() wires buttons carrying data-table-filter-group and data-table-filter-value, plus an optional data-table-filter-reset button, toggling tag-pill-active and aria-pressed; initTalksArchive() does the same for the talks archive.
Variants#
| Container | Pattern | Rendered by |
|---|---|---|
pagination-pages | Connected cluster; members share inner borders | partials/pagination.html |
filter-chip-list | Spaced row of toggles with counts | partials/data-page/filter-row.html |
home-hero-actions | Spaced pair of call-to-action buttons | layouts/index.html |
Interface manifest
- Kind
- Component
- Category
- Action
- Status
- Implemented
- Implementation
- Partial:
layouts/partials/data-page/filter-row.htmllayouts/partials/pagination.htmlLayout:layouts/index.htmlCSS:assets/css/site.cssJavaScript:assets/js/site.js
Accessibility#
- Every container is a named landmark or group: pagination and
filter-chip-listrender as<nav>with anaria-label— supplied by all five currentfilter-row.htmlcallers — andhome-hero-actionscarriesrole="group"with itsaria-label - Selection is announced on the member: toggle chips are
<button>elements whosearia-pressedis kept in sync byinitTableLists()andinitTalksArchive(); a chip rendered as an<a>navigates instead and carries no state - Each member is a separate tab stop and toggles with the native Enter and Space activation of
<button>; no script manages focus or arrow keys - Disabled pagination arrows are
aria-hidden<span>elements outside the tab order, and the previous, next, and page-number links each carry anaria-label - Avoid
role="toolbar": it promises arrow-key navigation between members that these groups do not implement