Name, Role, Value
Every interactive element must expose three things to assistive technology: what it's called, what kind of control it is, and what state it's in. Get any of those wrong and screen-reader users get a broken UI.
What this requires
Every UI component must programmatically expose its name (what the
control is called), its role (what kind of widget it is — button,
link, checkbox, tab, dialog), and its value or state (checked,
expanded, selected, current, disabled). Native HTML elements ship with
all three for free. Custom widgets built from <div> and <span> need
ARIA to fill the gap, and the contract has to be honoured: the role
implies a set of expected behaviours and state attributes, not just a
label.
How AI coding tools fail this
The most frequent failure: AI assistants reach for ARIA when native
HTML would have worked. A <div role="button" aria-label="Save"> with
an onClick handler costs more code than <button>Save</button> and
gets keyboard handling, focus, role, and accessible name automatically.
The AI-generated version typically forgets at least one of those.
The second pattern: custom widgets built with the right role but
without the matching state updates. A toggle gets role="switch" but
never sets aria-checked. An accordion gets aria-expanded on the
trigger but doesn't update it when the panel opens. A tab list gets
role="tablist" but each tab is missing aria-selected. Screen
readers announce the wrong state, the wrong number of items, the wrong
position in a set.
The third: invented patterns. AI tools occasionally assemble custom keyboard handling, focus management, and ARIA from scratch when ARIA APG already specifies the canonical pattern. The result is technically labelled but doesn't behave like any control the user has met before.
Edge cases
- Form fields need an associated
<label>element usinghtmlFor, oraria-labelledbypointing at the label, oraria-labelas a last resort. Placeholder text alone fails — it disappears on input. - Icon-only buttons need
aria-labelfor the name. The icon itself should bearia-hidden="true"so it isn't double-announced. - No ARIA is better than bad ARIA. A wrong role is worse than no role; a missing required state attribute is worse than not declaring the role at all.
- Custom widgets should follow the exact ARIA APG pattern for that type — the keyboard interactions, focus management, and required attributes are all part of the contract that comes with the role.
- Dynamic content changes (loading states, error messages,
notifications) often need
aria-liveregions so screen readers announce the change without user navigation.
How Jeikin handles this
Jeikin's CLI scanner flags <div onClick> and other non-interactive
elements with click handlers, then maps each finding to WCAG 4.1.2 and
2.1.1. When a custom widget declares an ARIA role, the scanner checks
that the required state attributes for that role are present and being
updated.
The MCP server returns the canonical ARIA APG pattern for the widget type, so when an AI assistant is about to invent a custom dropdown or tab list, it gets the spec-compliant template instead.