Description
Triggers an action using a single icon with no visible text label. A visually hidden label and tooltip ensure the action remains accessible and discoverable.
Table row actions (edit, delete, archive), toolbar controls, card overflow menus, inline list item actions such as pinning a stakeholder or removing a tag.
Dense data-heavy views in Open Point — stakeholder lists, consultation registers, document tables — need actions that do not consume horizontal space. IconButton preserves layout density while keeping interactions reachable.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Container | Required | Square or circular hit area sized to meet the 44x44px minimum touch target. Background is transparent at rest; fills on hover and active states. |
| Icon | Required | A single wa-icon rendered at 20px (default) or 16px (dense). Must have sufficient contrast against its background in all states. |
| Visually hidden label | Required | Screen-reader-only text provided via aria-label that names the action. Never omit — icon alone is not sufficient for assistive technology. |
| Tooltip | Recommended | A wa-tooltip wrapping the button exposes the label on hover and keyboard focus for sighted users who may not recognise the icon. Omit only when the icon is universally understood in context (e.g., a close X inside an already-labelled dialog). |
| Focus ring | Required | 3px solid ring using --op-color-interactive-focus, offset 2px. Always visible on :focus-visible; never suppressed. |
Variants
Default
Neutral action with no inherent positive or negative valence
General-purpose actions in toolbars or table rows — editing a record, opening a detail panel, copying a link.
Danger
Destructive or irreversible action
Deleting a stakeholder record, removing a consultation participant, discarding unsaved changes. Renders in --op-color-status-error to signal risk.
Ghost
Low-emphasis action that should recede visually
Secondary actions adjacent to a primary interactive element — collapsing a sidebar, toggling a filter panel, dismissing a non-critical notification.
States
| State | Behaviour |
|---|---|
| Default (rest) | Transparent background. Icon at --op-color-text-secondary. No border. |
| Hover | Background fills to --op-color-bg-primary (subtle tint). Icon shifts to --op-color-text-primary. Tooltip appears after a 300ms delay. |
| Focus-visible | 3px focus ring at --op-color-interactive-focus with 2px offset. Tooltip appears immediately on keyboard focus so the label is visible without requiring hover. |
| Active (pressed) | Background deepens to --op-color-interactive-default at low opacity (approx 12%). Icon returns to --op-color-text-secondary momentarily. |
| Disabled | Icon and container at 40% opacity. pointer-events: none. aria-disabled='true' on the button element so focus is still reachable and screen readers announce the disabled state. |
| Loading | Icon is replaced by a wa-spinner at the same size. aria-busy='true' set on the button. Interaction is blocked until the action resolves. |
Usage guidelines
When to use
- Space-constrained rows or toolbars where a full text button would break the layout — e.g., the actions column of a stakeholder table.
- Repeated identical actions across a list where the surrounding context already names the subject — e.g., a trash icon in each row of a document list.
- Toolbar controls where iconography is well-established and a visible label would create visual clutter — e.g., bold, italic, link in a rich text editor.
When not to use
- When the icon alone may be ambiguous to the user — use a full Button with a text label instead.
- As the sole call-to-action on a page or in a form — use a primary Button so the action is unmistakable.
- In empty-state or onboarding contexts where discoverability matters — use a labelled Button to guide unfamiliar users.
- When three or more icon actions appear together without grouping — use an overflow menu (ButtonMenu) to reduce cognitive load.
Do / Don't
Do
Always provide aria-label that names the specific action and its subject — e.g., 'Edit stakeholder Jane Smith'.
Don't
Do not use generic labels like 'Button' or 'Click here'. Screen reader users navigating by form controls need unique, descriptive names.
Do
Wrap the IconButton in a wa-tooltip so sighted keyboard and mouse users can discover the action label.
Don't
Do not rely on title attributes for tooltips — they are not announced reliably by screen readers and are inaccessible to keyboard-only users.
Do
Use the danger variant for destructive actions and pair it with a confirmation dialog before executing irreversible operations.
Don't
Do not use a red icon on the default variant as an ad-hoc danger signal — use the proper danger variant so the semantic intent is consistent.
Do
Keep icon size at 20px default (or 16px dense). Scale the container, not the icon, to meet touch target requirements.
Don't
Do not inflate the icon to fill the touch target — oversized icons reduce legibility at a glance in dense tables.
Layout & Spacing
Container size (default): 44x44px to meet touch target minimum Container size (dense): 32x32px — only use in mouse-primary desktop contexts confirmed via pointer media query Icon size (default): 20px Icon size (dense): 16px Internal padding: icon is vertically and horizontally centred within the container Gap between adjacent icon buttons in a toolbar: --op-space-4 (4px) Border radius: --op-radius-md (8px) default; --op-radius-pill (999px) for circular variant
Tokens
| Part | Token | Value |
|---|---|---|
| Container background (rest) | transparent | No background at rest to reduce visual noise in dense table rows. |
| Container background (hover) | --op-color-bg-primary | Subtle fill on hover to confirm interactivity without competing with row-level hover states. |
| Container background (active) | --op-color-interactive-default | Applied at ~12% opacity on mousedown/keydown. |
| Icon colour (rest) | --op-color-text-secondary | Secondary text colour keeps icon buttons visually subordinate to primary content. |
| Icon colour (hover/focus) | --op-color-text-primary | Shifts to full contrast on interaction to confirm affordance. |
| Icon colour (danger variant) | --op-color-status-error | Applied at rest and on hover for the danger variant. |
| Focus ring | --op-color-interactive-focus | 3px solid, 2px offset. Never override or suppress. |
| Disabled opacity | opacity: 0.4 | Applied to the entire button element. Do not change individual child token values. |
Engineering notes
- The component is built on the native HTML
- Wrap with wa-tooltip and set hoist on the tooltip to prevent clipping inside overflow:hidden table cells.
- Use aria-label to name the action — include the row subject when the button appears in a repeating list context (e.g., 'Archive consultation: Draft Coastal Strategy 2026').
- For the loading state, replace the icon slot content with
and set aria-busy='true' on the button. Restore icon on completion. - For the danger variant, add a data-variant='danger' attribute or a BEM modifier class and style via CSS custom property — do not use inline styles.
- Dense (32px) variant should only render when (pointer: fine) media query is true. Provide the 44px default as the baseline and progressively reduce.
- When multiple IconButtons appear in a table row, group them in a so screen readers announce them as a related set and arrow-key navigation is available.
Keyboard interaction
Key Action Tab Moves focus to the IconButton. Focus ring appears immediately. Enter Activates the button action. Space Activates the button action (matches native button behaviour). Escape Dismisses the tooltip if open. Does not activate the button. Why it matters
Government digital services must meet WCAG 2.1 AA. IconButtons are high-risk for accessibility failures because a missing or generic aria-label makes the action invisible to screen reader users — including those relying on assistive technology required under the Australian Government Digital Service Standard and the AODA in Canada.
Focus
Focus is managed by the browser native tab order. Do not set tabindex="-1" unless the button is inside a widget that manages its own focus (e.g., a toolbar using arrow-key navigation). When inside a role="toolbar", use roving tabindex: only the active button has tabindex="0"; all others have tabindex="-1". Arrow keys move focus within the toolbar; Tab exits it.
ARIA
Role or attribute When to use Example aria-labelRequired. Names the action and, in list contexts, the subject it acts on. <button aria-label="Edit stakeholder Wiremu Tane">...</button>aria-disabledUse instead of the HTML disabled attribute when the button must remain focusable (e.g., to allow a tooltip explanation of why it is disabled). <button aria-disabled="true" aria-label="Archive consultation (read-only access)">...</button>aria-busySet to true on the button during a loading state so screen readers announce the pending state. <button aria-busy="true" aria-label="Saving...">...</button>aria-pressedUse on toggle icon buttons (e.g., pin/unpin a stakeholder) to communicate the current pressed state. <button aria-pressed="true" aria-label="Unpin stakeholder">...</button>role="toolbar"Apply to the container element when two or more related IconButtons appear together. Enables arrow-key navigation between buttons. <div role="toolbar" aria-label="Stakeholder row actions"><button ...>...</button></div>Contrast
Icon colour at rest (--op-color-text-secondary) must achieve at least 3:1 contrast against its background per WCAG 1.4.11 (Non-text Contrast). Icon colour on hover/focus (--op-color-text-primary) must achieve 3:1 or greater. Danger variant icon (--op-color-status-error) must achieve 3:1 against --op-color-bg-primary. Focus ring (--op-color-interactive-focus) must achieve 3:1 against both the component background and the surrounding page background. Do not convey meaning through colour alone — the icon shape itself must communicate the action independent of colour.
Touch targets
The default 44x44px container meets WCAG 2.5.5 (Target Size). The dense 32px variant falls below this threshold and must only be used in pointer-fine (mouse/trackpad) contexts via a CSS media query. Never serve the dense variant to touch devices.
Things to avoid
- Using the title attribute as the accessible name — not announced reliably by screen readers and inaccessible to keyboard-only users.
- Suppressing the focus ring with outline:none or equivalent overrides.
- Placing an
inside the button with no alt attribute — use
or an inline SVG with aria-hidden='true' instead. - Using colour alone to distinguish the danger variant from the default — always use a distinct icon shape or additional visual treatment.
- Executing a destructive action immediately on click without a confirmation step — always interpose a confirmation dialog for irreversible operations.