4.1.2 Level A Guideline 4.1 Compatible Added in WCAG 2.0 Partly detectable automatically

Name, Role, Value

For all user interface components, the name and role can be determined programmatically; states, properties, and values can be set by the user and programmatically determined; and changes to them are notified to user agents.

Who this affects

Screen reader users, who are told what a control is, what it is called, and what state it is in entirely through this information. A custom toggle built from a div announces as nothing at all.

The failure and the fix

Each example below is a self-contained page, loaded in a frame so its markup cannot affect this page. Open either one on its own to test it with a keyboard or screen reader.

Fails 4.1.2

Open the failing example in a new tab

fail.html — the problem

<h1>Account settings</h1>

<!-- FAILURE 1: a toggle switch with no name, no role and no state.
     A screen reader announces absolutely nothing here. Even sighted keyboard
     users cannot reach it. The visual state lives only in a data attribute
     and a CSS rule, neither of which assistive technology can read. -->
<p>
  <span class="switch" data-on="false" onclick="this.dataset.on = this.dataset.on === 'true' ? 'false' : 'true'"></span>
  Marketing emails
</p>

<!-- FAILURE 2: an icon-only button whose accessible name is empty.
     Announced as just "button". The title attribute is unreliable: it is not
     announced by every screen reader and never appears for touch users. -->
<button type="button" class="button button--secondary" title="Delete account">
  <span aria-hidden="true">🗑</span>
</button>

<!-- FAILURE 3: a custom accordion. The header is a div, so it is neither
     focusable nor announced as a button, and nothing exposes whether the
     section is currently open or closed. -->
<div class="acc-head" onclick="togglePanel()">Privacy options</div>
<div id="panel" hidden>
  <p>Your data is never sold.</p>
</div>

<!-- FAILURE 4: an ARIA role applied without the states it requires.
     role="checkbox" promises aria-checked; without it, assistive technology
     reports a checkbox in an undefined state. A role is a contract. -->
<p>
  <span role="checkbox" tabindex="0">Show my reviews publicly</span>
</p>

Meets 4.1.2

Open the passing example in a new tab

pass.html — the fix

<h1>Account settings</h1>

<!-- FIX 1: a real button carries the role. aria-pressed carries the state and
     is kept in sync in script, so the switch announces as
     "Marketing emails, toggle button, pressed". The visual state and the
     programmatic state are driven by the same attribute, so they cannot drift. -->
<p>
  <button type="button" class="switch-btn" aria-pressed="false" onclick="toggleSwitch(this)">
    <span class="switch-track" aria-hidden="true"></span>
    <span>Marketing emails</span>
  </button>
</p>

<!-- FIX 2: the icon is hidden from assistive technology and the name comes
     from visually hidden text, so it announces as "Delete account, button". -->
<button type="button" class="button button--secondary">
  <span aria-hidden="true">🗑</span>
  <span class="visually-hidden">Delete account</span>
</button>

<!-- FIX 3: the accordion header is a real button. aria-expanded exposes open
     or closed state, and aria-controls points at the region it governs. -->
<h2>
  <button type="button" class="acc-btn" aria-expanded="false" aria-controls="panel" onclick="togglePanel(this)">
    Privacy options
  </button>
</h2>
<div id="panel" hidden>
  <p>Your data is never sold.</p>
</div>

<!-- FIX 4: use the native checkbox. It supplies role, state and keyboard
     behaviour with no script at all. Reach for ARIA only when HTML has no
     element that already does the job. -->
<p class="field">
  <input type="checkbox" id="public-reviews">
  <label for="public-reviews">Show my reviews publicly</label>
</p>

How to test it

For every control, inspect the accessibility tree for an accessible name, a correct role, and accurate state. Custom widgets — dropdowns, tabs, accordions, toggles, modals — are where this fails. Using a native button or input passes for free.

  • Automated

    Scanners catch missing accessible names, invalid ARIA roles and attributes, and forbidden role and attribute combinations. They cannot confirm that state stays accurate as the user interacts, which is the harder half.

    Relevant axe rules:

    • button-name
    • link-name
    • aria-roles
    • aria-valid-attr
    • aria-valid-attr-value
    • aria-required-attr
    • aria-allowed-attr
    • aria-allowed-role
    • aria-hidden-focus
    • aria-toggle-field-name
    • aria-command-name
    • aria-input-field-name
    • aria-meter-name
    • aria-progressbar-name
    • aria-tooltip-name
    • aria-dialog-name
    • frame-title
    • nested-interactive
  • Keyboard

    Unplug the mouse. Move through the page with Tab, Shift+Tab, Enter, Space and the arrow keys. See the keyboard testing script.

  • Screen reader

    Listen to both examples with NVDA, JAWS, VoiceOver or TalkBack and compare what is announced. See the screen reader cheat sheets.

  • Visual

    Zoom to 200% and 400%, narrow the viewport to 320 px, and apply the text-spacing overrides. See the visual testing procedures.

In the specification