Skip to content

fix(dashboard): a11y basics — skip link, toast live region, combobox palette, keyboard-reachable workbench cards; restore canonical link tokens (design audit P3) - #3182

Merged
Xore merged 5 commits into
mainfrom
dsa-r1-structural
Sep 14, 2026

Conversation

@Xore

@Xore Xore commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Payload workbench run cards keep the clickable-<div> design (per docs/design-lab/README.md), but are now keyboard-reachable: role="button", tabIndex={0}, aria-label, aria-expanded, Enter/Space via onKeyDown.
  • Toast stack (LiveToasts.tsx) gets role="status" aria-live="polite" aria-atomic="false", a per-toast dismiss button, and hover/focus pauses the 12s auto-dismiss timer (resumes on leave/blur).
  • Skip link added as the first focusable element in AppShell, targeting a new id="main" on <main>; reuses the vendored .sr-only class with an inline focus-reveal style (theme.css is vendored, not hand-edited).
  • CommandPalette's textarea now exposes role="combobox", aria-expanded, aria-controls, aria-activedescendant; result rows are tabIndex={-1} so the input remains the single tab stop.
  • branding/templates/web/apiary-theme.css --apiary-link restored to the canonical tokens (dark #de866b, light #9b4f3a) per branding/tokens.json, replacing the AA-failing blues.

Checked docs/design-lab/README.md (clickable cards not links, no lazy-load, skeleton-first ES panels, one page width, SPA feel) — none of these fixes conflict with it.

Found one more instance of the unfixed clickable-div pattern (Investigate.tsx CardTag, non-href branch) outside this task's scope — filing a separate issue rather than expanding this PR.

Test plan

  • npm ci + npm run typecheck (tsc --noEmit) — clean, no errors
  • git diff --stat reviewed — only the 5 intended files touched, no lockfile drift
  • Manual keyboard pass (Tab to skip link, Tab into workbench cards + Enter/Space, palette arrow+Enter, toast hover-pause) — not run in this environment

…palette, keyboard-reachable workbench cards; restore canonical link tokens (design audit P3)
@strix-security

strix-security Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Updated for 8668db3.


Reviewed by Strix
Re-run review · Configure security review settings

@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@strix-security strix-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed all five changed files in this accessibility-focused PR. The changes are limited to ARIA attributes, keyboard reachability, toast dismissal timer management, a skip link, and CSS color tokens. No untrusted data reaches any dangerous sink: interpolated values (toast messages, recipe names, palette indices) are rendered through React's automatic output/attribute escaping, and no user-controlled URL, HTML, SQL, file path, or exec sink is introduced. No security issues found.


Reviewed by Strix
Configure security review settings

Preserve the design decision record (design-notes, claude-pure picks,
palettes, standing rules, playground) from the preserve-design-lab-1763
branch so it lives on main: clickable cards not links, no lazy-loading,
skeleton-first rendering, one page width, SPA feel.
@strix-security

Copy link
Copy Markdown

This workspace has reached the limit of 50 included PR reviews during your trial. Upgrade to continue receiving PR security reviews.

So far, Strix has reviewed 36 pull requests and surfaced 2 security issues across this workspace.

Comment thread docs/design-lab/playground/compare.html Fixed
Comment thread docs/design-lab/playground/compare.html Fixed
docs/design-lab/ is the 2026-08-17 design-lab record restored byte-identical
from preserve-design-lab-1763. playground/compare.html is a local-only
harness (127.0.0.1 iframe frames), never shipped or served; same exclusion
class as the ghost vendor entry.
@Xore

Xore commented Sep 14, 2026

Copy link
Copy Markdown
Owner Author

CodeQL failure root-caused: paths-ignore entry for docs/design-lab was described in comments but never added as a live list item, so CodeQL kept scanning the local-only compare.html harness (documentation-artifact noise, same class as the ghost-vendor exclusion).

Round-2 attempted encodeURI() at the iframe sinks was rejected on review (double-encodes already-percent-encoded safePath output %→%25); reverted.

Now: real docs/design-lab paths-ignore entry added; the two DOM-text alerts on compare.html are excluded as documentation artifacts.

CI re-running — expected CodeQL green.

@Xore
Xore force-pushed the dsa-r1-structural branch from 49ef2b1 to 90a66eb Compare September 14, 2026 00:43
@Xore

Xore commented Sep 14, 2026

Copy link
Copy Markdown
Owner Author

Removed stray Co-Authored-By trailer from the ci(codeql) commit; content unchanged. CI re-running.

@Xore
Xore merged commit 526eaf5 into main Sep 14, 2026
109 checks passed
@Xore
Xore deleted the dsa-r1-structural branch September 14, 2026 01:49
Xore added a commit that referenced this pull request Sep 27, 2026
#3437)

The design lab was tracked twice. `branding/design-lab/` (12 files, #1827 +
#1935) is the real one: `branding/` is the visual spec, `pages.yml` builds
and publishes it, and `quality.yml` runs `lab.test.mjs` out of it in both
the self-hosted and the GitHub-hosted lane. `docs/design-lab/` (10 files) was
the #1763 snapshot re-added incidentally by the #3182 a11y PR (526eaf5) and
had drifted, so anyone editing it was editing a dead fork.

It was not a pure duplicate, which is why this is a fold and not just a
delete. The `docs/` copy had been maintained after the fact: the 2026-09-27
markdown reconciliation (b2b230c) gave it the redaction notice and the
design-notes staleness banner, it carried the redaction of one captured
attacker IP (85.14.245.122 -> RFC 5737 203.0.113.122) that `branding/` never
received, and it held a colour-research section the canonical README had
never had. All of it moves to `branding/`, which is the tree that gets
published, so the redaction now lives where it is read.

Two files keep the canonical version on purpose. `playground/compare.html`:
both copies added a `safePath` guard, the canonical one whitelists the path
character by character and keeps the `|| '/'` fallback at every call site,
the other resolves against an origin and drops it, so the duplicate had
nothing the canonical tree lacked. `README.md`: the canonical one documents
`lab.mjs`, `lab.test.mjs` and the read-only gate; the duplicate's last
section said it "does not carry the harness itself", which is no longer true
of either tree.

Ten files removed, none dropped: every one of the duplicate's filenames
exists in `branding/design-lab/`, and the ten byte-identical ones plus the
two redacted files are asserted in the new gate. The one change of substance
is that the redaction moved rather than disappeared, so `branding/` no longer
carries the address the `docs/` copy had already replaced.

Gate: tests/docs/test_3310_design_lab_dedupe.py, picked up by the existing
`pytest tests/docs/` row. Three of its six tests name no path -- they key
off basenames that exist nowhere but the lab, so a copy reappearing at
`docs/design-lab/`, `branding/docs/` or a new `lab/` fails, not just the one
path this issue does.

References repointed: the CodeQL `paths-ignore` entry moves to the tree that
exists (its reasoning is a property of the lab, not the directory, so
dropping it would have broken the CodeQL job and keeping it would have
exempted nothing while blessing a re-added fork), the `docs/` record-tree
exemption goes with the tree it named, and docs/README.md links the canonical
README. No workflow, no action pin and no openapi.json change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants