Use an icon button for frequent, well-known actions where space is tight: row actions in the work-order table, toolbar actions, the notification bell. If the icon isn't instantly recognizable to a new dispatcher, use a button with a text label instead.
Name every icon button with aria-label using verb + object, including the item in lists ("Edit work order WO-4182", not "Edit").
Pair with a tooltip that shows the same text (tooltip component) — sighted mouse users need the name too.
Square is the default; circle is for overflow ("more") and floating actions.
Toggle icon buttons (pin, mute) keep their label and switch aria-pressed.
Anatomy
Container — <button type="button" class="icon-btn" aria-label="…">, square of the button height
Icon — svg.icon-btn__icon, aria-hidden="true"
Badge (optional) — .icon-btn__badge, decorative; the count lives in the accessible name
Examples
States marked hover, focus-visible and active are frozen with .is-hover, .is-focus-visible, .is-active. Those classes share their rule with the real pseudo-class, so what you see is exactly what users get. Never use them in product code.
Icon button
32px — dense tables and toolbars.
40px — default.
48px — tablet screens used with gloves.
Close the 2 open work orders before archiving this van.
Toggle icon buttons keep the same label and use aria-pressed.
<button type="button" class="icon-btn icon-btn--outline" aria-label="Edit work order WO-4182">
<svg class="icon-btn__icon" aria-hidden="true" focusable="false"><use href="#icon-pencil"></use></svg>
</button>
<!-- Circle, small, large, emphasis -->
<button type="button" class="icon-btn icon-btn--circle" aria-label="More actions for van KX-219">…</button>
<button type="button" class="icon-btn icon-btn--sm" aria-label="Remove filter: Depot North Yard">…</button>
<button type="button" class="icon-btn icon-btn--lg icon-btn--primary" aria-label="Start inspection">…</button>
<!-- Toggle: same label, state in aria-pressed -->
<button type="button" class="icon-btn icon-btn--outline" aria-label="Pin van KX-219" aria-pressed="true">…</button>
<!-- Count belongs in the name; the badge is decorative -->
<button type="button" class="icon-btn icon-btn--outline" aria-label="Notifications, 3 unread">
<svg class="icon-btn__icon" aria-hidden="true" focusable="false"><use href="#icon-bell"></use></svg>
<span class="icon-btn__badge" aria-hidden="true">3</span>
</button>
API
Hook
Values
Purpose
.icon-btn
block class
Ghost icon button (default emphasis), square
.icon-btn--square | --circle
modifier
Shape
.icon-btn--outline | --primary
modifier
Emphasis: secondary outline, primary fill
.icon-btn--sm | --lg
modifier
32 / 48px (md 40px default), same as .btn--sm|lg
.icon-btn__icon, .icon-btn__badge
part
Icon and decorative count
aria-label
attribute (required)
Accessible name
aria-pressed
attribute
Toggle state (selected colors + border)
disabled / aria-disabled="true"
attribute
Unavailable; aria-disabled stays focusable and explains why
.is-hover | .is-focus-visible | .is-active
docs-only class
Freeze a state
Do and don't
Do name the action and the object in aria-label.Don't use ambiguous icons or generic names; when in doubt, show a text label.
Accessibility
Keyboard interaction
Key
Behavior
Tab
Moves focus to the button (skipped when disabled)
Enter / Space
Activates; toggles aria-pressed for toggle buttons
Role: native <button>. Name: aria-label (the lint in ds check flags icon-only buttons without one).
Badges: put the count in the name ("Notifications, 3 unread"), hide the visual badge with aria-hidden.
Target size: 32px minimum (WCAG 2.5.8 needs 24px); use --lg on tablet screens.
Pressed state is shown by fill and border, never color alone; forced colors use Highlight.