Context. surface studio · domain governance · route /studio/accessibility-governance · auth signed-in + studio entitlement · source apps/oshun/web/src/app/studio/accessibility-governance/page.tsx
Last walked. 2026-07-03 real-dev-infra Playwright addendum — focused route journey verified direct admin navigation, live BFF catalog, seeded and edited WCAG contrast audits, relaxed large-text thresholds, client-only JSON validation, live BFF 400 details, pending-submit lockout, malformed/offline fail-closed paths, catalog loading/error, non-admin fail-closed auth, direct BFF auth/schema gates, quick-action affordances, shared axe, mobile containment, and anonymous redirect. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §264; apps/oshun/web/e2e/studio-accessibility-governance.spec.ts. Previous: 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified.
Purpose#
Audit colour contrast with the real WCAG 2.x auditor: the WCAG Contrast Lane
computes sRGB relative-luminance contrast ratios and reports AA/AAA conformance
with the relaxed large-text thresholds. It is a live admin console wired to
/v1/admin/studio/wcag-contrast, not an in-page simulation.
Entry points#
- Direct URL / bookmark —
/studio/accessibility-governance(signed-in + studio entitlement; admin scope required to audit) - Sibling Studio routes — reached via the quick-action panel on internationalization-localization, experimentation-feature-flags, and other governance/operations pages
Layout regions#
page.tsx mounts <ShellLayout active="studio"> and renders
<StudioAccessibilityGovernanceWorkspace /> followed by a single panel of
quick-action links.
- Shell:
ShellLayoutchrome with the Studio tab active - Workspace heading:
WorkspaceHeadingrenders the page<h1>"Studio Accessibility Governance", followed by thedata-accessibility-governance-summaryparagraph - WCAG Contrast Lane: an
<h2>data-wc-lane-heading("WCAG Contrast Lane") section with the load states and, when ready, the audit form and result table - Quick-actions panel:
data-wc-quick-actionsdata-wc-quick-action-count="5"wraps 5 siblingquickActionLinks:data-wc-quick-action="internationalization-localization",performance-budgets,experimentation-feature-flags,observability-operational-dashboards, andmetrics-analytics-instrumentation. No self-link and no Route Map panel.
States#
- Loading —
data-wc-loading("Loading contrast auditor…") - Unauthorized —
data-wc-unauthorized("Access restricted.") on 401/403 - Error —
data-wc-error("Could not load the contrast auditor.") - Ready / form —
data-wc-enums("N conformance levels") plus thedata-wc-formaudit form seeded withDEFAULT_PAYLOADanddata-wc-form-state="idle|pending" - Pending submit —
data-wc-submit-state="pending", disabled,aria-disabled="true", andaria-busy="true"while the audit POST is in flight - Result —
data-wc-resultheadline (data-wc-headlinewithdata-wc-aa-count,data-wc-aaa-count,data-wc-fail-count) and thedata-wc-pairstable ofdata-wc-pair-rowrows (data-wc-pair-ratio,data-aa-threshold,data-aaa-threshold,data-aa-pass,data-aaa-pass) - Audit error —
data-wc-audit-errorfor invalid JSON, a non-200 response, an offline audit POST, or a malformed 200 success payload - Offline — catalog GET fails closed to the error panel; the audit POST surfaces the generic error when the network is unreachable
- Gated — admin scope denied →
data-wc-unauthorized; the route itself is proxy-gated to signed-in + studio - Standalone PWA — renders within the shell; no URL-bar dependency
Interactions#
- Colour pairs (JSON) —
textareadata-wc-payload,aria-label="contrast pairs json", seeded with{ pairs[{ pairId, foreground, background, largeText }] } - Audit contrast —
buttondata-wc-submit(type="submit"); POSTs the parsed JSON to/v1/admin/studio/wcag-contrast/audit, settingdata-wc-resulton a 200 with a validresult, ordata-wc-audit-errorotherwise - Quick-action links — each
quickActionLinknavigates to its sibling Studio route (see Layout regions for the 5 targets)
Data & contracts#
- Reads:
GET /v1/admin/studio/wcag-contraston mount — returns the catalog (conformanceLevels[]) - Writes:
POST /v1/admin/studio/wcag-contrast/audit— returns{ result: { pairs[], summary{ aaPassCount, aaaPassCount, failCount } } } - Realtime: none
- Caching: client
fetchwithcache: 'no-store';buildBffAuthHeaders()attaches the bearer token - Auth/role check: admin-scoped fail-closed —
admin:*oradmin:studio, 401/403 under the studio workspace gate
E2E coverage#
apps/oshun/web/e2e/studio-accessibility-governance.spec.tsforwardsGET /v1/admin/studio/wcag-contrastandPOST /v1/admin/studio/wcag-contrast/auditto the live dev BFF with explicit admin and non-admin dev bearers. It covers direct admin navigation, catalog bearer forwarding, exact seeded ratios (21,4.54,2.85,8.59), AA/AAA summaries, relaxed large-text thresholds, low-contrast failures, malformed JSON zero-POST rejection, live BFF invalid-payload details, pending-submit lockout, catalog loading/error, malformed catalog, malformed 200 audit success, network-unreachable audit error, non-admin fail-closed state, direct BFF auth/schema gates, quick-action href/touch targets, shared axe, anonymous redirect, and mobile containment.
Cross-references#
- Parent:
../studio-overview.md - Sibling:
studio-internationalization-localization.md,studio-experimentation-feature-flags.md - Component source:
apps/oshun/web/src/components/studio/StudioAccessibilityGovernanceWorkspace.tsx - BFF store:
apps/oshun/bff/src/studio/WCAG contrast auditor behind/v1/admin/studio/wcag-contrast
Open questions / known gaps#
- Confirm whether the page will gain a breadcrumb/Route Map panel like other
Studio surfaces, or keep the flat quick-action panel — current
page.tsxkeeps the flat quick-action panel with no self-link and no Route Map panel as of 2026-07-03. - Document whether keyboard/screen-reader/semantic accessibility lanes will join the single contrast lane once the auditor expands. Current V1 browser behavior audits caller-supplied contrast pairs through the real deterministic BFF auditor; richer AT/semantic lane ingestion remains a product source expansion gap, not a browser interaction gap.