Skip to content
onsystemv0.6.0

Rule

icon-button-accessible-name

error by default

Buttons whose only content is an icon need an aria-label (or visually hidden text).

What it catches
Buttons whose only content is an icon (from an icon library or icons module, or named like one: XIcon, Trash2), with no aria-label, aria-labelledby, title or visually hidden text. A child with text in its props (<IconMenu text=…>) or an unknown component makes it skip; a button passed as Base UI's render, conditionally too, is judged by its own and its host's children, a render-prop child ({() => <Trash2 />}) by what it returns, and hidden buttons and a story's args.render are skipped
Why it matters
A button with only an icon is announced as just "button" to screen-reader users. It is the most common accessibility slip in real apps: check found 11 in vercel/ai-chatbot and 105 in midday's dashboard. The fix guesses the label from the icon (Trash2 → "Delete").

Example

What check_ui returned for this code against the demo design system when the site was built, from version 0.6.0.

What the agent wrote
<Button variant="ghost" size="icon">  <Trash2 /></Button>
After the fixthe rule’s own fixes
<Button aria-label="Delete" variant="ghost" size="icon">  <Trash2 /></Button>
check_ui
  1. error1:2fix included

    Icon-only <Button> has no accessible name. Add aria-label="Delete" describing the action, or visually hidden text.

How the fix is chosen

aria-label, guessed from the icon (Trash2 → "Delete").

Every finding in the example carries its fix as text edits, which the agent applies, and so does eslint --fix. The code after the fix is those edits, applied.

Options

Its default severity is "error". Set it to "off", "warn" or "error" in onsystem.config.json, or as onsystem/icon-button-accessible-name in the ESLint plugin.

onsystem.config.json
{  "rules": {    "icon-button-accessible-name": "warn"  }}

The config file covers the rest, including overrides for folders.

Suppressing a finding

When a finding is intended, a comment turns the rule off for the next line and says why after --. Checked when the site was built: the comment hides the finding on the line below it, and the others stay.

With the comment
// onsystem-disable-next-line icon-button-accessible-name -- why this one is intended<Button variant="ghost" size="icon">  <Trash2 /></Button>

onsystem-disable-next-line covers the next line, onsystem-disable-line its own, and /* onsystem-disable */ the rest of the file (at the top, the whole file). Without rule names they turn every rule off. A misspelled rule name is reported as a suppression warning, with the closest one, and suppresses nothing; syntax errors cannot be suppressed. For whole folders or kinds of files, such as legacy screens or generated code, use overrides in the config.