fix(a11y,design): modal aria-hiding root cause (#211) + first nitpick slice#212
Merged
Conversation
Root cause (differs from the issue's original hypothesis): Radix's hideOthers was working, but the aria-hidden package exempts every [aria-live] element and its ancestor chain. Three explicit aria-live attributes on real UI (invite chip button, the whole 130-element chat feed, the status strip) pinned large background subtrees screen-reader-reachable behind open dialogs. - invite chip: announcement moves to a dedicated hidden LiveRegion leaf - chat feed: role="log" (implicit polite liveness, additions-only announcements) - status strip / offline pill: role="status" already implies liveness; drop the explicit attribute Measured with the command palette open: aria snapshot 202 lines (pre-Radix) -> 49 (post-Radix) -> 20 (dialog-only, zero background leak). Guarded by e2e/modal-aria-hiding.spec.ts. Also fixes truncated composer placeholder "@nod" -> "@NodeAgent" (Chat.tsx). Closes #211 Co-Authored-By: Claude Fable 5 <[email protected]>
… violation - styles.css: 11.2px (3 declarations, tertiary metadata text) -> 11px on-scale - landing.css .r-land2-trust: informational governance line was rendered in success green (green is reserved for success semantics) -> text-secondary; 12.5px -> 13px, gap 7px -> 8px (type/space scale) Prettify audit context (.qa evidence, advisory): V1 type-scale 13 distinct sizes, V2 off-grid 73.7%, V6 8 radii/8 shadows/8 icon sizes. The systematic per-surface token migration (incl. alwayson.css) is deliberately NOT in this commit — it needs its own judged, pixel-diffed pass. Co-Authored-By: Claude Fable 5 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Scaffold Handoff — For Your Coding Agent
Immutability CheckMode: advisory ✅ No immutable files were modified in this branch. Changed Files
Needs Adversarial Review — Do NOT Apply YetThese proposals passed the reject check but have not been approved by
Safety Boundary
Immutable files (never modify):
Scaffold files (safe to modify):
|
…ent-ui.json The app now publishes its own drivability contract instead of making agents discover the UI by scraping: - src/design/uiContract.ts — authored, versioned source of truth: surfaces (the data-noderoom-surface taxonomy), elements with declared contexts and actions, state transitions (palette open/close), invariants (each naming the spec that guards it), and executable journeys. - scripts/ui-contract-emit.ts — deterministic emit to public/.well-known/agent-ui.json (served by the built app + Vercel); --check mode wired into design:audit so contract edits cannot land stale. - e2e/ui-contract-drift.spec.ts — EXECUTES the served contract against the rendered DOM: fetches the JSON, completes journeys from the contract's own steps, resolves every element in its declared context (landing, room, paletteOpen state, mobile). Renamed testids now fail CI. Differs from the existing `proofloop ui contract` (detectUiContracts), which regex-infers a capped element list from source with guessed actions and no rendered-DOM enforcement. First drift-verifier run already caught a contract error: notifications-bell mounts only in convex mode (RoomShell gates it), so it is declared live-only rather than memory-room-available. Co-Authored-By: Claude Fable 5 <[email protected]>
… mobile card metadata its own row
Three composition fixes, no new tokens or visual language:
- index.html SSR shell: below 560px the header becomes brand row + one clean
nav row at pill scale — previously the three nav pills wrapped raggedly
beside the brand on phones.
- Landing.tsx / landing.css: the memory-mode trust line becomes the same
neutral chip row as the SSR shell's proof points, so first paint and
hydrated app read as one design (was a plain text run after hydration).
- Mobile recents cards: metadata gets its own full-width row (title -> meta ->
action) instead of sharing a row with the open button, which ellipsized it
to meaningless fragments ("deck ·…", "2…"); the redundant kind prefix is
dropped since the kind badge already names it.
Verified: mobile suites green, design:audit + ui:contract:check pass, drift +
aria guards pass against the rebuilt preview; before/after captures in
.qa/evidence/20260717-postmerge/before-after/.
Co-Authored-By: Claude Fable 5 <[email protected]>
…s itself Direction chosen from three scored candidates (atmospheric field rejected: decorative motion violates quiet-UI; command-first composer rejected: product- scope change, not a landing pass). Winner: amplify the existing 6-step provenance loop into THE signature moment, with a motion identity where every movement IS a ledger event — nothing decorative: - Row focus sweep: when the agent acts on a row, the product's Trace Lens focus treatment draws across it (terracotta sweep + settling inset ring) — the in-app Attention Overlay motif, brought to the landing. - The cited quote RULES ITSELF into the ledger: its underline draws left to right and persists. - Lock badges clamp on with a small overshoot (a physical claim gesture). - Committed values land with ink that dries (rise + decay), staged 160ms after the agent line; the version chip ticks LAST (+340ms) — commit first, then the ledger acknowledges. - The agent avatar carries a soft working ring until the loop settles on v43. - Hero type continuity: SSR paints up to 76px, hydration dropped to 44px — the page visibly shrank. Now clamp(36px, 4.8vw, 60px), lh 0.95. All motion is gated behind prefers-reduced-motion: no-preference, matching the file's existing idiom (reduced users get the settled final frame). Gates: design:audit + ui:contract:check pass; drift/aria/landing e2e pass. Filmstrip captures: .qa/evidence/20260717-postmerge/before-after/after-signature-*.png Co-Authored-By: Claude Fable 5 <[email protected]>
Recorded from the built app (Playwright recordVideo), cropped to the demo card, trimmed to precisely one 10.1s provenance loop using ffmpeg scene detection (v43->lock flip at pts 10.80; next wrap at 20.92 confirms the period). Opens on lock, closes on v43. MP4 (h264, 397KB) + GIF (15fps, 1.8MB) in docs/design/media/. Co-Authored-By: Claude Fable 5 <[email protected]>
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.
Post-merge frontend verification + design-nitpick campaign, pass 1 of the loop.
Full evidence in
.qa/evidence/20260717-postmerge/(local, gitignored) and the QAledger (
.qa/memory/).Commit 1 — fix(a11y): aria-live pins defeated modal aria-hiding (closes #211)
Root cause differs from the issue's hypothesis: Radix's
hideOthersworked, but thearia-hiddenpackage exempts every[aria-live]element AND its ancestor chain.Explicit
aria-liveon the invite chip button, the 130-element chat feed, and thestatus strip pinned large background subtrees screen-reader-reachable behind dialogs.
Fix: dedicated hidden
LiveRegionleaf for the copy announcement;role="log"on thechat feed (implicit polite, additions-only — better SR behavior); drop redundant
aria-livewhererole="status"exists. Also fixes the truncated composerplaceholder
@nod→@nodeagent.Measured (aria snapshot lines, command palette open): 202 (pre-Radix) → 49
(post-Radix) → 20 (dialog-only, zero background leak). Guarded by new
e2e/modal-aria-hiding.spec.ts.Commit 2 — fix(design): off-scale tokens + green-semantics violation
11.2px→11px(3 declarations, tertiary metadata).r-land2-trust: informational governance line rendered in success green →--text-secondary; 12.5px → 13px, 7px gap → 8px. Verified by computed style:rgb(198,204,212), no longer#2E9E6B.Verification on this tree
design:audit+ui:layer-audit: pass (589 files)items live only in the authenticated lane (skipped locally, unit analogs green)
Deliberately deferred (next loop passes)
Prettify audit scored V1/V2/V6 = 0 (13 font sizes, 73.7% off-grid spacing, 8 radii/
shadows/icon sizes;
alwayson.cssis its own drifted mini-system). That systematictoken migration needs its own judged, pixel-diffed pass per surface — not bundled here.
Open P2s in the QA ledger: mobile landing nav wrap at 375px, mobile metadata
truncation ("2…"), binder pinned/recent duplication.
🤖 Generated with Claude Code