axe-core presentation-role-conflict error focusable content bug
Fixes the axe-core presentation-role-conflict error by removing the clash between presentational roles and focusable children. Use it when axe flags focusable content inside a presentation or none role. Not for focus inside aria-hidden regions, which is the aria-hidden-focus rule.
axe-core presentation-role-conflict error focusable content bug - how to fix it
TL;DR
An element with role presentation or none must not contain focusable content: either remove the presentational role or move the focusable children out. One line of why: presentation tells assistive tech to ignore the element's semantics, but a focusable child still needs semantics, and the two instructions contradict each other.
The error, verbatim
{
"id": "presentation-role-conflict",
"impact": "minor",
"help": "Elements with presentation role must not have focusable content",
"nodes": [
{ "target": [".icon-list[role=presentation]"], "failureSummary": "Fix any of the following: Element has focusable content" }
]
}Fix it step by step
Step 1: Reproduce on one page
npx @axe-core/cli https://example.com --rules presentation-role-conflict --save axe-pres.jsonExpected: Violations array contains presentation-role-conflict with the presentational containers.
Step 2: See what is focusable inside
node -e "const r=require('./axe-pres.json'); r.violations[0].nodes.forEach(function(n){console.log(n.target.join(' '))})"Expected: Selectors for the conflicting containers.
Step 3: Find them in source
rg -n 'role=.presentation|role=.none' src --glob '*.{jsx,tsx,vue,html}' | head -20Expected: Shows each presentational role usage to triage.
Step 4: Resolve the conflict and re-scan
npx @axe-core/cli https://example.com --rules presentation-role-conflictExpected: Exit code 0, 0 violations. The list semantics are either properly exposed or the container is truly presentational with no focusable children.
Step 5: Gate the rule in CI
npx @axe-core/cli https://example.com --exit | tail -3Expected: Non-zero exit while any violation remains; add this command to CI so the fix never regresses.
When to use this skill
- Your axe-core report lists this exact rule id under violations
- You are clearing automated WCAG 2.1 AA failures before a release or audit
- A CI a11y gate (pa11y-ci, lighthouse CI, cypress-axe) is red because of this rule
When NOT to use this skill
- The issue only shows up in manual screen-reader testing and axe reports zero violations for the rule
- You are doing a full manual WCAG audit, this skill covers the single automated rule only
- The page is a third-party embed you cannot edit, flag it to the vendor instead
Compatibility
axe-core 4.8+ (rule presentation-role-conflict, WCAG 1.3.1 / 4.1.2). Same rule in axe DevTools. Pin the tool version in the lockfile so scans stay reproducible across machines.
Variant phrasings
element has focusable content presentation role
The failureSummary wording, same fix.
axe role none contains focusable element
Same conflict with the none spelling.
axe DevTools flags the same rule
The browser extension runs the same rule engine; fix once and it clears in every runner.
Why it happens
Authors put role presentation on lists and tables used for layout, then later add links or buttons inside them, creating the conflict. The role was meant to strip redundant semantics from layout tables, but once interactive content lives inside, the semantics are not redundant anymore. Axe flags any focusable descendant of a presentational element because browsers resolve the conflict inconsistently. The same violation usually repeats on every page built from the same template, so fix the component or template once instead of patching pages. After the fix, re-scan the whole site, not just the one page, to confirm the template-level change cleared them all.
Edge cases
- If the list is real content, drop role presentation entirely instead of fighting it.
- role none and role presentation are equivalent, the rule treats them the same.
- A presentational wrapper around a single focusable icon button is a sign the wrapper should not exist.
- Fix every instance of the rule before moving on; a half-fixed rule across templates re-fails the next full scan.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_vfLywCUgXbN7Jdpz-2PoJg