Skip to content

Fix accessibility and performance issues across the page set - #123

Merged
pambrose merged 3 commits into
masterfrom
accessibility-and-performance-pass
Aug 1, 2026
Merged

pambrose merged 3 commits into
masterfrom
accessibility-and-performance-pass

Conversation

@pambrose

@pambrose pambrose commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Runs the vendored impeccable design workflow end to end against the page set — document, audit, then all six remediation actions — and lands the resulting fixes.

Accessibility

  • Answer grading was color-only and never announced. checkAnswers painted a cell green or red and nothing else, so a screen-reader user got no result at all and a colorblind user got two fills measuring 1.36:1 against each other. Feedback cells now carry ✓ correct / ✗ try again text, and the status cell is a role="status" / aria-live="polite" region reporting N of M correct.
  • Answer inputs got aria-label="Return value for <invocation>" — previously they were unlabeled.
  • Like/dislike buttons got aria-labels; their decorative images got alt="".
  • The sign-in modal's close control was a span with an onClick — now a real button with aria-label="Close sign-in dialog" at 28×28.
  • bodyTitle() emits the wordmark as h1; the language nav got aria-label="Languages".
  • Added <meta charset> and a viewport tag.

Contrast

The pass/fail green and red met contrast as fills but not as text. Added rb-correct-text (#3E862E) and rb-wrong-text (#ED0000) as separate tokens for text use, and darkened rb-header to #337E9C for AA at its size.

HEADER_COLOR was being emitted as an inline style on the same elements that already carried text-rb-header, so the inline value won and darkening the token alone would have been a silent no-op. The constant and all 13 call sites are gone.

Link hover was color: red, which collided with the wrongness signal — now an underline.

Performance

  • The four like/dislike PNGs were 1600px wide, displayed at 30px: 345 KB → 12 KB.
  • nervous.png / panic.png → JPEG, ~450 KB → ~72 KB.
  • All six img sites got explicit width/height (no layout shift) and loading hints.

Latent bug fixed

The like/dislike and admin spinners used fa-spin, but Font Awesome is not loaded on those pages — the spinners were invisible. Replaced with a CSS .rb-spinner, including a prefers-reduced-motion branch.

Design record

PRODUCT.md, DESIGN.md, and .impeccable/design.json capture the product context and the design system the audit was run against. Per-machine .impeccable/ state is gitignored; the durable output is tracked.

One deliberate departure

The audit recommended width=device-width. This ships width=1024 instead. With no responsive CSS anywhere in the page set, device-width would trade a zoomed-but-coherent phone rendering for horizontal-scroll breakage. Worth revisiting once responsive work lands.

Not included

Deferred and each worth its own change: <main>/<header> landmarks (needs a BODYFlowContent receiver refactor across 27 pages), a custom :focus-visible treatment, and deleting the orphaned tw-safelist.html.

Verification

make build, make lint, and ./gradlew check all pass.

🤖 Generated with Claude Code

pambrose and others added 3 commits July 29, 2026 10:48
Answer grading was conveyed by background color alone and never announced,
which made the product's central action unreadable for screen-reader users
and unreliable for red-green colorblind students: the correct and wrong
fills sit at 1.36:1 against each other.

Accessibility
- Label each feedback cell "✓ correct" / "✗ try again" alongside the fill.
  Black on either fill holds 7.1:1 and 5.3:1.
- Announce outcomes via a role="status" aria-live region.
- Name the answer inputs, the like/dislike buttons, and the language nav.
- Add meta charset, promote the wordmark to the page's single h1, and give
  every image alt text and intrinsic dimensions.
- Make the sign-in modal close control a real button; it was a span with an
  onClick and could not be reached by keyboard.

Contrast (hue and saturation preserved, lightness reduced until AA passes)
- Chalk Blue heading #419DC1 -> #337E9C (3.08:1 -> 4.56:1)
- Success text -> #3E862E (2.22:1 -> 4.51:1)
- Error text -> #ED0000 (3.81:1 -> 4.56:1)
- a:hover underlines instead of turning red, so red means only "wrong"

The accent was also emitted as an inline style on elements that already
carried text-rb-header, so the inline value won and darkening the token
alone would have had no effect. HEADER_COLOR and all 13 call sites are gone.

Performance
- Thumbs-up/down were 1600x1600 rendered at 30x30: 345 KB -> 12 KB, on
  every challenge page.
- Replace the Font Awesome CDN stylesheet with an .rb-spinner rule that
  honors prefers-reduced-motion. The like/dislike and admin spinners used
  fa-spin on pages that never loaded Font Awesome, so they were invisible.
- Error-page photographs stored as PNG converted to JPEG: 241 KB -> 36 KB
  and 200 KB -> 35 KB.

Also declares a fixed width=1024 viewport rather than width=device-width,
which without responsive CSS would trade a zoomed-but-coherent phone
rendering for horizontal-scroll breakage; raises sub-24px hit areas;
removes dead td.ok/td.no rules; and routes message colors through
Message.colorClass instead of inline hex.

DESIGN.md and PRODUCT.md record the design system and product context this
work was measured against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.impeccable/ is otherwise a tracked output directory: design.json is the
design-system sidecar and belongs in the repo. Two things in there do not.

config.local.json records one developer's hook consent, and live/sessions/
is an append-only recovery journal for an in-progress editing session.
Neither describes the project, and config.local.json was already showing up
as untracked after the previous commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hook.cache.json and hook.pending.json are keyed by session id and hold
absolute paths from a single machine. The tooling excludes them via
.git/info/exclude, which is local to one clone, so they surfaced as
untracked for everyone else. Duplicate the rules here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pambrose
pambrose merged commit e51e809 into master Aug 1, 2026
4 checks passed
@pambrose
pambrose deleted the accessibility-and-performance-pass branch August 1, 2026 16:57
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.

1 participant