IconButton
Icon-only action button. Wraps @dt/Button in its rounded icon-only form with an enforced accessible label, sharing Button's variant (weight) x tone (semantic) matrix and surface awareness.
Status: stable. Group: actions. Markdown version: /design-system/components/icon-button.md
When to use
IconButton is an icon-only action for compact surfaces: toolbars, card corners, table rows, dialog close affordances. It wraps Button in its rounded icon-only form, so weight, tone, and surface behave exactly like Button; label is required and becomes the accessible name. A className is applied to an inline-flex wrapper span around the button, not to the button itself.
When not to use
- Do not omit
label; there is no fallback announcement. - Do not use for primary marketing CTAs; text + icon is more discoverable; use
Buttonwith an icon prop. - Do not rely on
tooltipas the only accessible name. - Do not use IconButton for primary marketing CTAs; text plus icon is more discoverable, use Button with an icon prop.
Look-alikes
- Button: Do not use IconButton for primary marketing CTAs; text plus icon is more discoverable, use Button with an icon prop.
Props
| Prop | Type | Default | Description | Required |
|---|---|---|---|---|
icon | ReactNode | string | none | Icon glyph: a rendered element (e.g. <CaretRight />) or a Phosphor icon name (e.g. "x"). | Yes |
label | string | none | Accessible name when aria-labelledby is not set. | Yes |
variant | "primary" | "secondary" | "tertiary" | tertiary | Visual weight. | No |
tone | ButtonProps["tone"] | neutral | Semantic colour, orthogonal to variant. | No |
surface | ButtonProps["surface"] | default | Surface the control sits on; prefer over color overrides on tinted bands. | No |
size | "sm" | "md" | "lg" | md | No description in the contract. | No |
tooltip | string | none | Native tooltip text shown on hover; the label stays the accessible name. | No |
Variants
- Available variants:
primary,secondary,tertiary(defaulttertiary). - Available tones:
neutral,error,warning,success,info(defaultneutral). - Available surfaces:
default,onDark,onBrand(defaultdefault). - Available sizes:
sm,md,lg(defaultmd).
Invalid combinations
The contract records no invalid combinations.
Keyboard
| Key | Behaviour |
|---|---|
| Tab | Focusable. |
| Enter / Space | Activates. |
Rules
Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.
| Level | Rule | How it is checked |
|---|---|---|
| MUST | ARIA: label prop is required and renders as the accessible name; the icon glyph itself is aria-hidden. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: tooltip is hover-only garnish; it never replaces label as the accessible name. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: disabled (and Button's loading state) sets the native disabled attribute on the button, which removes it from the tab order and from pointer interaction; screen readers still expose it as a disabled button when browsing. It does not use aria-disabled. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by npm run test:stories:matrix:ci. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by npm run test:stories:hc. |
| MUST | Every documented keyboard interaction works as specified. | Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-11-04. |
| SHOULD | Write label as the action, like Close dialog or Copy link, never the icon name like X or Clipboard. | Not stated in the contract. |
| SHOULD | Stay on the default variant=tertiary in toolbars and chrome; reserve primary for the one dominant action of a compact surface. | Not stated in the contract. |
| SHOULD | Pick an icon whose meaning survives without text (x, magnifying-glass, caret-*); if you have to explain the icon, use Button with a visible label instead. | Not stated in the contract. |
| SHOULD | Add tooltip for sighted-user reassurance on ambiguous glyphs; the label still carries the accessible name. | Not stated in the contract. |
| SHOULD | Use surface=onDark or onBrand when the control sits on a dark or brand band instead of overriding colors. | Not stated in the contract. |
| SHOULD NOT | Do not omit or auto-generate label; there is no fallback announcement and axe fails the render. | Not stated in the contract. |
| SHOULD NOT | Do not stack more than about five icon-only actions in a row without grouping or labels; recognition drops sharply. | Not stated in the contract. |
Install and import
- Install:
npm install @digitaltableteur/react(version 0.9.1, restricted access on npm). - Import:
import { IconButton } from "@digitaltableteur/react"; - Entry point:
@digitaltableteur/react/actionsexports it too. - Styles: import
@digitaltableteur/react/style.cssonce at the app boundary. - Also available as the custom element
<dt-icon-button>; see the custom elements manifest.
Links
- Machine-readable contract (Design System Contract 1.0)
- Markdown version of this page
- Component index as JSON
- All components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.