Skip to content

feat(mapgen-studio): token-driven chrome, a11y landmarks/live-regions/aria, and OptionSelect adapter#1613

Merged
mateicanavra merged 1 commit into
mainfrom
design/craft-a11y
Jun 12, 2026
Merged

feat(mapgen-studio): token-driven chrome, a11y landmarks/live-regions/aria, and OptionSelect adapter#1613
mateicanavra merged 1 commit into
mainfrom
design/craft-a11y

Conversation

@mateicanavra

Copy link
Copy Markdown
Owner

This PR delivers a presentation and accessibility pass over the Mapgen Studio chrome. No behavior, data contracts, or generation logic are changed.

Accessibility

AppFooter diagnostics restored for assistive tech and static markup. The prior Radix Tooltip migration moved operational diagnostics (Run-in-Game request id, failure reason, save/deploy status, live-sync hint, autoplay hint) into hover-only TooltipContent, making them invisible to screen readers and absent from static markup. Each diagnostic is now mirrored onto its visible trigger via aria-label and an in-DOM title. The footer wraps its own TooltipProvider so hints work in bare-mount test contexts without an ancestor provider, fixing the 8 previously-failing static-markup parity assertions.

Landmark structure and skip link. The deck.gl canvas host is wrapped in <main aria-label="Map preview">. The left and right docks are rendered as <aside> elements with descriptive labels. A visually-hidden skip-to-main link is the first focusable element in the shell.

Live regions. ErrorBanner gains role="alert" and aria-live="assertive". A visually-hidden aria-live="polite" region in StudioShell mirrors the volatile run/live/operation status without stealing focus.

Disclosure and selection state. aria-expanded and aria-controls are added to the collapsible section headers in ExplorePanel (Stage, Step, Layers), RecipePanel (Recipe, Config), and AppHeader (Setup). Active Stage, Step, and Layer items expose aria-current; render-mode and space toggle buttons expose aria-pressed.

Token-driven controls

OptionSelect adapter. A thin OptionSelect component wraps the token-driven Radix Select (src/components/ui/select) behind the simple value/onValueChange/options shape the legacy native <select> used. Empty option values map to an internal sentinel so Radix's non-empty value requirement is satisfied while the visible placeholder and emitted values are unchanged.

AppHeader and RecipePanel selects migrated. World Size, Players, Config, Resources, Leader, Civilization, Difficulty, and Game Speed dropdowns in AppHeader, and the Recipe and Config dropdowns in RecipePanel, all move from the legacy lightMode native select to OptionSelect. The lightMode prop is dropped from these call sites.

RJSF override widgets re-skinned. TextWidget, TextareaWidget, NumberWidget, SelectWidget, CheckboxWidget, SwitchWidget, and TagSelectWidget are rebuilt on the src/components/ui primitives. The lightMode prop and off-token ring-gray-400 are removed. The SelectWidget uses the same sentinel mapping as OptionSelect. Enum-to-typed-value mapping and emptyValue normalization are preserved byte-for-byte.

Stage craft and type scale

Token-referenced CanvasStage backdrop. The hard-coded lightMode hex ternary for the backdrop is replaced with bg-background. The radial vignette and grid are drawn using --muted-foreground at low alpha (luminance, not chroma). lightMode is still forwarded to DeckCanvas because it governs deck.gl scene rendering.

"Awaiting matter" empty state. The bare centered "Click Run to generate a map" text is replaced with a contour-framed panel over a coarse graticule field, so the empty stage reads as a ready survey console rather than dead space.

Named type scale. Ad-hoc text-[10px] and text-[11px] utilities in AppHeader, RecipePanel, and ExplorePanel adopt the text-label and text-data tokens.

Dead code removed

The unused src/ui/components/ui/AlertDialog.tsx (a hand-rolled re-implementation with no importers) and its barrel re-export are deleted.

@github-actions

Copy link
Copy Markdown

Railway preview (MapGen Studio): not provisioned for this PR.

Policy (Graphite stacks): previews are created only for the top-of-stack PR by default.

  • To force a preview for this PR: add label railway-preview
  • To suppress a preview: add label no-railway-preview

Debug: {"isClosed":false,"isFork":false,"suppress":false,"force":false,"isTopOfStack":false,"hasToken":true,"draft":false}

mateicanavra commented Jun 12, 2026

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

This was referenced Jun 12, 2026

mateicanavra commented Jun 12, 2026

Copy link
Copy Markdown
Owner Author

Merge activity

  • Jun 12, 8:50 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 12, 9:09 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jun 12, 9:09 PM UTC: @mateicanavra merged this pull request with Graphite.

@mateicanavra
mateicanavra changed the base branch from design/test-harness-fix to graphite-base/1613 June 12, 2026 21:06
@mateicanavra
mateicanavra changed the base branch from graphite-base/1613 to main June 12, 2026 21:07
…, token selects)

Presentation + accessibility only; no behavior, logic, or parity-critical
flow changes.

- AppFooter: restore operational diagnostics (run-in-game request id/phase/
  failure/recovery, save/deploy status, live-sync + autoplay hints) onto the
  visible triggers as aria-label/title so they are present for assistive tech
  and in static markup, not hover-only TooltipContent. Footer carries its own
  TooltipProvider. Fixes the 8 red AppFooter static-markup assertions (token-
  align the stale border-orange-400 assertion to border-warning).
- ErrorBanner: role=alert + aria-live=assertive; StudioShell adds a polite
  visually-hidden live region mirroring run/live status.
- Landmarks: <main aria-label="Map preview">, docks as <aside>, skip-to-main
  link as first focusable element.
- aria-expanded/aria-controls on ExplorePanel/RecipePanel/AppHeader collapsible
  headers; aria-current on active Stage/Step/Layer items; aria-pressed on
  render-mode/space toggles.
- Migrate AppHeader + RecipePanel select call sites to the token-driven Radix
  Select via a new OptionSelect adapter; re-skin rjsf widgets onto src/
  components/ui primitives (drops lightMode + off-token ring-gray-400). Remove
  dead legacy AlertDialog.
- Adopt named type scale (text-data/text-label) over text-[Npx]; token-
  reference CanvasStage backdrop + intentional 'awaiting matter' graticule
  framing, drop the lightMode chrome ternary.

OpenSpec: mapgen-studio-craft-a11y.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mateicanavra
mateicanavra merged commit f813ed5 into main Jun 12, 2026
This was referenced Jun 12, 2026
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