diff --git a/assets/cards/faces/10C.png b/assets/cards/faces/10C.png index 1114dca..fba11c9 100644 Binary files a/assets/cards/faces/10C.png and b/assets/cards/faces/10C.png differ diff --git a/assets/cards/faces/10D.png b/assets/cards/faces/10D.png index a10e9ac..199bebb 100644 Binary files a/assets/cards/faces/10D.png and b/assets/cards/faces/10D.png differ diff --git a/assets/cards/faces/10H.png b/assets/cards/faces/10H.png index 92362db..4aa940f 100644 Binary files a/assets/cards/faces/10H.png and b/assets/cards/faces/10H.png differ diff --git a/assets/cards/faces/10S.png b/assets/cards/faces/10S.png index f1832e8..254a0bb 100644 Binary files a/assets/cards/faces/10S.png and b/assets/cards/faces/10S.png differ diff --git a/assets/cards/faces/2C.png b/assets/cards/faces/2C.png index 5b1e29c..a4551ad 100644 Binary files a/assets/cards/faces/2C.png and b/assets/cards/faces/2C.png differ diff --git a/assets/cards/faces/2D.png b/assets/cards/faces/2D.png index c6e8409..efbcd55 100644 Binary files a/assets/cards/faces/2D.png and b/assets/cards/faces/2D.png differ diff --git a/assets/cards/faces/2H.png b/assets/cards/faces/2H.png index d31fcd0..5578156 100644 Binary files a/assets/cards/faces/2H.png and b/assets/cards/faces/2H.png differ diff --git a/assets/cards/faces/2S.png b/assets/cards/faces/2S.png index c6e4e1d..96c73c0 100644 Binary files a/assets/cards/faces/2S.png and b/assets/cards/faces/2S.png differ diff --git a/assets/cards/faces/3C.png b/assets/cards/faces/3C.png index 3872ae6..69d9bc0 100644 Binary files a/assets/cards/faces/3C.png and b/assets/cards/faces/3C.png differ diff --git a/assets/cards/faces/3D.png b/assets/cards/faces/3D.png index e31d47e..6560532 100644 Binary files a/assets/cards/faces/3D.png and b/assets/cards/faces/3D.png differ diff --git a/assets/cards/faces/3H.png b/assets/cards/faces/3H.png index fe01a2a..56a8532 100644 Binary files a/assets/cards/faces/3H.png and b/assets/cards/faces/3H.png differ diff --git a/assets/cards/faces/3S.png b/assets/cards/faces/3S.png index 1e94a3d..43c09bb 100644 Binary files a/assets/cards/faces/3S.png and b/assets/cards/faces/3S.png differ diff --git a/assets/cards/faces/4C.png b/assets/cards/faces/4C.png index 1986982..a0f8769 100644 Binary files a/assets/cards/faces/4C.png and b/assets/cards/faces/4C.png differ diff --git a/assets/cards/faces/4D.png b/assets/cards/faces/4D.png index fe2431d..b770537 100644 Binary files a/assets/cards/faces/4D.png and b/assets/cards/faces/4D.png differ diff --git a/assets/cards/faces/4H.png b/assets/cards/faces/4H.png index c23f27c..e90268b 100644 Binary files a/assets/cards/faces/4H.png and b/assets/cards/faces/4H.png differ diff --git a/assets/cards/faces/4S.png b/assets/cards/faces/4S.png index c9721a6..07be4e0 100644 Binary files a/assets/cards/faces/4S.png and b/assets/cards/faces/4S.png differ diff --git a/assets/cards/faces/5C.png b/assets/cards/faces/5C.png index 77d048c..8c5f7e9 100644 Binary files a/assets/cards/faces/5C.png and b/assets/cards/faces/5C.png differ diff --git a/assets/cards/faces/5D.png b/assets/cards/faces/5D.png index 69e6de7..5e31fb3 100644 Binary files a/assets/cards/faces/5D.png and b/assets/cards/faces/5D.png differ diff --git a/assets/cards/faces/5H.png b/assets/cards/faces/5H.png index 4ab139b..26f0e5f 100644 Binary files a/assets/cards/faces/5H.png and b/assets/cards/faces/5H.png differ diff --git a/assets/cards/faces/5S.png b/assets/cards/faces/5S.png index 779cac8..84331c0 100644 Binary files a/assets/cards/faces/5S.png and b/assets/cards/faces/5S.png differ diff --git a/assets/cards/faces/6C.png b/assets/cards/faces/6C.png index 3b44b1a..757cc5e 100644 Binary files a/assets/cards/faces/6C.png and b/assets/cards/faces/6C.png differ diff --git a/assets/cards/faces/6D.png b/assets/cards/faces/6D.png index 9bd8f42..3a3405f 100644 Binary files a/assets/cards/faces/6D.png and b/assets/cards/faces/6D.png differ diff --git a/assets/cards/faces/6H.png b/assets/cards/faces/6H.png index 3c7702b..0b7a5c6 100644 Binary files a/assets/cards/faces/6H.png and b/assets/cards/faces/6H.png differ diff --git a/assets/cards/faces/6S.png b/assets/cards/faces/6S.png index 893c800..0d88f51 100644 Binary files a/assets/cards/faces/6S.png and b/assets/cards/faces/6S.png differ diff --git a/assets/cards/faces/7C.png b/assets/cards/faces/7C.png index 16a9ee7..c9262bb 100644 Binary files a/assets/cards/faces/7C.png and b/assets/cards/faces/7C.png differ diff --git a/assets/cards/faces/7D.png b/assets/cards/faces/7D.png index 3941053..081b721 100644 Binary files a/assets/cards/faces/7D.png and b/assets/cards/faces/7D.png differ diff --git a/assets/cards/faces/7H.png b/assets/cards/faces/7H.png index 333577f..0af5b34 100644 Binary files a/assets/cards/faces/7H.png and b/assets/cards/faces/7H.png differ diff --git a/assets/cards/faces/7S.png b/assets/cards/faces/7S.png index f05fec7..4830071 100644 Binary files a/assets/cards/faces/7S.png and b/assets/cards/faces/7S.png differ diff --git a/assets/cards/faces/8C.png b/assets/cards/faces/8C.png index 443f594..23ef996 100644 Binary files a/assets/cards/faces/8C.png and b/assets/cards/faces/8C.png differ diff --git a/assets/cards/faces/8D.png b/assets/cards/faces/8D.png index 0b44ae9..072cbe4 100644 Binary files a/assets/cards/faces/8D.png and b/assets/cards/faces/8D.png differ diff --git a/assets/cards/faces/8H.png b/assets/cards/faces/8H.png index 35a3ba8..dfc5506 100644 Binary files a/assets/cards/faces/8H.png and b/assets/cards/faces/8H.png differ diff --git a/assets/cards/faces/8S.png b/assets/cards/faces/8S.png index c788731..5b52714 100644 Binary files a/assets/cards/faces/8S.png and b/assets/cards/faces/8S.png differ diff --git a/assets/cards/faces/9C.png b/assets/cards/faces/9C.png index 2e73df0..3e81dff 100644 Binary files a/assets/cards/faces/9C.png and b/assets/cards/faces/9C.png differ diff --git a/assets/cards/faces/9D.png b/assets/cards/faces/9D.png index 992b867..b88869c 100644 Binary files a/assets/cards/faces/9D.png and b/assets/cards/faces/9D.png differ diff --git a/assets/cards/faces/9H.png b/assets/cards/faces/9H.png index a5e37db..ef0a45a 100644 Binary files a/assets/cards/faces/9H.png and b/assets/cards/faces/9H.png differ diff --git a/assets/cards/faces/9S.png b/assets/cards/faces/9S.png index 4882e41..d5146ba 100644 Binary files a/assets/cards/faces/9S.png and b/assets/cards/faces/9S.png differ diff --git a/assets/cards/faces/AC.png b/assets/cards/faces/AC.png index 6f53fdc..9ded55d 100644 Binary files a/assets/cards/faces/AC.png and b/assets/cards/faces/AC.png differ diff --git a/assets/cards/faces/AD.png b/assets/cards/faces/AD.png index b627ea7..8058115 100644 Binary files a/assets/cards/faces/AD.png and b/assets/cards/faces/AD.png differ diff --git a/assets/cards/faces/AH.png b/assets/cards/faces/AH.png index 1e98bb5..2f69612 100644 Binary files a/assets/cards/faces/AH.png and b/assets/cards/faces/AH.png differ diff --git a/assets/cards/faces/AS.png b/assets/cards/faces/AS.png index 86f8a2e..7a1f8cd 100644 Binary files a/assets/cards/faces/AS.png and b/assets/cards/faces/AS.png differ diff --git a/assets/cards/faces/JC.png b/assets/cards/faces/JC.png index dea4920..9919f75 100644 Binary files a/assets/cards/faces/JC.png and b/assets/cards/faces/JC.png differ diff --git a/assets/cards/faces/JD.png b/assets/cards/faces/JD.png index c78d22c..07c5d62 100644 Binary files a/assets/cards/faces/JD.png and b/assets/cards/faces/JD.png differ diff --git a/assets/cards/faces/JH.png b/assets/cards/faces/JH.png index 9fe208f..badcda8 100644 Binary files a/assets/cards/faces/JH.png and b/assets/cards/faces/JH.png differ diff --git a/assets/cards/faces/JS.png b/assets/cards/faces/JS.png index 71a49d8..e9d56b4 100644 Binary files a/assets/cards/faces/JS.png and b/assets/cards/faces/JS.png differ diff --git a/assets/cards/faces/KC.png b/assets/cards/faces/KC.png index 3a0f4f8..3b2bb7c 100644 Binary files a/assets/cards/faces/KC.png and b/assets/cards/faces/KC.png differ diff --git a/assets/cards/faces/KD.png b/assets/cards/faces/KD.png index ac69212..3a63eb7 100644 Binary files a/assets/cards/faces/KD.png and b/assets/cards/faces/KD.png differ diff --git a/assets/cards/faces/KH.png b/assets/cards/faces/KH.png index 58d5aa4..ecd0611 100644 Binary files a/assets/cards/faces/KH.png and b/assets/cards/faces/KH.png differ diff --git a/assets/cards/faces/KS.png b/assets/cards/faces/KS.png index 5e4147f..4a732f0 100644 Binary files a/assets/cards/faces/KS.png and b/assets/cards/faces/KS.png differ diff --git a/assets/cards/faces/QC.png b/assets/cards/faces/QC.png index 5a96614..7ee3c12 100644 Binary files a/assets/cards/faces/QC.png and b/assets/cards/faces/QC.png differ diff --git a/assets/cards/faces/QD.png b/assets/cards/faces/QD.png index e2e7366..b04fc47 100644 Binary files a/assets/cards/faces/QD.png and b/assets/cards/faces/QD.png differ diff --git a/assets/cards/faces/QH.png b/assets/cards/faces/QH.png index c0bb9d9..3f9cf2e 100644 Binary files a/assets/cards/faces/QH.png and b/assets/cards/faces/QH.png differ diff --git a/assets/cards/faces/QS.png b/assets/cards/faces/QS.png index e61f017..e20b7be 100644 Binary files a/assets/cards/faces/QS.png and b/assets/cards/faces/QS.png differ diff --git a/docs/ui-mockups/design-system.md b/docs/ui-mockups/design-system.md index e02e697..f4b80ec 100644 --- a/docs/ui-mockups/design-system.md +++ b/docs/ui-mockups/design-system.md @@ -137,22 +137,23 @@ The palette is base16-eighties — a 16-slot terminal palette where indices 00 ## Suit Colors -**Four-color deck**, with mandatory color-blind support. Each suit -picks up its own base16-eighties accent so a player scanning the -table can identify the suit by hue alone (faster than the -traditional 2-color red/black mapping; common in poker decks and -online card games): +**Two-color traditional pairing**, with mandatory color-blind +support. Saturated red for hearts + diamonds, near-white for clubs ++ spades — the "Microsoft Solitaire on dark mode" feel of a real +playing-card deck. (A brief 4-color-deck experiment shipped between +v0.21.0 and the next post-cut commit; reverted to traditional +2-color at the player's request.) | Suit | Default | Color-blind mode | Glyph differentiation | |---|---|---|---| -| Hearts | `#fb9fb1` (pink, base08) | `#acc267` (lime) | Solid filled glyph | -| Diamonds | `#ddb26f` (gold, base09) | `#ddb26f` (unchanged) | **Outlined glyph (1.5px stroke)** | -| Spades | `#d0d0d0` (foreground, base05) | `#d0d0d0` (unchanged) | Solid filled glyph | -| Clubs | `#acc267` (lime, base0A) | `#acc267` (unchanged) | **Outlined glyph (1.5px stroke)** | +| Hearts | `#e35353` (saturated red) | `#acc267` (lime) | Solid filled glyph | +| Diamonds | `#e35353` (saturated red) | `#acc267` (lime) | **Outlined glyph (1.5px stroke)** | +| Spades | `#e8e8e8` (near-white) | `#e8e8e8` (unchanged) | Solid filled glyph | +| Clubs | `#e8e8e8` (near-white) | `#e8e8e8` (unchanged) | **Outlined glyph (1.5px stroke)** | The outlined-glyph treatment is the **primary** differentiation mechanism. Color is supplementary. This means a player viewing the game on a monochrome display, or with severe red-green deficiency, can still distinguish all four suits without context. This is a hard requirement, not an optional setting. -The "color-blind mode" toggle in Settings only swaps the heart suit colour from pink to lime; the other three suits (diamonds gold, clubs lime, spades gray) are already hue-distinct from pink and stay unchanged. The toggle does not turn the outlined glyphs on or off, because outlined glyphs are always on. Note: under CBM with the 4-colour deck, hearts and clubs share the lime hue — the always-on filled-vs-outlined glyph differentiation (♥ filled, ♣ outlined) keeps them readable. (Was red→cyan before the 2026-05-08 primary-accent swap; CBM moved to lime to stay hue-distinct from the new red-family primary.) +The "color-blind mode" toggle in Settings swaps both red suits (hearts + diamonds) from `#e35353` to `#acc267` (lime); clubs + spades stay at the near-white. The toggle does not turn the outlined glyphs on or off, because outlined glyphs are always on. (Was red→cyan before the 2026-05-08 primary-accent swap; CBM moved to lime to stay hue-distinct from the new red-family primary.) ## Typography @@ -276,7 +277,7 @@ Top-right corner of the HUD: a 6px circular dot. ## Accessibility -1. **Color-blind mode** (Settings → Gameplay): swaps the heart suit colour from `#fb9fb1` (pink) to `#acc267` (lime). The other three suits in the 4-colour deck (diamonds gold, clubs lime, spades gray) are already hue-distinct and stay unchanged. Outlined-glyph differentiation remains active in *all* modes. +1. **Color-blind mode** (Settings → Gameplay): swaps the red suits' default `#e35353` for `#acc267` (lime). Outlined-glyph differentiation remains active in *all* modes. 2. **High-contrast mode** (Settings → Gameplay): boosts on-surface from `#d0d0d0` to `#f5f5f5`, outline from `#505050` to `#a0a0a0`, suit-red from `#fb9fb1` to `#ff8aa0`. 3. **Reduce-motion mode** (Settings → Gameplay): disables card-lift transition (instant z-lift), disables CRT scanline effect, disables the warning-chip pulse animation. 4. **Tabular figures** are mandatory for any number that updates live (timer, score, moves) so they don't reflow. diff --git a/solitaire_engine/assets/themes/default/clubs_10.svg b/solitaire_engine/assets/themes/default/clubs_10.svg index 0875f0b..de9fd16 100644 --- a/solitaire_engine/assets/themes/default/clubs_10.svg +++ b/solitaire_engine/assets/themes/default/clubs_10.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 10 + fill="#e8e8e8">10 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_2.svg b/solitaire_engine/assets/themes/default/clubs_2.svg index 19ba63d..43bfe1b 100644 --- a/solitaire_engine/assets/themes/default/clubs_2.svg +++ b/solitaire_engine/assets/themes/default/clubs_2.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 2 + fill="#e8e8e8">2 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_3.svg b/solitaire_engine/assets/themes/default/clubs_3.svg index bd88816..aa8e6ae 100644 --- a/solitaire_engine/assets/themes/default/clubs_3.svg +++ b/solitaire_engine/assets/themes/default/clubs_3.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 3 + fill="#e8e8e8">3 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_4.svg b/solitaire_engine/assets/themes/default/clubs_4.svg index a8a2e68..423d626 100644 --- a/solitaire_engine/assets/themes/default/clubs_4.svg +++ b/solitaire_engine/assets/themes/default/clubs_4.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 4 + fill="#e8e8e8">4 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_5.svg b/solitaire_engine/assets/themes/default/clubs_5.svg index 70c887f..e462b51 100644 --- a/solitaire_engine/assets/themes/default/clubs_5.svg +++ b/solitaire_engine/assets/themes/default/clubs_5.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 5 + fill="#e8e8e8">5 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_6.svg b/solitaire_engine/assets/themes/default/clubs_6.svg index 1d62ad4..5172109 100644 --- a/solitaire_engine/assets/themes/default/clubs_6.svg +++ b/solitaire_engine/assets/themes/default/clubs_6.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 6 + fill="#e8e8e8">6 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_7.svg b/solitaire_engine/assets/themes/default/clubs_7.svg index 95581a4..ff556d0 100644 --- a/solitaire_engine/assets/themes/default/clubs_7.svg +++ b/solitaire_engine/assets/themes/default/clubs_7.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 7 + fill="#e8e8e8">7 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_8.svg b/solitaire_engine/assets/themes/default/clubs_8.svg index b27d0da..1c7edde 100644 --- a/solitaire_engine/assets/themes/default/clubs_8.svg +++ b/solitaire_engine/assets/themes/default/clubs_8.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 8 + fill="#e8e8e8">8 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_9.svg b/solitaire_engine/assets/themes/default/clubs_9.svg index d5826c0..3db19d0 100644 --- a/solitaire_engine/assets/themes/default/clubs_9.svg +++ b/solitaire_engine/assets/themes/default/clubs_9.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 9 + fill="#e8e8e8">9 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_ace.svg b/solitaire_engine/assets/themes/default/clubs_ace.svg index 72f1c58..09ad7c7 100644 --- a/solitaire_engine/assets/themes/default/clubs_ace.svg +++ b/solitaire_engine/assets/themes/default/clubs_ace.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> A + fill="#e8e8e8">A - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_jack.svg b/solitaire_engine/assets/themes/default/clubs_jack.svg index d55b92d..cca4db5 100644 --- a/solitaire_engine/assets/themes/default/clubs_jack.svg +++ b/solitaire_engine/assets/themes/default/clubs_jack.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> J + fill="#e8e8e8">J - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_king.svg b/solitaire_engine/assets/themes/default/clubs_king.svg index b654e92..dbeb418 100644 --- a/solitaire_engine/assets/themes/default/clubs_king.svg +++ b/solitaire_engine/assets/themes/default/clubs_king.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> K + fill="#e8e8e8">K - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/clubs_queen.svg b/solitaire_engine/assets/themes/default/clubs_queen.svg index ba540fc..2a288c9 100644 --- a/solitaire_engine/assets/themes/default/clubs_queen.svg +++ b/solitaire_engine/assets/themes/default/clubs_queen.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> Q + fill="#e8e8e8">Q - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_10.svg b/solitaire_engine/assets/themes/default/diamonds_10.svg index 60ab29c..3541721 100644 --- a/solitaire_engine/assets/themes/default/diamonds_10.svg +++ b/solitaire_engine/assets/themes/default/diamonds_10.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 10 + fill="#e35353">10 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_2.svg b/solitaire_engine/assets/themes/default/diamonds_2.svg index 371b467..66b6a72 100644 --- a/solitaire_engine/assets/themes/default/diamonds_2.svg +++ b/solitaire_engine/assets/themes/default/diamonds_2.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 2 + fill="#e35353">2 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_3.svg b/solitaire_engine/assets/themes/default/diamonds_3.svg index 6a1999b..0a6bc4c 100644 --- a/solitaire_engine/assets/themes/default/diamonds_3.svg +++ b/solitaire_engine/assets/themes/default/diamonds_3.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 3 + fill="#e35353">3 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_4.svg b/solitaire_engine/assets/themes/default/diamonds_4.svg index 2c48b73..4fdb3d2 100644 --- a/solitaire_engine/assets/themes/default/diamonds_4.svg +++ b/solitaire_engine/assets/themes/default/diamonds_4.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 4 + fill="#e35353">4 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_5.svg b/solitaire_engine/assets/themes/default/diamonds_5.svg index 7b07f42..2236e04 100644 --- a/solitaire_engine/assets/themes/default/diamonds_5.svg +++ b/solitaire_engine/assets/themes/default/diamonds_5.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 5 + fill="#e35353">5 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_6.svg b/solitaire_engine/assets/themes/default/diamonds_6.svg index ddce01a..0f52ef4 100644 --- a/solitaire_engine/assets/themes/default/diamonds_6.svg +++ b/solitaire_engine/assets/themes/default/diamonds_6.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 6 + fill="#e35353">6 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_7.svg b/solitaire_engine/assets/themes/default/diamonds_7.svg index cac2f21..94d9069 100644 --- a/solitaire_engine/assets/themes/default/diamonds_7.svg +++ b/solitaire_engine/assets/themes/default/diamonds_7.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 7 + fill="#e35353">7 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_8.svg b/solitaire_engine/assets/themes/default/diamonds_8.svg index 25abc8f..da135f8 100644 --- a/solitaire_engine/assets/themes/default/diamonds_8.svg +++ b/solitaire_engine/assets/themes/default/diamonds_8.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 8 + fill="#e35353">8 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_9.svg b/solitaire_engine/assets/themes/default/diamonds_9.svg index c658ff9..c0aff22 100644 --- a/solitaire_engine/assets/themes/default/diamonds_9.svg +++ b/solitaire_engine/assets/themes/default/diamonds_9.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 9 + fill="#e35353">9 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_ace.svg b/solitaire_engine/assets/themes/default/diamonds_ace.svg index 686a878..af1f32c 100644 --- a/solitaire_engine/assets/themes/default/diamonds_ace.svg +++ b/solitaire_engine/assets/themes/default/diamonds_ace.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> A + fill="#e35353">A - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_jack.svg b/solitaire_engine/assets/themes/default/diamonds_jack.svg index b96c4ce..f4f3c4b 100644 --- a/solitaire_engine/assets/themes/default/diamonds_jack.svg +++ b/solitaire_engine/assets/themes/default/diamonds_jack.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> J + fill="#e35353">J - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_king.svg b/solitaire_engine/assets/themes/default/diamonds_king.svg index 39f96a2..b13128e 100644 --- a/solitaire_engine/assets/themes/default/diamonds_king.svg +++ b/solitaire_engine/assets/themes/default/diamonds_king.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> K + fill="#e35353">K - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/diamonds_queen.svg b/solitaire_engine/assets/themes/default/diamonds_queen.svg index 358021a..8e467bc 100644 --- a/solitaire_engine/assets/themes/default/diamonds_queen.svg +++ b/solitaire_engine/assets/themes/default/diamonds_queen.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> Q + fill="#e35353">Q - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_10.svg b/solitaire_engine/assets/themes/default/hearts_10.svg index 8eb7042..e31e090 100644 --- a/solitaire_engine/assets/themes/default/hearts_10.svg +++ b/solitaire_engine/assets/themes/default/hearts_10.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 10 + fill="#e35353">10 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_2.svg b/solitaire_engine/assets/themes/default/hearts_2.svg index 301cb2e..3eca6b5 100644 --- a/solitaire_engine/assets/themes/default/hearts_2.svg +++ b/solitaire_engine/assets/themes/default/hearts_2.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 2 + fill="#e35353">2 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_3.svg b/solitaire_engine/assets/themes/default/hearts_3.svg index a9413a5..271488f 100644 --- a/solitaire_engine/assets/themes/default/hearts_3.svg +++ b/solitaire_engine/assets/themes/default/hearts_3.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 3 + fill="#e35353">3 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_4.svg b/solitaire_engine/assets/themes/default/hearts_4.svg index 9515f91..eaa97b0 100644 --- a/solitaire_engine/assets/themes/default/hearts_4.svg +++ b/solitaire_engine/assets/themes/default/hearts_4.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 4 + fill="#e35353">4 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_5.svg b/solitaire_engine/assets/themes/default/hearts_5.svg index 50aaa13..6119a14 100644 --- a/solitaire_engine/assets/themes/default/hearts_5.svg +++ b/solitaire_engine/assets/themes/default/hearts_5.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 5 + fill="#e35353">5 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_6.svg b/solitaire_engine/assets/themes/default/hearts_6.svg index 21e07be..2ded556 100644 --- a/solitaire_engine/assets/themes/default/hearts_6.svg +++ b/solitaire_engine/assets/themes/default/hearts_6.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 6 + fill="#e35353">6 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_7.svg b/solitaire_engine/assets/themes/default/hearts_7.svg index a8c905f..51100af 100644 --- a/solitaire_engine/assets/themes/default/hearts_7.svg +++ b/solitaire_engine/assets/themes/default/hearts_7.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 7 + fill="#e35353">7 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_8.svg b/solitaire_engine/assets/themes/default/hearts_8.svg index 8ecea9d..b7b9cca 100644 --- a/solitaire_engine/assets/themes/default/hearts_8.svg +++ b/solitaire_engine/assets/themes/default/hearts_8.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 8 + fill="#e35353">8 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_9.svg b/solitaire_engine/assets/themes/default/hearts_9.svg index 8bac73d..d7e0cac 100644 --- a/solitaire_engine/assets/themes/default/hearts_9.svg +++ b/solitaire_engine/assets/themes/default/hearts_9.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> 9 + fill="#e35353">9 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_ace.svg b/solitaire_engine/assets/themes/default/hearts_ace.svg index 08c1278..6006e9b 100644 --- a/solitaire_engine/assets/themes/default/hearts_ace.svg +++ b/solitaire_engine/assets/themes/default/hearts_ace.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> A + fill="#e35353">A - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_jack.svg b/solitaire_engine/assets/themes/default/hearts_jack.svg index d4b63f5..f5fd15d 100644 --- a/solitaire_engine/assets/themes/default/hearts_jack.svg +++ b/solitaire_engine/assets/themes/default/hearts_jack.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> J + fill="#e35353">J - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_king.svg b/solitaire_engine/assets/themes/default/hearts_king.svg index 476b1ce..3b110a0 100644 --- a/solitaire_engine/assets/themes/default/hearts_king.svg +++ b/solitaire_engine/assets/themes/default/hearts_king.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> K + fill="#e35353">K - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/hearts_queen.svg b/solitaire_engine/assets/themes/default/hearts_queen.svg index f4e9848..b17ce05 100644 --- a/solitaire_engine/assets/themes/default/hearts_queen.svg +++ b/solitaire_engine/assets/themes/default/hearts_queen.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e35353" stroke-width="2"/> Q + fill="#e35353">Q - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_10.svg b/solitaire_engine/assets/themes/default/spades_10.svg index 660dd6c..e0ab4fc 100644 --- a/solitaire_engine/assets/themes/default/spades_10.svg +++ b/solitaire_engine/assets/themes/default/spades_10.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 10 + fill="#e8e8e8">10 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_2.svg b/solitaire_engine/assets/themes/default/spades_2.svg index cdb029f..bacdb90 100644 --- a/solitaire_engine/assets/themes/default/spades_2.svg +++ b/solitaire_engine/assets/themes/default/spades_2.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 2 + fill="#e8e8e8">2 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_3.svg b/solitaire_engine/assets/themes/default/spades_3.svg index a66ef53..33b270b 100644 --- a/solitaire_engine/assets/themes/default/spades_3.svg +++ b/solitaire_engine/assets/themes/default/spades_3.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 3 + fill="#e8e8e8">3 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_4.svg b/solitaire_engine/assets/themes/default/spades_4.svg index e9122ed..8ff891b 100644 --- a/solitaire_engine/assets/themes/default/spades_4.svg +++ b/solitaire_engine/assets/themes/default/spades_4.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 4 + fill="#e8e8e8">4 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_5.svg b/solitaire_engine/assets/themes/default/spades_5.svg index 9907e1e..09034f9 100644 --- a/solitaire_engine/assets/themes/default/spades_5.svg +++ b/solitaire_engine/assets/themes/default/spades_5.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 5 + fill="#e8e8e8">5 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_6.svg b/solitaire_engine/assets/themes/default/spades_6.svg index 567f970..7071cf1 100644 --- a/solitaire_engine/assets/themes/default/spades_6.svg +++ b/solitaire_engine/assets/themes/default/spades_6.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 6 + fill="#e8e8e8">6 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_7.svg b/solitaire_engine/assets/themes/default/spades_7.svg index bfb33be..c5290c1 100644 --- a/solitaire_engine/assets/themes/default/spades_7.svg +++ b/solitaire_engine/assets/themes/default/spades_7.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 7 + fill="#e8e8e8">7 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_8.svg b/solitaire_engine/assets/themes/default/spades_8.svg index 847c652..161338e 100644 --- a/solitaire_engine/assets/themes/default/spades_8.svg +++ b/solitaire_engine/assets/themes/default/spades_8.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 8 + fill="#e8e8e8">8 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_9.svg b/solitaire_engine/assets/themes/default/spades_9.svg index f384d2e..870eb25 100644 --- a/solitaire_engine/assets/themes/default/spades_9.svg +++ b/solitaire_engine/assets/themes/default/spades_9.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> 9 + fill="#e8e8e8">9 - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_ace.svg b/solitaire_engine/assets/themes/default/spades_ace.svg index 0c61e8d..f9a8398 100644 --- a/solitaire_engine/assets/themes/default/spades_ace.svg +++ b/solitaire_engine/assets/themes/default/spades_ace.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> A + fill="#e8e8e8">A - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_jack.svg b/solitaire_engine/assets/themes/default/spades_jack.svg index 1f38d1f..fd7af05 100644 --- a/solitaire_engine/assets/themes/default/spades_jack.svg +++ b/solitaire_engine/assets/themes/default/spades_jack.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> J + fill="#e8e8e8">J - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_king.svg b/solitaire_engine/assets/themes/default/spades_king.svg index dcbdeca..60428d3 100644 --- a/solitaire_engine/assets/themes/default/spades_king.svg +++ b/solitaire_engine/assets/themes/default/spades_king.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> K + fill="#e8e8e8">K - + - + \ No newline at end of file diff --git a/solitaire_engine/assets/themes/default/spades_queen.svg b/solitaire_engine/assets/themes/default/spades_queen.svg index 25c0ef5..d5ae4ae 100644 --- a/solitaire_engine/assets/themes/default/spades_queen.svg +++ b/solitaire_engine/assets/themes/default/spades_queen.svg @@ -1,18 +1,18 @@ + fill="#1a1a1a" stroke="#e8e8e8" stroke-width="2"/> Q + fill="#e8e8e8">Q - + - + \ No newline at end of file diff --git a/solitaire_engine/src/assets/card_face_svg.rs b/solitaire_engine/src/assets/card_face_svg.rs index 3133437..58deb4f 100644 --- a/solitaire_engine/src/assets/card_face_svg.rs +++ b/solitaire_engine/src/assets/card_face_svg.rs @@ -30,18 +30,14 @@ pub const TARGET: UVec2 = UVec2::new(256, 384); const BG_FACE: &str = "#1a1a1a"; // BG_ELEVATED — face background -// Four-colour deck: each suit picks up its own base16-eighties -// accent so a player scanning the table can distinguish the suit -// by hue alone. Faster recognition than the 2-colour traditional -// red/black scheme; common in poker-room decks and online card -// games. The outlined-glyph differentiation (♦ ♣ outlined, ♥ ♠ -// filled) is preserved on top of the colour split as the -// always-on colour-blind fallback per `design-system.md` -// §Accessibility. -const SUIT_HEART: &str = "#fb9fb1"; // pink (base08 / RED_SUIT_COLOUR) -const SUIT_DIAMOND: &str = "#ddb26f"; // gold (base09 / STATE_WARNING) -const SUIT_CLUB: &str = "#acc267"; // lime (base0A / STATE_SUCCESS) -const SUIT_SPADE: &str = "#d0d0d0"; // foreground gray (base05 / TEXT_PRIMARY) +// 2-colour traditional pairing — hearts + diamonds share a +// saturated red, clubs + spades share a near-white. Mirrors the +// "Microsoft Solitaire on dark mode" feel of a real card deck. +// Brief 4-colour-deck experiment between v0.21.0 and this commit +// reverted; the always-on filled-vs-outlined glyph differentiation +// (♦ ♣ outlined, ♥ ♠ filled) carries the always-on CBM fallback. +const SUIT_RED: &str = "#e35353"; // matches `RED_SUIT_COLOUR` +const SUIT_DARK: &str = "#e8e8e8"; // matches `BLACK_SUIT_COLOUR` const BACK_BG: &str = "#151515"; const BACK_SCANLINE: &str = "#1a1a1a"; @@ -156,10 +152,10 @@ enum GlyphPaint { fn suit_paint(suit: Suit) -> (&'static str, GlyphPaint) { match suit { - Suit::Hearts => (SUIT_HEART, GlyphPaint::Filled), - Suit::Diamonds => (SUIT_DIAMOND, GlyphPaint::Outlined), - Suit::Spades => (SUIT_SPADE, GlyphPaint::Filled), - Suit::Clubs => (SUIT_CLUB, GlyphPaint::Outlined), + Suit::Hearts => (SUIT_RED, GlyphPaint::Filled), + Suit::Diamonds => (SUIT_RED, GlyphPaint::Outlined), + Suit::Spades => (SUIT_DARK, GlyphPaint::Filled), + Suit::Clubs => (SUIT_DARK, GlyphPaint::Outlined), } } diff --git a/solitaire_engine/src/card_plugin.rs b/solitaire_engine/src/card_plugin.rs index cdb1506..0d3bc6f 100644 --- a/solitaire_engine/src/card_plugin.rs +++ b/solitaire_engine/src/card_plugin.rs @@ -63,39 +63,33 @@ const FONT_SIZE_FRAC: f32 = 0.28; /// Card-face background — Terminal `#1a1a1a` (BG_ELEVATED). pub const CARD_FACE_COLOUR: Color = Color::srgb(0.102, 0.102, 0.102); -/// Suit colour for hearts — Terminal `#fb9fb1` (suit-pink). Per -/// the 4-colour deck convention, hearts and diamonds no longer -/// share a hue: hearts stay pink (the most strongly "red" of the -/// four base16 accents), diamonds pick up gold so a player -/// scanning the table can distinguish the suit by colour alone. -/// Kept as `RED_SUIT_COLOUR` for back-compat; semantically this -/// is now "the heart suit colour". -pub const RED_SUIT_COLOUR: Color = Color::srgb(0.984, 0.624, 0.694); -/// High-contrast variant of [`RED_SUIT_COLOUR`] — `#ff8aa0`. Lifted -/// chroma + luminance for the Settings → Accessibility → High- -/// contrast mode toggle. Spec at `design-system.md` §Accessibility -/// (#2): suit-red boosts from `#fb9fb1` to `#ff8aa0` so red suits -/// remain unambiguously distinguishable from foreground gray on -/// low-quality displays. Independent of `RED_SUIT_COLOUR_CBM` +/// Suit colour for hearts + diamonds — saturated red `#e35353`. +/// 2-colour traditional pairing (the "Microsoft Solitaire on dark +/// mode" feel) replacing the brief 4-colour-deck experiment that +/// shipped between v0.21.0 and this commit. Brighter and more +/// saturated than the v0.21.0 pink `#fb9fb1` so the cards read as +/// a "real solitaire deck" rather than a Terminal-pastel theme. +/// Visually distinct from `ACCENT_PRIMARY` (`#a54242` brick red, +/// darker) so chrome and suit don't read as the same hue. +pub const RED_SUIT_COLOUR: Color = Color::srgb(0.890, 0.325, 0.325); +/// High-contrast variant of [`RED_SUIT_COLOUR`] — `#ff6868`. Lifted +/// luminance for the Settings → Accessibility → High-contrast mode +/// toggle. Pre-2-colour-revert this was `#ff8aa0` (pink-salmon) +/// matching the v0.21.0 pink default; rebumped to a brighter red +/// so it reads as "more chromatic" than the new saturated default, +/// not "less saturated." Independent of `RED_SUIT_COLOUR_CBM` /// (lime) — high-contrast is *additive* over the default colour /// palette; CBM is a *replacement* of red with a hue-distinct /// alternative. The two modes can stack; CBM wins when both are on /// because the CBM lime is itself a high-contrast colour. -pub const RED_SUIT_COLOUR_HC: Color = Color::srgb(1.000, 0.541, 0.627); -/// Suit colour for diamonds — Terminal `#ddb26f` (gold, base09). -/// In the 4-colour deck split, diamonds break away from sharing -/// the "red" hue with hearts and pick up gold so the two former -/// red suits are visually distinguishable. -pub const DIAMOND_SUIT_COLOUR: Color = Color::srgb(0.867, 0.698, 0.435); -/// Suit colour for clubs — Terminal `#acc267` (lime, base0A). -/// In the 4-colour deck split, clubs break away from sharing the -/// "black" hue with spades and pick up lime so the two former -/// black suits are visually distinguishable. -pub const CLUB_SUIT_COLOUR: Color = Color::srgb(0.675, 0.761, 0.404); -/// Suit colour for spades — Terminal `#d0d0d0` (TEXT_PRIMARY). -/// Kept as `BLACK_SUIT_COLOUR` for back-compat; semantically this -/// is now "the spade suit colour" in the 4-colour deck split. -pub const BLACK_SUIT_COLOUR: Color = Color::srgb(0.816, 0.816, 0.816); +pub const RED_SUIT_COLOUR_HC: Color = Color::srgb(1.000, 0.408, 0.408); +/// Suit colour for spades + clubs — near-white `#e8e8e8`. Brighter +/// than `TEXT_PRIMARY` (`#d0d0d0`, foreground gray) so the +/// "black suit" reads as a distinct, chromatic-neutral counterpart +/// to the new saturated red, not as "the same gray as body text." +/// `TEXT_PRIMARY_HC` (`#f5f5f5`) is still brighter for the +/// high-contrast boost path. +pub const BLACK_SUIT_COLOUR: Color = Color::srgb(0.910, 0.910, 0.910); /// Pre-loaded [`Handle`]s for card face and back PNG textures. /// @@ -831,58 +825,38 @@ fn label_for(card: &Card) -> String { /// Suit colour for the rank/suit overlay rendered atop the constant /// fallback sprite (only fires under `MinimalPlugins` — production -/// renders the suit glyph baked into the PNG). The 4-colour deck -/// split assigns each suit a distinct base16-eighties accent so -/// players can identify the suit by hue alone: +/// renders the suit glyph baked into the PNG). 2-colour traditional +/// pairing — hearts + diamonds share the saturated red, clubs + +/// spades share the near-white. Two accessibility flags compose: /// -/// | Suit | Default colour | CBM swap | HC boost | -/// | -------- | ----------------------- | ------------- | ---------------- | -/// | Hearts | `RED_SUIT_COLOUR` pink | lime | `RED_SUIT_COLOUR_HC` | -/// | Diamonds | `DIAMOND_SUIT_COLOUR` gold | (no swap — already non-red-family) | (no boost — already mid-luminance) | -/// | Clubs | `CLUB_SUIT_COLOUR` lime | (no swap) | (no boost) | -/// | Spades | `BLACK_SUIT_COLOUR` gray | (no swap) | `TEXT_PRIMARY_HC` | -/// -/// Two independent accessibility flags compose: -/// -/// - `color_blind`: hearts swap from pink to `RED_SUIT_COLOUR_CBM` -/// (lime). The other three suits are already hue-distinct from -/// pink so they don't change. Note that CBM lime collides with -/// the club suit colour — players running CBM on the 4-colour -/// deck rely on the always-on filled-vs-outlined differentiation -/// (♥ filled, ♣ outlined) to distinguish hearts and clubs. -/// - `high_contrast`: hearts boost to `RED_SUIT_COLOUR_HC` -/// (`#ff8aa0`); spades boost from `#d0d0d0` to `#f5f5f5` -/// (`TEXT_PRIMARY_HC`). Diamonds (gold) and clubs (lime) are -/// already mid-luminance accents so no HC boost is defined for -/// them. +/// - `color_blind`: red-suit cards swap to `RED_SUIT_COLOUR_CBM` +/// (lime) — the "Settings toggle swaps red→lime" half of the +/// design system's colour-blind support. CBM is a hue-replacement +/// for red, so HC has no further effect on red when CBM is on +/// (the lime is itself a high-luminance colour). +/// - `high_contrast`: when CBM is off, red suits boost to +/// `RED_SUIT_COLOUR_HC` (`#ff6868`); black suits boost from +/// `#e8e8e8` (near-white) to `#f5f5f5` (`TEXT_PRIMARY_HC`). /// /// The other half of CBM support (always-on filled-vs-outlined /// glyph differentiation for ♥♠ vs ♦♣) is baked into the PNG art /// and has no constant-fallback equivalent. fn text_colour(card: &Card, color_blind: bool, high_contrast: bool) -> Color { - match card.suit { - Suit::Hearts => { - if color_blind { - // CBM lime replaces the pink for red-deficient - // readers; the always-on filled-vs-outlined split - // keeps hearts visually distinct from clubs (which - // are also lime in the 4-colour scheme). - RED_SUIT_COLOUR_CBM - } else if high_contrast { - RED_SUIT_COLOUR_HC - } else { - RED_SUIT_COLOUR - } - } - Suit::Diamonds => DIAMOND_SUIT_COLOUR, - Suit::Clubs => CLUB_SUIT_COLOUR, - Suit::Spades => { - if high_contrast { - TEXT_PRIMARY_HC - } else { - BLACK_SUIT_COLOUR - } + if card.suit.is_red() { + if color_blind { + // CBM lime wins — the colour-blind swap replaces the + // red hue entirely, and the lime is already high- + // luminance, so an HC boost on top has nothing to do. + RED_SUIT_COLOUR_CBM + } else if high_contrast { + RED_SUIT_COLOUR_HC + } else { + RED_SUIT_COLOUR } + } else if high_contrast { + TEXT_PRIMARY_HC + } else { + BLACK_SUIT_COLOUR } } @@ -1823,41 +1797,19 @@ mod tests { } #[test] - fn text_colour_4_colour_deck_assigns_each_suit_its_own_hue() { - // Pre-4-colour-deck this was two tests asserting hearts + - // diamonds shared `RED_SUIT_COLOUR` and clubs + spades - // shared `BLACK_SUIT_COLOUR`. The 4-colour split breaks - // both pairings: hearts pink, diamonds gold, clubs lime, - // spades gray. All four colours must be distinct so a - // player scanning the table can identify the suit by hue - // alone. + fn text_colour_is_red_for_hearts_and_diamonds() { let h = Card { id: 0, suit: Suit::Hearts, rank: Rank::Ace, face_up: true }; let d = Card { id: 0, suit: Suit::Diamonds, rank: Rank::Ace, face_up: true }; + assert_eq!(text_colour(&h, false, false), RED_SUIT_COLOUR); + assert_eq!(text_colour(&d, false, false), RED_SUIT_COLOUR); + } + + #[test] + fn text_colour_is_near_white_for_clubs_and_spades() { let c = Card { id: 0, suit: Suit::Clubs, rank: Rank::Ace, face_up: true }; let s = Card { id: 0, suit: Suit::Spades, rank: Rank::Ace, face_up: true }; - - assert_eq!(text_colour(&h, false, false), RED_SUIT_COLOUR); - assert_eq!(text_colour(&d, false, false), DIAMOND_SUIT_COLOUR); - assert_eq!(text_colour(&c, false, false), CLUB_SUIT_COLOUR); + assert_eq!(text_colour(&c, false, false), BLACK_SUIT_COLOUR); assert_eq!(text_colour(&s, false, false), BLACK_SUIT_COLOUR); - - // All four hues must be pairwise distinct — the visual - // identification gain of a 4-colour deck depends on hue - // separation, so this is the load-bearing invariant. - let colours = [ - text_colour(&h, false, false), - text_colour(&d, false, false), - text_colour(&c, false, false), - text_colour(&s, false, false), - ]; - for i in 0..colours.len() { - for j in (i + 1)..colours.len() { - assert_ne!( - colours[i], colours[j], - "4-colour deck requires every suit to have a distinct colour", - ); - } - } } #[test] @@ -2149,35 +2101,27 @@ mod tests { // ----------------------------------------------------------------------- #[test] - fn text_colour_color_blind_mode_swaps_hearts_to_lime() { - // Pre-4-colour-deck this test asserted both red suits - // (hearts + diamonds) swapped to lime under CBM. With - // hearts the only "red-family" suit now, CBM only - // affects hearts; diamonds is gold and stays gold. - let hearts = Card { id: 0, suit: Suit::Hearts, rank: Rank::Queen, face_up: true }; - let cbm_colour = text_colour(&hearts, true, false); + fn text_colour_color_blind_mode_swaps_red_suits_to_lime() { + let red_card = Card { id: 0, suit: Suit::Diamonds, rank: Rank::Queen, face_up: true }; + let cbm_colour = text_colour(&red_card, true, false); assert_eq!( cbm_colour, RED_SUIT_COLOUR_CBM, - "color-blind mode must replace the heart suit colour with the CBM lime", + "color-blind mode must replace the red suit colour with the CBM lime", ); assert_ne!( cbm_colour, RED_SUIT_COLOUR, - "CBM lime must be visibly distinct from the default heart suit colour", + "CBM lime must be visibly distinct from the default red suit colour", ); } #[test] - fn text_colour_color_blind_mode_does_not_change_non_heart_suits() { - // CBM only affects hearts in the 4-colour deck. Diamonds - // (gold), clubs (lime), and spades (gray) are already - // hue-distinct from the original heart pink and stay - // unchanged. - let d = Card { id: 0, suit: Suit::Diamonds, rank: Rank::Jack, face_up: true }; - let c = Card { id: 0, suit: Suit::Clubs, rank: Rank::Jack, face_up: true }; - let s = Card { id: 0, suit: Suit::Spades, rank: Rank::Jack, face_up: true }; - assert_eq!(text_colour(&d, true, false), DIAMOND_SUIT_COLOUR); - assert_eq!(text_colour(&c, true, false), CLUB_SUIT_COLOUR); - assert_eq!(text_colour(&s, true, false), BLACK_SUIT_COLOUR); + fn text_colour_color_blind_mode_does_not_change_dark_suits() { + let black_card = Card { id: 0, suit: Suit::Clubs, rank: Rank::Jack, face_up: true }; + assert_eq!( + text_colour(&black_card, true, false), + BLACK_SUIT_COLOUR, + "color-blind mode must not alter dark-suit text colour", + ); } // ----------------------------------------------------------------------- @@ -2217,48 +2161,31 @@ mod tests { } #[test] - fn text_colour_color_blind_wins_over_high_contrast_on_hearts() { - // When both modes are enabled on hearts, CBM lime wins over - // HC red because the CBM lime is itself a high-luminance - // accent and the HC boost would pick a different hue, - // defeating the purpose of the colour-blind swap. - let hearts = Card { id: 0, suit: Suit::Hearts, rank: Rank::Ace, face_up: true }; + fn text_colour_color_blind_wins_over_high_contrast_on_red_suits() { + // When both modes are enabled, red→lime (CBM) wins because + // the CBM lime is itself a high-luminance accent and the HC + // boost would pick a different hue, defeating the purpose of + // the colour-blind swap. + let red_card = Card { id: 0, suit: Suit::Diamonds, rank: Rank::Ace, face_up: true }; assert_eq!( - text_colour(&hearts, true, true), + text_colour(&red_card, true, true), RED_SUIT_COLOUR_CBM, "CBM lime must win over HC red when both modes are on", ); } #[test] - fn text_colour_high_contrast_alone_boosts_spades_under_cbm() { - // CBM doesn't touch spades, so HC remains the only source - // of variation for the spade row when both are on. (Pre- - // 4-colour-deck this test used Clubs; in the 4-colour - // scheme clubs is lime not gray, so the assertion shifted - // to the only suit that's still gray-family.) - let spades = Card { id: 0, suit: Suit::Spades, rank: Rank::King, face_up: true }; + fn text_colour_high_contrast_alone_boosts_dark_suits_under_cbm() { + // CBM doesn't touch the dark suits, so HC remains the only + // source of variation for the dark row when both are on. + let black_card = Card { id: 0, suit: Suit::Clubs, rank: Rank::King, face_up: true }; assert_eq!( - text_colour(&spades, true, true), + text_colour(&black_card, true, true), TEXT_PRIMARY_HC, - "with CBM + HC both on, spades still pick up the HC boost", + "with CBM + HC both on, dark suits still pick up the HC boost", ); } - #[test] - fn text_colour_diamonds_and_clubs_are_immune_to_accessibility_flags() { - // Diamonds (gold) and clubs (lime) are already mid-luminance - // hue-distinct accents, so neither CBM nor HC has a defined - // boost for them. Verify all four flag combinations leave - // them at their default suit colour. - let d = Card { id: 0, suit: Suit::Diamonds, rank: Rank::King, face_up: true }; - let c = Card { id: 0, suit: Suit::Clubs, rank: Rank::King, face_up: true }; - for (cbm, hc) in [(false, false), (false, true), (true, false), (true, true)] { - assert_eq!(text_colour(&d, cbm, hc), DIAMOND_SUIT_COLOUR); - assert_eq!(text_colour(&c, cbm, hc), CLUB_SUIT_COLOUR); - } - } - // ----------------------------------------------------------------------- // label_visibility (pure) // ----------------------------------------------------------------------- diff --git a/solitaire_engine/tests/card_face_svg_pin.rs b/solitaire_engine/tests/card_face_svg_pin.rs index 377f326..6002935 100644 --- a/solitaire_engine/tests/card_face_svg_pin.rs +++ b/solitaire_engine/tests/card_face_svg_pin.rs @@ -24,58 +24,58 @@ use solitaire_engine::assets::card_face_svg::{ use solitaire_engine::assets::rasterize_svg; const EXPECTED: &[(&str, u64)] = &[ - ("face_AC", 0x287e3293f95990a5), - ("face_2C", 0x01c66d8e461fb0c4), - ("face_3C", 0xfdae6be53af8b7c8), - ("face_4C", 0x4b2a7aef966c6cc2), - ("face_5C", 0xa4ca0ce3759b5cc9), - ("face_6C", 0xe1a730d1ce810314), - ("face_7C", 0x9c8de5c7d014eca3), - ("face_8C", 0x39e09f90c957b192), - ("face_9C", 0xd6627707fb2d5079), - ("face_10C", 0xbe8411c60411195c), - ("face_JC", 0x7c33abf5619477ac), - ("face_QC", 0xe75657d63c99a892), - ("face_KC", 0xf4a445b771026496), - ("face_AD", 0xad8820c694c464d7), - ("face_2D", 0xef771dbb39ae4f5a), - ("face_3D", 0xe955ec9a96e1256a), - ("face_4D", 0x6bb5979ef6004957), - ("face_5D", 0x55715fd2353b2126), - ("face_6D", 0x87fbd6efce1b1f9f), - ("face_7D", 0xabb2d52d363e93ab), - ("face_8D", 0xde78161ee9093b05), - ("face_9D", 0x1475987ba1e66036), - ("face_10D", 0x3a52d7fda7158aeb), - ("face_JD", 0xc9078d8a7b2e6372), - ("face_QD", 0x84c9011b916fdbe8), - ("face_KD", 0xbcd20dbb6b1c8cdf), - ("face_AH", 0x2c8e05964b5e3a5f), - ("face_2H", 0xb44e68b79bb3842e), - ("face_3H", 0x15226ed29769e1c4), - ("face_4H", 0xe28c86ba92a3aee9), - ("face_5H", 0x18276e48b28d0f6b), - ("face_6H", 0xcca5e60e65724eaa), - ("face_7H", 0x7f3eee634137f13a), - ("face_8H", 0x8974515a8904d6c4), - ("face_9H", 0x2f8155cd7690d4b9), - ("face_10H", 0x78142f898fd66578), - ("face_JH", 0x5e6df78654a1de73), - ("face_QH", 0xc231ae8c25d877a9), - ("face_KH", 0x55a0a772baf3e97f), - ("face_AS", 0xc90e798aebdc1c5f), - ("face_2S", 0x4178c699a726ea70), - ("face_3S", 0xdfcd34480bb06f4c), - ("face_4S", 0xdbd4938042afb02e), - ("face_5S", 0x8741456ab1ec58ab), - ("face_6S", 0x6d2632f648f1c34d), - ("face_7S", 0x3c05c70ff3d93ea6), - ("face_8S", 0x12d7f456efbaffe0), - ("face_9S", 0x11b6ade208b8fa12), - ("face_10S", 0x475d4110834b6b2a), - ("face_JS", 0x52525a2200c07246), - ("face_QS", 0xb4f0251a2757cbb1), - ("face_KS", 0x1e1975919bb9a029), + ("face_AC", 0xecfed9881dab58cc), + ("face_2C", 0x9d226854e375a071), + ("face_3C", 0x57635bd3396b1c6f), + ("face_4C", 0x3db4ebca46202411), + ("face_5C", 0x6aaaa97f8d64d141), + ("face_6C", 0x45ab0e3692f5086f), + ("face_7C", 0xb6c6d47a9c41c042), + ("face_8C", 0x95e467bebfe1f43f), + ("face_9C", 0x78c67114e728f726), + ("face_10C", 0x59ea22af2a519731), + ("face_JC", 0x0757d9cae053863e), + ("face_QC", 0xc3de9e10c2e8819e), + ("face_KC", 0xefd2e9dd4c6f734f), + ("face_AD", 0x95e2954416f7051d), + ("face_2D", 0xfa494e129a7d130b), + ("face_3D", 0x493f32ac1b4f1365), + ("face_4D", 0x1303407818e3896d), + ("face_5D", 0x3c68bc01d5661c9b), + ("face_6D", 0x4ae0872812942c95), + ("face_7D", 0xf4a040f288b53a3d), + ("face_8D", 0xb5964ffbcc1834c0), + ("face_9D", 0xfc2b244f9e6c987c), + ("face_10D", 0xc9648dfd2f74e387), + ("face_JD", 0x055c9e4b1f56b2b4), + ("face_QD", 0x05d0d7e3be132b36), + ("face_KD", 0x540753328025961e), + ("face_AH", 0x8ac76ac84674dae6), + ("face_2H", 0xf20c188bc5cf1008), + ("face_3H", 0xc604901c0da15c0e), + ("face_4H", 0x371c115d9292fa56), + ("face_5H", 0x5cabef7840c6e378), + ("face_6H", 0x48948872acab515e), + ("face_7H", 0x49e96e37591f8c86), + ("face_8H", 0xe30b740fd0f3575b), + ("face_9H", 0x4067a838eeff2ea7), + ("face_10H", 0xd9e9913fa5d9b974), + ("face_JH", 0xe4344bff58d04e7f), + ("face_QH", 0xf33df3f193827f25), + ("face_KH", 0x8ada887b665fa3fd), + ("face_AS", 0x586d5587ad518f46), + ("face_2S", 0xbc0deb204e690d57), + ("face_3S", 0xac04b5df8741d889), + ("face_4S", 0x6a2ebcdb517b7ab7), + ("face_5S", 0x9868f72763bbdae7), + ("face_6S", 0x9a4c6842e0cbc489), + ("face_7S", 0x15d17732dadf2ec0), + ("face_8S", 0xb581df40dace0e59), + ("face_9S", 0xce92a55ddcc6b4fc), + ("face_10S", 0x1d92560a36938e97), + ("face_JS", 0xd339b7a54139f9d4), + ("face_QS", 0x59eae032af251c74), + ("face_KS", 0x901e0d1ace6ff6a9), ("back_0", 0xfd1742ebe330481a), ("back_1", 0x446fdc0a3c83a03a), ("back_2", 0xcf188fdec9f5819a),