( Robust / WCAG 4.1.1 )

Duplicate id on active, focusable elements

SeriousLevel AWCAG 4.1.1 — Parsing

What is this issue?

Two or more elements that are natively focusable (<a>, <button>, <input>, <select>, <textarea>) or made focusable with tabindex="0" or tabindex="-1" share the same id attribute value. This is a narrower, more consequential case of the general duplicate id failure, specifically for elements a user directly interacts with rather than passive content.

Because id values are required to be unique across the whole page under the HTML specification, any lookup that resolves this id (a <label for>, an in-page anchor link, a piece of JavaScript keying off it) resolves to whichever of the two duplicated interactive elements the browser encounters first, not necessarily the one the user is actually focused on or trying to reach.

Why does this matter?

An id collision on a heading or a paragraph is a labeling bug someone might trace back eventually. An id collision on a button or a form field is different in kind: it directly affects the mechanics of interaction itself. A <label for="email"> pointing at two different inputs both named id="email" connects to only the first one: clicking the second input’s label moves focus and click-target behavior to the wrong field entirely, not just the wrong description text.

This compounds with anything that automates interaction with the page. Browser autofill, password managers, and end-to-end test scripts frequently locate form fields by id, and a duplicate on an interactive element means these tools can just as easily target, fill, or click the wrong one of the two duplicated elements, a failure mode a purely visual page review won’t reveal at all.

Who is affected?

  • Keyboard users: click a visible <label> expecting focus to move into its associated input, and instead land on a different, duplicate-id element entirely, or find that focus behaves inconsistently between the two colliding elements.
  • Screen reader users: hear a name or state announced for one interactive element that was actually intended for its duplicate elsewhere on the page, since label[for] and similar id-based lookups resolve to only the first match.
  • Motor impairments: anyone relying on switch access, voice control, or another input method that targets controls by their programmatic identity is especially exposed, since the collision affects the underlying targeting mechanism itself, not just what’s visually rendered.

What users experience

Farah has cerebral palsy and uses a mouth-operated joystick with on-screen keyboard software that lets her Tab between form fields rather than pointing precisely at small targets. On a multi-step checkout form, both the shipping-address “Apartment” field and the billing-address “Apartment” field were built from the same copy-pasted component and share id="apt-number". She fills in her shipping apartment number, and when she tabs to what she believes is the billing apartment field’s associated label and clicks it to confirm which field has focus, focus jumps back to the shipping field instead, because the label’s for="apt-number" resolves to the first matching input on the page. She submits the order uncertain whether her billing apartment number was ever actually entered.

How do I fix it?

Give every focusable element on the page its own unique id, generated in a way that guarantees uniqueness even when the same interactive component renders more than once. This works because it restores a clean one-to-one mapping between each id and exactly one interactive element, so every label, anchor, and script-based lookup resolves to the specific control the user is actually working with.

This failure is most common in forms and repeated interactive components (shipping/billing address pairs, a list of editable rows, a set of accordion toggle buttons) where the same markup template is reused with a hardcoded id. Fix it at the template level, deriving each instance’s id from a stable per-instance value like a loop index or a field name prefix (shipping-apt, billing-apt), rather than patching individually rendered copies.

Code Examples

Before
<button id="submit-btn">Save</button>
<a id="submit-btn" href="/cancel">Cancel</a>
After
<button id="save-btn">Save</button>
<a id="cancel-link" href="/cancel">Cancel</a>

The original markup gives two entirely different interactive elements (a save button and a cancel link) the identical id="submit-btn", so anything targeting #submit-btn (a label, a test script, a focus-management call) resolves to whichever one comes first in the DOM, silently ignoring the other. The fixed version gives each control a distinct, purpose-named id, so any future reference to either one resolves unambiguously.

Common Mistakes

Mistake: “Both duplicated buttons trigger the same action, so sharing an id is harmless.” Identical behavior today doesn’t protect against future changes, and it doesn’t fix the underlying targeting problem in the meantime: any script, test, label, or ARIA reference that targets the shared id still only ever reaches the first of the two elements, which becomes an active bug the moment the second element’s behavior needs to diverge even slightly.

Mistake: “This is a form-only problem, so it doesn’t apply to duplicate ids on links or custom buttons.” Any natively focusable element, or any element made focusable with tabindex, is in scope: a duplicated id on two <a> elements or two role="button" custom controls carries the exact same focus-and-targeting risk as a duplicated id on two form fields.

