feat(accessibility): wire high-contrast + reduce-motion modes through engine

Resume-prompt Option F, part 1 of 2. Adds two accessibility flags
to Settings and threads each through the engine surfaces that
react to them. Settings UI toggle rows follow in a separate
commit; players who want to test today can edit `settings.json`
manually.

Spec at `docs/ui-mockups/design-system.md` §Accessibility (#2 and
#3).

### High-contrast mode

`Settings::high_contrast_mode: bool` (defaults to false; serde-
default for back-compat). When on:

- Red-suit text colour boosts from `RED_SUIT_COLOUR` (`#fb9fb1`)
  to a new `RED_SUIT_COLOUR_HC` (`#ff8aa0`).
- Black-suit text colour boosts from `BLACK_SUIT_COLOUR`
  (`#d0d0d0`) to a new `TEXT_PRIMARY_HC` (`#f5f5f5`).
- New `BORDER_SUBTLE_HC` (`#a0a0a0`) constant available for
  future chrome-side wiring (this commit only routes HC through
  card text rendering — chrome border boost is a separable
  follow-up).

The HC and CBM flags compose. CBM red→lime wins over HC on red
suits when both are on (lime is itself a high-luminance accent,
so the HC boost has nothing further to do). HC still applies to
black suits when both flags are on (CBM doesn't touch black).
Four new `text_colour` tests pin the truth table.

### Reduce-motion mode

`Settings::reduce_motion_mode: bool` (defaults to false; serde-
default for back-compat). When on:

- Card-slide animation duration is forced to `0.0` regardless of
  the player's `AnimSpeed` selection — cards snap instantly to
  their target position. Implemented by extracting a new
  `effective_slide_secs(&Settings)` helper that wraps
  `anim_speed_to_secs` with the reduce-motion gate.
- Future scaffolding hooks (splash scanline, warning-chip pulse,
  card-lift z-bump animation) follow the same `if
  settings.reduce_motion_mode { skip }` pattern when wired —
  stays out of scope for this commit since each motion path
  needs its own per-system gate.

Two new tests cover the gate behaviour and the fall-through-to-
AnimSpeed pass-through path.

### Threading

`text_colour` signature extended with a `high_contrast: bool`
parameter; `sync_cards` / `sync_cards_startup` /
`sync_cards_on_change` / `sync_cards` core / `spawn_card_entity`
/ `update_card_entity` all gain a parallel parameter mirroring
the existing `color_blind: bool` plumbing. Verbose but matches
the established pattern; a future refactor could pack both into
an `AccessibilityView` struct, but bigger blast radius.

### Stats

1191 passing / 0 failing across the workspace (net +6 from
v0.21.0's 1185 baseline once the icon-pin test landed):
- 4 new `text_colour` HC tests in `card_plugin`
  (red-suit boost, black-suit boost, CBM-wins-on-red,
  black-suits-with-CBM+HC-still-boost).
- 2 new `effective_slide_secs` tests in `animation_plugin`
  (zero-out under reduce-motion, fall-through to AnimSpeed when
  off).

`cargo clippy --workspace --all-targets -- -D warnings` clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-08 11:23:22 -07:00
parent 716a025352
commit c5787c6953
4 changed files with 203 additions and 27 deletions
+14
View File
@@ -61,6 +61,11 @@ pub const BG_HUD_BAND: Color = Color::srgba(0.125, 0.125, 0.125, 1.0);
/// `#d0d0d0`.
pub const TEXT_PRIMARY: Color = Color::srgb(0.816, 0.816, 0.816);
/// High-contrast variant of [`TEXT_PRIMARY`] — `#f5f5f5`. Boosted
/// luminance for the Settings → Accessibility → High-contrast mode
/// toggle. Spec at `design-system.md` §Accessibility (#2).
pub const TEXT_PRIMARY_HC: Color = Color::srgb(0.961, 0.961, 0.961);
/// Secondary text — captions, hints, muted labels. `#a0a0a0`.
pub const TEXT_SECONDARY: Color = Color::srgb(0.627, 0.627, 0.627);
@@ -212,6 +217,15 @@ pub const CARD_SHADOW_LOCAL_Z: f32 = -0.05;
/// `#353535`.
pub const BORDER_SUBTLE: Color = Color::srgba(0.208, 0.208, 0.208, 1.0);
/// High-contrast variant of [`BORDER_SUBTLE`] — `#a0a0a0`. Lifts
/// outlines from near-invisible to clearly visible for the
/// Settings → Accessibility → High-contrast mode toggle. Spec at
/// `design-system.md` §Accessibility (#2): outline jumps from
/// `#505050` to `#a0a0a0` so card borders, popover edges, and
/// focus rings are legible on low-quality displays / for low-
/// vision users.
pub const BORDER_SUBTLE_HC: Color = Color::srgba(0.627, 0.627, 0.627, 1.0);
/// Strong border — hover outline, focused button, active popover.
/// `outline` from the design system. `#505050`.
pub const BORDER_STRONG: Color = Color::srgba(0.314, 0.314, 0.314, 1.0);