Ever had that moment where you slap an ARIA role on an element, only to find your screen reader ignores it or reads something totally unexpected? You double-check your markup, and then notice you also have some nested roles, or conflicting aria attributes nearby. What’s going on?
I ran into this while fixing accessibility bugs on a client site. A button inside a custom widget had role="button" but also aria-haspopup="menu" , and the screen reader would announce "menu" instead of "button." Digging in, I realized that the way browsers handle ARIA roles and properties isn’t just about what you explicitly set on an element. There’s a whole system of inheritance, overrides, and conflict resolution happening under the hood.
Let’s talk about how browsers actually resolve ARIA roles and properties when there are conflicts, and what that means for you when debugging accessibility issues.
The tangled web of ARIA roles and properties
ARIA roles describe what an element is to assistive technology. For example, role="button" tells a screen reader this element behaves like a button. Other roles include menu, checkbox, dialog, and so on.
But ARIA also has properties and states, like aria-haspopup, aria-checked, or aria-expanded, that modify or add semantic information.
The confusion starts when you put multiple roles or conflicting properties on the same element, or when nested elements define roles that might clash.
Here’s the kicker: an element can only have one computed role. While you can set multiple roles explicitly in your markup (like via role="button menu", which is invalid), browsers use a priority and inheritance system to pick one.
How browsers pick the winning ARIA role
The spec gives some guidelines, but browser implementations differ subtly. Here’s the gist:
-
Explicit role attribute wins: If you set
roleon an element, that role is the first candidate. -
Implicit role fallback: If no explicit role is set, browsers may infer a role based on the tag name and attributes (like
<button>implicitly has role="button"). -
Special roles override generic ones: Some roles have higher precedence. For example,
alertoverridesregion. - ARIA role inheritance: Roles do not literally inherit like CSS properties, but some ARIA properties do affect descendants’ semantics.
For example, if you have role="menu" on a container and inside it a child with role="menuitem", the child’s role is explicit and independent.
What about conflicting ARIA properties?
This is where things get tricky. Properties like aria-haspopup can influence what screen readers announce.
In my example, adding aria-haspopup="menu" to a button tells the screen reader the button controls a menu , sometimes causing it to announce "menu" or "button with menu." But if you also set role="menu" on the same element, the role wins and it treats the element as a menu, not a button.
In other cases, properties can override or augment roles:
-
aria-checkedon a checkbox or switch controls the checked state. -
aria-expandedon a disclosure widget signals whether it’s open.
But if the role and the ARIA properties don’t align, screen readers may behave unpredictably.
Under the hood: the accessibility tree and role resolution
The secret sauce is the accessibility tree , a parallel structure browsers build from the DOM, CSS, and ARIA attributes that assistive tech consumes.
When the browser parses the DOM:
- It looks for explicit roles on elements.
- If absent, it infers implicit roles.
- It collects ARIA properties, validating them against the role.
- It resolves conflicts:
- Multiple roles aren’t allowed; the browser picks one by priority or last-known override.
- Conflicting properties may be ignored or cause fallback behavior.
This process can differ slightly between browsers and screen readers, which is why your accessibility testing must include multiple platforms.
Debugging tips: How to see the computed ARIA role and properties
Feeling lost? Here’s how to peek under the hood:
- Use browser devtools accessibility pane: Chrome and Firefox have accessibility inspectors that show the computed role, name, states, and properties for any element.
- VoiceOver or NVDA output: Listen carefully for what role and state the screen reader announces.
- Accessibility tree snapshots: Tools like the Accessibility Object Model (AOM) inspector or axe-core can reveal discrepancies.
For example, in Chrome DevTools:
- Right-click your element and inspect.
- Go to the "Accessibility" tab.
- See the "Computed Properties" , it shows the effective role and ARIA states.
If the computed role isn’t what you expect, check for conflicting role attributes on the same or ancestor elements.
Common pitfalls and how to avoid them
-
Don’t mix roles on the same element: Stick to one explicit
role. If you need nested roles, use child elements. -
Align ARIA properties with the role: Don’t add
aria-haspopup="menu"on an element withrole="menu"; it’s redundant and confusing. - Use native semantics when possible: Native HTML elements have implicit roles and states that assistive tech understands well.
- Test with multiple screen readers: Behavior varies, so verify your ARIA usage under real conditions.
Why does this matter?
Misusing ARIA roles and properties can confuse users relying on assistive technology. Overriding or conflicting roles may cause screen readers to announce the wrong widget type, breaking navigation and interaction.
Understanding how browsers resolve these conflicts helps you write cleaner, more predictable accessible code.
Next time your ARIA role isn’t behaving as expected, remember it’s not just the markup but how the browser builds the accessibility tree and resolves those roles and properties. Use the tools to inspect what’s really going on, and keep your roles clear and consistent.
Accessibility isn’t just about adding attributes , it’s about making sure those attributes speak clearly and consistently to the user’s assistive technology.
Helpful learning resources
- W3C WAI accessibility guidance
- W3Schools accessibility tutorials
- MDN Web Docs Originally published at Under The Hood. Get the next deep dive in your inbox: subscribe to Under The Hood.
Top comments (0)