Skip to content

Accessibility

An icon button can be a native <button> or an <a> with no visible text — so its whole accessibility rests on two things: an explicit accessible name that stands in for the missing label, and an icon that stays out of the accessibility tree. Get those right and the native element carries the role, keyboard operation, and focus for free. An accessible icon button meets WCAG 2.1 AA: it names its action for assistive technology, keeps its state announced, and stays operable and legible whether the user sees it, hears it, or tabs to it.

  • The glyph and every state — hover, focus, disabled, and the contrast variant placed over imagery — must maintain accessible contrast on every supported surface. Source: WCAG 1.4.3 Contrast (Minimum).
  • With no text to lean on, meaning rests entirely on the glyph — never let colour alone distinguish one state or action from another; the icon’s shape must carry it. Source: WCAG 1.4.1 Use of Color.
  • Focus styles must stay visible, meet contrast requirements on every supported surface, and be clearly distinguishable from hover. Source: WCAG 2.4.7 Focus Visible.
  • Keep the interactive target at least 44 × 44 px — when the visible glyph is smaller, extend the hit area with padding rather than shrinking the target. Source: WCAG 2.5.8 Target Size (Minimum).
  • Use semantic colour tokens (e.g. foreground/neutral/default) rather than fixed values, so the component adapts correctly across themes and surfaces.

An icon button can be a native <button> or an <a> (link), so it carries the appropriate role, keyboard behaviour, and focus behaviour for each element — never rebuild one from a <div> or <span>. Its one hard requirement is an accessible name: with no visible text, give every icon button an aria-label that names the action or destination. Beyond naming, the wiring depends on what the element does:

  • As a button: A native <button> carries the button role, keyboard activation with Enter and Space activation, and focus behaviour by default.
  • As a link: A native <a> carries the link role, keyboard activation with Enter, and focus behaviour by default. When navigating to an external URL in a new tab or window, the accessible name must clearly inform assistive technology users that context will change (e.g., “Visit Toyota on Instagram, opens in new tab”).

The glyph must stay out of the accessibility tree; those per-case roles, names, and states are in the Labelling elements section below. Keep the interactive target at least 44 px high for comfortable touch use — comfortably clearing the WCAG AA minimum. Source: WCAG 2.5.8 Target Size (Minimum).

Give every element the role, name, and state assistive technology needs.

Button

A native <button>, so the role comes for free. Because there’s no visible text, it has no accessible name of its own — set one with aria-label naming the action (“Close”, “Next slide”), never the icon. When a visible label or persistent tooltip exists, wire the name with aria-labelledby instead.

Link

A native <a>, so the link role comes for free. Give it an aria-label that names the destination, not the icon. When navigating to an external URL in a new tab or window (via target="_blank"), the accessible name must inform users of the context change (e.g., “Visit Toyota on Instagram, opens in new tab”).

Icon

The glyph is decorative — it restates the action or destination the label already names. Mark it aria-hidden="true" so it never announces as a separate node, leaving the button’s or link’s aria-label as the single accessible name.

Toggle / pressed state

A button that flips between two states (bookmark on/off, mute/unmute) keeps aria-pressed in sync — "true" when active, "false" when not. A disclosure button that shows or hides content uses aria-expanded the same way. Keep the aria-label stable across the toggle so it never contradicts the glyph.

Disabled

Use the native disabled attribute on buttons rather than a class or aria-disabled alone, so the button leaves the tab order and announces as unavailable. Keep the button in place rather than removing it when the action is only temporarily unavailable, so its position stays discoverable.

Source: WCAG 4.1.2 Name, Role, Value.