squirrelscan
GitHub

Duplicate id on a focusable element: what it breaks

Two inputs sharing an id break label association and focus management. squirrel checks every focusable element that carries an id.

What a duplicate id breaks

An id has to be unique in a document. When it is not, everything that resolves an id takes the first match and ignores the rest: document.getElementById, a <label for>, an aria-labelledby, a fragment link, a focus call.

On a focusable element that means the second control silently loses its label, and code that moves focus by id always lands on the first one. The page looks correct and behaves wrongly, usually only for the users who navigate by keyboard.

What squirrel checks

One check, duplicate-id-active, at severity error. It gathers focusable elements that carry an id: a[href], button, input, select and textarea with an id, plus any element that has both an id and a tabindex other than -1. The tabindex lookup is case-insensitive, so React’s tabIndex in server-rendered markup counts.

  • Fail with N duplicate ID(s) on focusable elements. Items read "submit" (3 occurrences), up to ten with a count of the rest.
  • Pass with All focusable elements have unique IDs.
  • Info with No focusable elements with IDs found.

Counting happens only across that focusable set. A collision between a focusable <input id="main"> and a non-focusable <section id="main"> is not reported here, so this check is narrower than a full duplicate-id sweep.

How to fix it

<label for="email-header">Email</label>
<input id="email-header" name="email" type="email">

<label for="email-footer">Email</label>
<input id="email-footer" name="email" type="email">

Give each instance its own id. A repeated newsletter form in the header and the footer is the classic source, and prefixing by location is enough.

Where a component can appear more than once on a page, generate the id inside the component and pass it to both the <label for> and the control, so the pair can never drift apart.

Rule IDa11y/duplicate-id-active
CategoryAccessibility
ScopePer-page
Severityerror
Weight7/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/duplicate-id-active"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/duplicate-id-active"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. Each repeated id is listed with the number of elements claiming it. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close