ui: a11y fixes from web-design-guidelines audit + transition: all sweep#72
Open
Kures wants to merge 2 commits into
Open
ui: a11y fixes from web-design-guidelines audit + transition: all sweep#72Kures wants to merge 2 commits into
Kures wants to merge 2 commits into
Conversation
Keyboard users had no visible focus, motion-sensitive users got the CRT drift even with prefers-reduced-motion set, the run-interrupt overlay was a non-semantic div that needed an a11y-suppress comment, and the provider/channel reorder/remove buttons were icon-only with no screen-reader name. - app.css: replace global `outline: none` with `:focus-visible` ring (2px solid accent + 2px offset) covering input/textarea/button/anchor; add `@media (prefers-reduced-motion: reduce)` block that neutralises CRT scanline + pulse animations. - InterruptPanel: drop the `<div role="button">` overlay backdrop in favour of a real `<button class="backdrop">` so backdrop-click is reachable by keyboard; add `aria-modal="true"`; remove the `svelte-ignore a11y_click_events_have_key_events` workaround. - ProviderList / ChannelList: add `aria-label` to the icon-only reorder (^/v) and remove (x) buttons; mark the glyph `aria-hidden` so it isn't double-announced. - TopBar / InstanceCard / ConfigEditor / InterruptPanel: replace \`transition: all\` with explicit property lists (background-color, border-color, box-shadow, color, transform, text-shadow) so the compositor isn't asked to animate everything.
`transition: all` forces the browser to track every animatable property, including layout-sensitive ones (width, height, padding) the components never actually change on hover/focus. That wastes work on every interaction and on weaker hardware shows up as visible jank during the hover/transition. Listing the properties we actually animate (background-color, border-color, box-shadow, color, transform, text-shadow) lets the compositor skip everything else. Mechanical sweep across the remaining 22 files; the previously-touched TopBar / InstanceCard / ConfigEditor / InterruptPanel were already converted in the prior a11y commit. Durations preserved as-is (mostly 0.2s ease, a few 0.15s, one 0.1s). No behaviour change beyond the perf/jank improvement.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR7 —
ui: a11y fixes from web-design-guidelines audit + transition: all sweepRepository:
nullclaw/nullhubBranch:
feat/ui-a11y-fixesTwo commits, 29 files, no behaviour change. Pure accessibility,
keyboard navigation, and CSS-correctness polish — no business logic
touched, no redesign.
Why
Ran the Web Interface Guidelines audit (
vercel-labs/web-interface-guidelines)over
ui/src/and found ~48 issues. The top 5 affect real users:input, button, textarea { outline: none }is applied without any:focus-visiblereplacement, so Tab navigation moves an invisiblecursor through the UI.
their system setting. No
@media (prefers-reduced-motion: reduce)anywhere in
app.css. The "intentional" terminal aestheticbecomes an accessibility barrier for users with vestibular
disorders.
<div role="button" tabindex="-1" onclick=…>with asvelte-ignore a11y_click_events_have_key_eventsworkaround.Screen readers don't announce it; keyboard users can't close it
by clicking outside.
↑ ↓ ×) in ProviderList and ChannelListcarry only
title="..."for hover tooltips. Screen readersannounce them as just "button".
transition: all 0.2s easeappears 78 times across 24 files.The compositor tracks every animatable property — including
layout-sensitive ones the components never change on hover — which
on weaker hardware shows up as visible jank.
What this PR ships
Commit 1 —
ui: fix top a11y issues from web-design-guidelines audit7 files, +79/−40.
src/app.css— replaces globaloutline: nonewith a:focus-visiblering (outline: 2px solid var(--accent)plusoutline-offset: 2px) covering input, textarea, button and anchor.Adds a
@media (prefers-reduced-motion: reduce)block thatneutralises the CRT scanline-drift and pulse animations
(
animation-duration: 0.01ms+ explicitanimation: noneon thebody pseudo-elements).
src/lib/components/orchestration/InterruptPanel.svelte— dropsthe
<div role="button">overlay backdrop in favour of a real<button class="backdrop">so backdrop-click is reachable bykeyboard. Adds
aria-modal="true"on the panel and removes thesvelte-ignoreworkaround.src/lib/components/ProviderList.svelteandsrc/lib/components/ChannelList.svelte—aria-labelon thereorder (
↑,↓) and remove (×) icon-only buttons, with theglyph wrapped in
<span aria-hidden="true">so it isn'tdouble-announced.
src/lib/components/TopBar.svelte,InstanceCard.svelte,ConfigEditor.svelte— replacetransition: allwith explicit property lists in the most-visiblecomponents.
Commit 2 —
ui: replace transition: all with explicit property lists22 files, +64/−64. Mechanical sweep of the remaining 78
transition: all <duration> easeoccurrences across components androutes. Each becomes:
Durations preserved as-is (mostly
0.2s, a few0.15s, one0.1s).No behaviour change beyond the perf/jank improvement.
Verification
Manual verification in Chrome via
agent-browser:document.activeElementafter Tab carriesoutline: rgb(0, 255, 65) solid 2pxand the glowbox-shadow. Thering is clearly visible on the rendered page (screenshot
02-focus-ring.pngin the project'sscreenshots-audit/).body::beforehasanimation-name: crt-scanline-driftandanimation-duration: 9s. Withagent-browser set media reduced-motion, both becomenone/0s. The media query isload-bearing.
grepon the production bundleconfirms
aria-label="Close dialog",Move provider up,Move provider down,Remove provider,Remove channelare allemitted in their respective node chunks.
The modal itself can't be exercised end-to-end without a real
orchestration interrupt, but the static markup is correct and the
build compiles.
Scope
Out of scope:
reduced-motion block only fires when the user has explicitly asked
the OS for less motion.
.tsfiles inlib/api/or storestouched.
transition: all→ explicit is incidentallybetter for the compositor, but this PR doesn't claim a measurable
perf win, just correctness.
In scope, not done here (worth a follow-up):
transition: allviolations that werenot in the most-visible components and are covered by the second
commit, plus several smaller items (missing
inputmodeon numberinputs, placeholders not ending in
…, hardcoded date formats).They are real but lower-impact than the top 5.
Affected files
29 files, +143/−104 across the two commits.
Environment
nullhub-ui@0.0.0-dev— Vite 6 + SvelteKit 2 + Svelte 5agent-browsernpm run build— clean (Windows host, MSYS bash)