Mistake: “I tested by clicking both elements with a mouse and they both worked fine.” Mouse clicks generally target whichever element is under the cursor directly, bypassing the id-based lookups that break under duplication: label[for] clicks, keyboard-driven focus management, and script-based getElementById calls are where the failure actually surfaces, none of which a simple mouse-click test exercises.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s duplicate-id-active rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule scans for id values duplicated specifically on active, focusable elements (natively interactive elements and anything with a tabindex making it focusable), as a stricter subset of the general duplicate-id check.

False negative: none typical, since matching duplicate id strings specifically among focusable elements is a deterministic DOM comparison axe-core performs reliably for every element present at scan time. False positive: none typical for the same reason: an id either appears on two or more focusable elements or it doesn’t, with no judgment call involved. Manual step: for every flagged pair, click each associated <label> (if one exists) and confirm focus moves to the correct field, and Tab to each duplicated element individually to confirm its announced name and behavior match what’s expected for that specific control, not its duplicate.

Manual Testing

  1. Open the page in Chrome or Firefox with NVDA or VoiceOver running.
  2. Tab through every interactive element on the page, noting each one’s announced name.
  3. For any two duplicated id values found via DevTools, click the <label> (if present) associated with the second occurrence and confirm keyboard focus lands on the correct, intended field.
  4. Confirm any aria-describedby or aria-labelledby pointing at a duplicated id announces content relevant to the specific element you’re focused on, not its duplicate elsewhere on the page.
  5. Test autofill or password manager behavior on any duplicated form fields, confirming saved values fill into the intended field rather than its duplicate.

4.1.1 Parsing: Markup must be well-formed, with id values unique across the document. This rule maps directly to 4.1.1, isolating the specific case where the duplicated id sits on an active, focusable element rather than passive content, which is the more consequential version of the same underlying parsing failure.

Duplicate ID attributes found on the page is the broader parent check this rule narrows: any duplicate id anywhere on the page, whether or not the affected elements are interactive.

Focusable element is hidden from assistive technology shares this rule’s theme of focus-management defects that break a keyboard or screen reader user’s ability to reliably reach and operate interactive controls.

Duplicate id used in an ARIA reference is the sibling check for the same underlying defect, narrowed instead to duplicated ids that are the target of an ARIA naming or description attribute.

Keyboard focus becomes trapped in a widget is a related focus-management failure worth checking on the same page, since both rules can compound to make an interactive widget difficult or impossible to operate by keyboard.

Focused element is hidden behind a sticky header or footer is a different focus-related defect worth auditing alongside this rule, since both affect whether a keyboard user can reliably tell which interactive element currently has focus.

References

Frequently asked questions

Why is this rule rated more severe than the general duplicate-id check?

Because the elements involved are already interactive (a button, link, or form field a user is actively trying to operate) rather than a passive piece of content like a heading. When the id collision affects something a keyboard user is directly targeting with focus, the consequence is an immediate, first-hand functional failure rather than a secondary labeling mistake.

Does this rule only apply to elements reachable by Tab?

It applies to any active, focusable element, which mainly means natively interactive elements (buttons, links, form fields) plus anything with an explicit tabindex making it focusable. Tab reachability is the main practical symptom, but the underlying id collision is a document-level problem regardless of whether the specific element happens to be in the current Tab order.

Can duplicate ids on focusable elements break browser autofill?

Yes, this is a common real-world symptom. Browser autofill for forms frequently keys off an input's id or name to decide which field to fill and to remember previously entered values; two form fields sharing an id can cause a browser to fill the wrong field, skip a field entirely, or overwrite one field's saved value with another's.

If two buttons with the same id both do the same thing when clicked, is this still a real problem?

Yes. Even if both buttons happen to trigger identical behavior today, any label, aria-describedby, or in-page anchor pointing at that shared id resolves to only the first matching button, which silently breaks the moment the two buttons' behavior or labeling diverges even slightly during a future change, and a keyboard trap or wrong-focus-target bug can still occur even when the click behavior looks fine.

Is this different from duplicate-id-aria if the focusable element also has an ARIA reference pointing at its id?

An element can trigger both rules at once if it is both focusable and the target of an ARIA reference with a duplicated id; the two checks evaluate different consequences of the same underlying collision. This rule is about focus and interaction resolving correctly; duplicate-id-aria is about accessible name and description resolving correctly.