fix(a11y): mark two non-interactive wrappers as presentation and retighten the lint ceiling - #7768
Conversation
b61aff3 to
85a568e
Compare
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Removes real silent-admission slack, wrappers keep child semantics intact, and the hand-maintained-literal root cause is correctly deferred to the named follow-up. [DESIGN-REVIEWED] dc25dbe |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
UX Review (Fable 5) — ✅ PASSUX-level review of This PR touches no rendered pixels, strings, or flows — it annotates two wrapper divs as UX-Verdict: PASS No user-visible change: both [UX-REVIEWED] dc25dbe |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of The evidence checks out against the repo: both wrappers' activation genuinely lives elsewhere (the First-Principles-Verdict: PASS Two false-interactive wrappers get truthful roles and the ceiling returns to the count the CI comment itself mandates — every item is derived, nothing rides along. What this change shipsIntent: stop two wrappers from lint-reading as unreachable controls and take back the slack the lint ceiling's own instruction forbids — a FIX.
No new public surface, config key, or concept ships, so consumer counts don't arise. The two declared deferrals — the conditional-role pin in [FIRST-PRINCIPLES-REVIEWED] dc25dbe |
85a568e to
918eef3
Compare
…ghten the lint ceiling The drop surface in design-critique/Composer.tsx and the row wrapper in FindingRow.tsx both carry pointer handlers while their activation lives on a child control, so jsx-a11y read them as non-native interactive elements. Both are now role="presentation", which states what they are and removes nothing from the accessibility tree. That takes the tree from 601 warnings to 599, so the eslint ceiling returns to the count the tree actually measures. It had drifted to 604 as a fleet unblock after a third exhaustive-deps warning landed in ArtifactsPage.tsx while the ceiling said 603 — main measured one over its own ceiling and every PR touching website/** went red on a warning its diff never wrote. That page measures zero warnings now, so the slack has no remaining justification. The pin marker in DesignCritiquePage.tsx is deliberately left alone. Its role/tabIndex ternaries are invisible to jsx-a11y, which resolves only literal role values, but splitting them into literal branches means hoisting a shared style object — and that moves CSS box-shadow strings onto lines the diff-scoped i18n gate counts at zero tolerance. Two gates disagree there; the fix belongs to that page, not to a ceiling correction.
918eef3 to
dc25dbe
Compare
dwu96
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (3 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: fix with a clear root cause — two non-interactive wrapper divs in the design-critique app carried no role while their activation lives on inner controls, so jsx-a11y warned; adding role="presentation" removes both warnings and the eslint ceiling in ci.yml is retightened from 604 to the 599 the tree now measures.
iamwhatever
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (3 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: adds role="presentation" to two non-interactive design-critique wrappers whose activation already lives on child controls, and lowers the eslint --max-warnings ceiling 604 -> 599 to the count the tree now measures. Root cause is explicit: the wrappers carried an implicit generic role while jsx-a11y required the intent be stated; no behaviour or pixel change.
chenmingwei23
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (3 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean (Semgrep 0, CodeQL 0 alerts), security checklist all-NO, AI reviewers green. Category: fix -- two role=presentation a11y attributes on non-interactive wrappers plus eslint ceiling retightened 604 to 599, clear root cause, no behaviour change to controls.
Why no screenshot: the diff adds two
role="presentation"attributes and changes one CI number — the accessibility tree loses nothing and no pixel moves, so a before/after pair would be two identical images.What
design-critique/Composer.tsxdrop surfacerole="presentation"— the drag handlers are a pointer-only shortcut; the keyboard path is the existingrole="button"drop tiledesign-critique/FindingRow.tsxrow wrapperrole="presentation"— hover styling only; activation already lives on the<Clickable>child.github/workflows/ci.yml--max-warnings 604→599Both wrappers carried pointer handlers while their activation lived on a child control, so jsx-a11y read them as non-native interactive elements.
presentationstates what they are, and since adiv's implicit role is generic, it removes nothing from the accessibility tree.Why the ceiling moves
The step's own comment requires it: "the ceiling must EQUAL the measured count, not sit above it: slack is silent admission, and a warning that lands inside it never surfaces again."
It had drifted to 604 as a deliberate fleet unblock —
7362b6a2b(#7259) landed a thirdreact-hooks/exhaustive-depswarning inArtifactsPage.tsxwhile the ceiling said 603, so main measured one over its own ceiling and every open PR touchingwebsite/**went red on a warning its diff never wrote. That page measures zero warnings now, so the slack has no remaining justification. Removing two more warnings takes the tree to 599, which is what this sets.Pattern harvest
Rule candidate: CI — make the eslint ratchet self-measuring instead of a hand-maintained literal. The defect class is "the ceiling and the tree disagree," and it fired twice in one day in opposite directions: #7696 raised 603 → 604 to unblock the fleet, and this PR lowers it to 599. Both were manual re-measurements of a number CI can compute. Two mechanisms, either sufficient: (a) have the Lint step measure the merge base itself and fail only on growth relative to it, which is what the step's comment already describes in prose — that removes the literal and with it the window where a merge into a moved
mainfails on a warning the diff never wrote; or (b) if the literal stays, add a check that fails when it does not equal the measured count, so slack cannot accumulate silently, which is the property the comment asks for and nothing currently enforces.Not generalizable: the two
role="presentation"fixes themselves.useSemanticElementsis already an enforced lint rule, so this class is caught at authoring time; these two sites predate its coverage of their pattern rather than escaping it.Deliberately not fixed here
The pin marker in
DesignCritiquePage.tsxcarriesrole={interactive ? 'button' : undefined}andtabIndex={interactive ? 0 : undefined}. jsx-a11y resolves only literal role values, so that pin is neither a button nor presentational to the linter, and a keyboard user gets a<span>with a click handler — a real defect. Fixing it means splitting into literal branches, which means hoisting the shared style object, which moves CSSbox-shadowstrings onto lines the diff-scoped i18n gate counts at zero tolerance. Two gates disagree on that line; resolving it belongs to that page (whose i18n debt is already tracked), not to a ceiling correction. Left as a warning, inside the new ceiling.Tested
npx eslint src/(cwdwebsite, deps synced vianpm ciagainst current main's lockfile): 601 → 599 warnings, 0 errors.--max-warnings 599, fails at598.19 checks · PASS.npx tsc -b: clean.DesignCritique*specs: 6 files, 107 tests, all pass.Notes
PR #7569 is burning the same ceiling down repo-wide and touches these files too; it can drop the redundant hunks on its next rebase. Kept small so the ceiling correction can land on its own.