Here’s the thing about screen readers that most developers never learn until it’s too late: they don’t see what you see. They don’t render pixels. They don’t read CSS. They read the accessibility tree.
And if your HTML is a <div> with a click handler and no label, the accessibility tree has nothing to say. To a screen reader, that <div> doesn’t exist.
That’s the core problem WCAG 4.1.2: Name, Role, Value addresses. And honestly, it’s the single most important criterion for making your interface visible to assistive technology.
The Three Things You Need to Get Right
Every user interface component needs three programmatic properties exposed to the accessibility API:
| Property | What It Is | Why It Matters |
|---|---|---|
| Name | Text that identifies and describes the purpose of the component | Screen readers announce this so users know what they’re interacting with |
| Role | What type of component it is (button, link, checkbox, etc.) | Tells users how the component behaves as each type has its own keyboard interaction expectations |
| Value | The current state or value of the component | Lets users know what’s selected, checked, expanded, etc. |
Miss any one of these, and you’ve rendered the component invisible—or worse, misleading—to assistive technology.
The <div onClick> Trap
Let’s talk about the most common offender in modern development:
<!-- BAD: Invisible to screen readers -->
<div class="button" onclick="handleSubmit()"> Submit Form </div>
Looks fine visually. Has the text. Responds to clicks. But to a screen reader, it’s just a div. No role. No name (unless you explicitly add one). No way to navigate to it with keyboard.
For blind users, this is their entire window into your interface
Here’s the fix:
<!-- GOOD: Proper semantic HTML -->
<button type="submit" id="submit-btn"> Submit Form </button>
Now the role is button, the name is “Submit Form”, and the value is whatever state the button is in (enabled, disabled, focused). Built-in. No extra markup needed.
Semantic elements work because they expose these properties automatically. Custom elements don’t unless you tell them to.
When You Must Use Custom Components
Sometimes you need a custom widget—a dropdown combobox, a tree view, a carousel. Semantic HTML won’t cover that. That’s when ARIA comes in.
<!-- CUSTOM DROPDOWN WITH PROPER ARIA -->
<div role="combobox" aria-expanded="false" aria-haspopup="listbox" id="country-combo">
<input type="text" aria-controls="country-listbox" aria-autocomplete="list">
<ul role="listbox" id="country-listbox" hidden>
<li role="option" aria-selected="true">United States</li>
<li role="option">Canada</li>
<li role="option">Mexico</li>
</ul> </div>
Key things happening here:
role="combobox"— tells the screen reader this is a combo box, not just a divaria-expanded— state that toggles when opened/closedaria-controls— links the input to the listbox it controlsaria-selected— indicates which option is currently chosen
Each attribute fills in a piece of the accessibility tree that would otherwise be missing.
Common Failure Modes
I’ve seen these break in dozens of ways during audits. Here are the most frequent:
Missing Roles
- Failure:
<span onclick="...">Delete</span>withoutrole="button"ortabindex - Impact: Screen reader users can’t find or activate it
- Fix: Use semantic HTML or add
role="button" tabindex="0"
Missing Names
- Failure: Icon-only button with no
aria-labelor visible text - Impact: Screen reader announces “button” with no context
- Fix: Add
aria-label="Delete item"or visible text
Missing Values / States
- Failure: Accordion toggle without
aria-expanded - Impact: Screen reader doesn’t announce whether content is open or closed
- Fix: Toggle
aria-expanded="true/false"on click
Each attribute fills in a piece of the accessibility tree that would otherwise be missing
Non-Semantic Links
- Failure:
<div onclick="navigate('/page')">Read More</div>instead of<a href="/page"> - Impact: Can’t right-click to open in new tab, can’t bookmark, screen reader reads as generic container
- Fix: Always use
<a>for navigation
Testing Your Work
Here’s how to check if you’re exposing name, role, and value correctly:
- Turn off your screen — Use a screen reader (NVDA, JAWS, VoiceOver, TalkBack) and navigate through the page. Does it announce what you expect?
- Inspect the accessibility tree — Most devtools let you view the accessibility tree directly (Chrome DevTools → Elements → Accessibility pane).
- Use automated tools — axe, WAVE, Pa11y, LevelCI catch many 4.1.2 violations automatically.
- Tab through — Can you reach every interactive element with Tab? Can you activate it?
If the answer is no, you’ve got a 4.1.2 violation somewhere.
4.1.2 isn’t a “nice-to-have”
It’s the foundation. Without it, nothing else matters. You can have perfect colors, perfect focus rings, perfect error messages. If the screen reader can’t tell what you’re clicking, you’ve built a house on sand.
For blind users, this is their entire window into your interface. Every label, every button, every checkbox they encounter depends on this. If you get it wrong, they’re lost on your site.
And it’s not just about blind users. Cognitive disability users rely on clear labels. Motor impairment users depend on consistent roles to know how to interact. Keyboard-only users need focusable elements with proper names.
This criterion touches everyone who relies on anything other than a mouse and screen.
Do it Right
Expose the name. Expose the role. Expose the value.
Use semantic HTML whenever possible. It’s free. When you need custom components, use ARIA to fill the gaps. Test with assistive technology regularly.
Your users aren’t asking for bells and whistles. They’re asking for visibility. They want to know what’s on the page, what it does, and what state it’s in.
Give them that. And the rest of accessibility builds on top of it.
One more left in the series after this: 4.1.3 Status Messages. What should I cover after that? Let me know over on LinkedIn or BlueSky!
Discover more from Nat Tarnoff
Subscribe to get the latest posts sent to your email.

Comments are closed.