Elements must only use allowed ARIA attributes
An aria-* attribute isn't permitted for that element's role, so the browser drops it and the widget's state never reaches a screen reader.
What you see
Elements must only use allowed ARIA attributes (aria-allowed-attr) Fix any of the following: ARIA attribute is not allowed: aria-checked="true" <div aria-checked="true" class="toggle">Email alerts</div>
What’s actually happening
axe flags an element carrying an aria-* attribute that its role doesn't support. The browser computes the accessibility tree, sees the attribute isn't valid for that role, and ignores it. So a custom toggle built as <div aria-checked="true"> announces no checked state at all — the screen reader reads "Email alerts" with no on/off, and the user has no idea it's a control. The classic trip-up: a bare div has no role, and an element with no role only accepts the global ARIA attributes, so aria-checked there is invalid even though aria-label on the same div would be fine.
Common causes
- aria-checked, aria-selected, or aria-expanded on a plain <div>/<span> that was never given the role those states belong to
- A role and a state that don't match — aria-expanded on role="button" is allowed, but aria-checked on role="link" is not
- Copying ARIA from a similar-looking widget without checking which states that role actually permits
- A role typo (role="checkbutton") that leaves the real role unset, so its states read as disallowed
- Framework props that emit aria-* attributes regardless of the role the component ends up rendering
How to fix it
- Add the role the attribute belongs toIf the element genuinely is a checkbox, give it role="checkbox" so aria-checked becomes valid — and then add everything else that role requires (a checkbox needs aria-checked set and keyboard support). The attribute was never wrong; the missing role was.
- Use the native element and skip ARIA entirely<input type="checkbox"> exposes checked state, focus, and keyboard handling for free and can't trigger this rule. Reach for role + aria-checked on a div only when no native control fits the design.
- Check the role's allowed states in the ARIA in HTML specEvery role lists which states and properties it supports, plus the global attributes any element accepts (aria-label, aria-hidden, aria-describedby). Match each aria-* you set to that list. Anything outside it gets dropped silently — no visible error, just a control that doesn't announce.
- Remove ARIA that's there by accidentSometimes the fix is deletion. If a wrapper picked up aria-selected from a copied snippet and isn't actually a tab or option, strip it. An unsupported attribute does nothing useful and only adds noise to the next audit.
Stop it recurring
Build interactive widgets from a vetted component library or headless primitive that pairs each role with its allowed states, instead of hand-adding aria-* to divs.