Icon button — accessibility

Keyboard

  • Native <button>: Tab focuses, Enter and Space activate. With href it is a native <a> instead: Tab focuses, Enter activates, and it also gets middle-click / "open in new tab" / "save link as" for free.
  • disabled removes it from the tab order (native <button disabled>); with href there is no native way to disable a link, so disabled instead renders <a aria-disabled="true"> with no href — it stays in the tab order but does nothing, so a screen reader user is not routed to a dead end silently. Prefer omitting the action instead of a disabled link where the screen allows it.
  • Default type="button" — pressing Enter inside a form does not submit through it.

ARIA

  • No role needed — native <button> or, with href, native <a>.
  • The button has no text, so the accessible name must come from aria-label (preferred) or title. Without one the button is announced as "button" (or "link") only.
  • Do not rely on the icon's SVG for a name — <Icon> is decorative.
  • pressed renders aria-pressed (acrm !3936) — for toggles (format, panel, template/AI in mail compose). It is not used for a menu trigger's open state, which comes from aria-expanded/aria-haspopup on the trigger instead.

Focus

  • Foundation ring from ds.css: 2px solid var(--accent), outline-offset: 1px, following --radius-sm.
  • No component-specific focus style; hover background is not a focus indicator.

Contrast

Ratios computed from packages/ui/src/tokens/tokens.css:

  • Icon at rest --fg-secondary on canvas: #7d8184 on #ffffff = 3.93:1 (light); #9a9ca1 on #1b1c1f = 6.20:1 (dark). Light theme passes the 3:1 minimum for graphical objects, not AA 4.5:1 for text (the icon is a graphic).
  • Icon on hover --fg: 17.59:1 (light), 14.53:1 (dark).
  • Focus ring --accent on canvas: 4.44:1 (light), 3.83:1 (dark).

Known limits

  • Rest-state icon contrast in light theme is 3.93:1 — meets the non-text minimum only.
  • sm size (24px) meets the 24 × 24 CSS px target minimum exactly, with no margin to spare; keep --sp-2 or more between adjacent sm buttons.
  • No built-in tooltip beyond the native title; touch users get no tooltip at all, so the icon must be unambiguous.

Source: docs/button/icon-button.a11y.md · GitLab