Skip to content

a11y sweep keeps finding real WCAG defects in components nothing renders #46

Description

@havardljohansen

The a11y sweep has no way to tell a rendered component from an orphaned one, so it keeps finding real WCAG defects in code no user can reach — and each finding costs a filing, a triage and a review before anyone notices.

Needed: a decision on whether the a11y task should skip components nothing imports. Filing rather than patching the prompt, since this is a judgement call about what the sweep is for.

The instance

src/components/dark-mode-toggle.tsx on distriktssenteret-frontend. grep -rn "DarkModeToggle" over the repo (excluding node_modules, .next, .git) returns the component and its own test file, nothing else. No layout, page, prototype route or spec renders it. Same on merkur, verified against origin/develop there.

Every commit in that file's history is a Night Shift a11y bundle:

451c9bf  chore(night-shift): bundle 2026-06-18+19+22+…
b59315a  night-shift(a11y): add aria-hidden to decorative icons across 4 components
506daf8  fix(a11y): increase touch targets and fix event card image alt text
86a5ca0  night-shift/a11y: fix WCAG 2.1 AA violations across 8 components

The most recent finding — no aria-pressed on a toggle button, WCAG 4.1.2 — was filed as distriktssenteret-frontend#624 and was correct. merkur even had the fix already, so the obvious move was to port it. The component just does not ship. Resolved by deleting it instead: distriktssenteret-frontend#628, with frontkom/merkur-frontend#581 filed for the sibling.

Why this is a prompt finding, not a one-off

The diagnosis was right every time. The sweep is working as specified; the specification does not include "is this reachable". So the loop has no natural end while the file exists, and deleting one orphan does not stop it finding the next.

Per the triage rule — triaging generated output includes auditing the prompt behind it — one contradictory rule fixed upstream changes every later generation, where closing a bad PR handles one instance.

Possible shapes

  1. Reachability pre-filter. Before reporting on src/components/X.tsx, check whether anything outside X.test.* imports it. Cheap, mechanical, and would have caught this exact case. Risk: dynamic imports and string-keyed registries would read as unreachable — this stack has at least one (block-renderer's switch on _type), though that dispatches on already-imported components.
  2. Report it as a deletion candidate instead of an a11y fix. Keeps the finding, changes the recommendation — arguably more useful output than either fixing or skipping.
  3. Leave it, and accept that orphan components generate recurring findings until someone deletes them. Defensible if orphans are rare; on this stack I have now found one, and have not gone looking for others.

I would take (2) over (1): the sweep noticing "this component is unreachable" is a genuinely useful signal that nothing else currently produces, and it avoids the dynamic-import false negatives of a hard filter.

Worth knowing either way

Neither axe nor any CI gate could see this. The a11y suites on both sites were green throughout, because a component that never renders never gets audited. The only thing that surfaced it was reading the grep output while about to port a fix.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions