From a292a7ead081c50227448c399d2c79c56acae7c1 Mon Sep 17 00:00:00 2001 From: funman300 Date: Fri, 8 May 2026 10:30:35 -0700 Subject: [PATCH] feat(engine): swap ACCENT_PRIMARY from cyan #6fc2ef to brick red #a54242 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Project-wide palette shift at user request. Replaces the cyan primary accent everywhere it surfaces — splash boot screen, home menu glyphs, action chevrons, replay overlay banner + scrub fill + chip border, achievement checkmarks, leaderboard #1 indicator, radial menu fill, focus ring, card-back canonical badge, etc. — with `#a54242` from the same base16-eighties family as the existing pink suit colour. Knock-on changes that all land in this commit per the lockstep rule: - ui_theme.rs: ACCENT_PRIMARY (#a54242), ACCENT_PRIMARY_HOVER (#c25e5e brightened companion), FOCUS_RING (same hue, 0.85 alpha). Module-level palette comment + STOCK_BADGE_FG + CARD_SHADOW_ALPHA_DRAG doc strings updated to match. - card_plugin.rs: card_back_colour(0) now returns the brick-red ACCENT_PRIMARY (was cyan). RED_SUIT_COLOUR_CBM swapped from cyan to lime #acc267 — the CBM alternative needs to stay hue-distinct from the new red-family primary, lime is the next-best non-red base16-eighties accent. text_colour doc + CBM tests renamed cyan→lime in lockstep (text_colour_color_blind_mode_swaps_red_suits_to_lime). - card_face_svg.rs: BACK_ACCENTS[0] now "#a54242" (canonical Terminal back). - splash_plugin.rs / ui_modal.rs / replay_overlay.rs / selection_plugin.rs: descriptive "cyan" comments swapped to "accent" / "primary-accent" wording so the doc strings stay decoupled from any specific hue. Future palette tweaks won't require comment churn. - design-system.md: YAML token frontmatter updated (primary, surface-tint, suit-red-cb, primary-container, on-primary-container, inverse-primary). Palette table gains a project-specific `base08` slot for the new red. CTA / Selection / Card-back badge / Primary button / Bottom-bar active-icon / glow / CBM swap text all retuned. Historical references preserved (e.g. "Was cyan #6fc2ef before the 2026-05-08 swap") so the audit trail stays in the spec. - card_face_svg_pin.rs: rebaselined. Exactly one hash drift (back_0 — the canonical Terminal back's badge changed colour). Other 56 hashes identical (face SVGs don't reference the accent; back_1..4 use unchanged accents). The one-hash-drift signal confirms the change scope was surgical. Workspace clippy + cargo test --workspace clean, 1184 passing. Co-Authored-By: Claude Opus 4.7 --- assets/cards/backs/back_0.png | Bin 3142 -> 3140 bytes docs/ui-mockups/design-system.md | 38 +++++++++--------- .../assets/themes/default/back.svg | 2 +- solitaire_engine/src/assets/card_face_svg.rs | 8 ++-- solitaire_engine/src/card_plugin.rs | 28 ++++++++----- solitaire_engine/src/replay_overlay.rs | 10 ++--- solitaire_engine/src/selection_plugin.rs | 6 +-- solitaire_engine/src/splash_plugin.rs | 10 ++--- solitaire_engine/src/ui_modal.rs | 10 ++--- solitaire_engine/src/ui_theme.rs | 29 +++++++------ solitaire_engine/tests/card_face_svg_pin.rs | 2 +- 11 files changed, 79 insertions(+), 64 deletions(-) diff --git a/assets/cards/backs/back_0.png b/assets/cards/backs/back_0.png index fc207c3d51826cf724623f5070d96b588fc4d8d6..3bd60fd0888d8988cd6e21b9b602738399334ff8 100644 GIT binary patch literal 3140 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 L*R!vk0{;9r7AbLq diff --git a/docs/ui-mockups/design-system.md b/docs/ui-mockups/design-system.md index 808d54f..af58be3 100644 --- a/docs/ui-mockups/design-system.md +++ b/docs/ui-mockups/design-system.md @@ -15,12 +15,12 @@ colors: inverse-on-surface: '#151515' outline: '#505050' outline-variant: '#353535' - surface-tint: '#6fc2ef' - primary: '#6fc2ef' + surface-tint: '#a54242' + primary: '#a54242' on-primary: '#151515' - primary-container: '#1f3a4a' - on-primary-container: '#a8dcf5' - inverse-primary: '#0e6e99' + primary-container: '#3a1f1f' + on-primary-container: '#d5a8a8' + inverse-primary: '#993e3e' secondary: '#acc267' on-secondary: '#151515' secondary-container: '#2a3320' @@ -38,7 +38,7 @@ colors: surface-variant: '#353535' suit-red: '#fb9fb1' suit-black: '#d0d0d0' - suit-red-cb: '#6fc2ef' + suit-red-cb: '#acc267' highlight-valid: '#acc267' highlight-celebration: '#e1a3ee' highlight-warning: '#ddb26f' @@ -119,14 +119,16 @@ The palette is base16-eighties — a 16-slot terminal palette where indices 00 | base09 | `#ddb26f` | orange — used for warning chips | | base0A | `#acc267` | yellow/lime — used for `highlight-valid` (drag targets, valid moves) | | base0B | `#12cfc0` | green/teal — used for `highlight-info` (toasts, neutral status) | -| base0C | `#6fc2ef` | cyan/sky — primary CTA, focus ring, `selection`, `suit-red-cb` (color-blind tinted red) | +| base0C | `#6fc2ef` | cyan/sky — historically the primary CTA; now reserved for ad-hoc accents only | | base0D | `#6fc2ef` | (alias) | +| base08 (project) | `#a54242` | brick red — primary CTA, focus ring, `selection` (project-specific extension; the base16-eighties `base08` slot is `#fb9fb1` pink which we keep as `error`/`suit-red`) | +| `suit-red-cb` slot | `#acc267` | lime — color-blind-mode swap for red suits (was `#6fc2ef` cyan before the 2026-05-08 primary-accent swap; lime is the next-best non-red base16-eighties accent) | | base0E | `#e1a3ee` | violet — used for celebration (level-up, achievement unlock) | | base0F | `#fb9fb1` | (alias) | ### Semantic assignments -- **CTA / Primary action**: cyan `#6fc2ef`. Reserved for "Play," "New Game," "Save," "Resume," and the focus ring on selected cards. Never used decoratively. +- **CTA / Primary action**: brick red `#a54242`. Reserved for "Play," "New Game," "Save," "Resume," and the focus ring on selected cards. Never used decoratively. (Was cyan `#6fc2ef` before the 2026-05-08 swap.) - **Valid-move / drag-target highlight**: lime `#acc267`. Reserved for in-game feedback only. Never appears in chrome. - **Celebration**: lavender `#e1a3ee`. Used for level-up flashes, achievement unlock cards, and the daily-streak chip when the streak is active. Quiet otherwise. - **Warning / soft alert**: gold `#ddb26f`. Used for "challenge expires in N minutes" chips, sync-pending status, and the daily-seed countdown. @@ -139,14 +141,14 @@ The palette is base16-eighties — a 16-slot terminal palette where indices 00 | Suit | Default | Color-blind mode | Glyph differentiation | |---|---|---|---| -| Hearts | `#fb9fb1` (pink) | `#6fc2ef` (cyan) | Solid filled glyph | -| Diamonds | `#fb9fb1` (pink) | `#6fc2ef` (cyan) | **Outlined glyph (1.5px stroke)** | +| Hearts | `#fb9fb1` (pink) | `#acc267` (lime) | Solid filled glyph | +| Diamonds | `#fb9fb1` (pink) | `#acc267` (lime) | **Outlined glyph (1.5px stroke)** | | Spades | `#d0d0d0` (foreground) | `#d0d0d0` | Solid filled glyph | | Clubs | `#d0d0d0` (foreground) | `#d0d0d0` | **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 red→cyan; it does not turn the outlined glyphs on or off, because outlined glyphs are always on. +The "color-blind mode" toggle in Settings only swaps red→lime; it 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 @@ -177,7 +179,7 @@ Depth is created through **tonal layering and 1px outlines**, not blur shadows. - **Level 0 (Background)**: the `#151515` base canvas. - **Level 1 (Tableau slots, empty piles)**: 1px dashed outline in `#353535`. Empty foundations show a faint suit glyph at 12% opacity inside the outline. - **Level 2 (Cards at rest)**: solid `#1a1a1a` fill, 1px solid border in the suit color (so the suit is detectable at a glance even if the card is partially obscured). -- **Level 3 (Active / dragged card)**: same border, but glow effect: 0 0 12px of `#6fc2ef` at 40% opacity. **No scale transform** — flatness preserved. Z-index lifts above siblings. +- **Level 3 (Active / dragged card)**: same border, but glow effect: 0 0 12px of `#a54242` at 40% opacity. **No scale transform** — flatness preserved. Z-index lifts above siblings. - **Modals**: full-screen with backdrop `#151515` at 95% opacity (just enough to dim the table without blurring it). Modal panel is `#202020` with a 1px `#505050` border — like a terminal pane. - **Toasts**: bottom of screen, `#202020` fill, 1px border in the toast's accent color (info=teal, warning=gold, error=pink, celebration=lavender). 16px monospaced caption. @@ -193,7 +195,7 @@ The shape language is **soft-rounded but tight**: - **Avatars / circular indicators**: `rounded-full`. - **Card-back pattern corners**: matches the card's `rounded-md`. -Selection highlights use a **2px inset stroke** in `#6fc2ef` following the host shape's corner radius. Never an outer stroke — the outer stroke is reserved for the suit-color hairline. +Selection highlights use a **2px inset stroke** in `#a54242` following the host shape's corner radius. Never an outer stroke — the outer stroke is reserved for the suit-color hairline. ## Motion @@ -228,17 +230,17 @@ Flat face design. - Background: `#151515` - Pattern: horizontal scanlines at 2px pitch in `#1a1a1a` (1px line, 1px gap), full bleed - Border: 1px solid `#353535` -- Top-left badge: a 12×16px solid `#6fc2ef` block (the "terminal cursor"), 6px from the corner +- Top-left badge: a 12×16px solid `#a54242` block (the "terminal cursor"), 6px from the corner - Bottom-right monogram: the characters `▌RS` in JetBrains Mono 12px, color `#505050`, 6px from the corner - Corner radius: 8px (matches face) ### Primary Buttons -Solid `#6fc2ef` fill, `#151515` text, JetBrains Mono Medium 14px uppercase with `+0.08em` tracking. 4px corner radius. Pressed state: darken to `#5aa9d4`. Disabled: `#353535` fill, `#505050` text. +Solid `#a54242` fill, `#151515` text, JetBrains Mono Medium 14px uppercase with `+0.08em` tracking. 4px corner radius. Pressed state: darken to `#7a3030`. Disabled: `#353535` fill, `#505050` text. ### Secondary Buttons -Transparent fill, 1px `#505050` border, `#d0d0d0` text. Hover/press: border becomes `#6fc2ef`, text becomes `#6fc2ef`. +Transparent fill, 1px `#505050` border, `#d0d0d0` text. Hover/press: border becomes `#a54242`, text becomes `#a54242`. ### HUD Chips @@ -258,7 +260,7 @@ Full-screen backdrop at 95% opacity. Centered panel: `#202020` fill, 1px `#50505 ### Navigation Bar -Fixed at the bottom of in-game screens. Height: 64px. `#202020` fill, 1px top border in `#353535`. Four icon buttons: Undo / Hint / New / Auto-complete. Icons: 24px, 1.5px stroke weight, color `#d0d0d0`. Active/pressed: icon color `#6fc2ef`. +Fixed at the bottom of in-game screens. Height: 64px. `#202020` fill, 1px top border in `#353535`. Four icon buttons: Undo / Hint / New / Auto-complete. Icons: 24px, 1.5px stroke weight, color `#d0d0d0`. Active/pressed: icon color `#a54242`. ### Status / Sync Indicator @@ -270,7 +272,7 @@ Top-right corner of the HUD: a 6px circular dot. ## Accessibility -1. **Color-blind mode** (Settings → Gameplay): swaps red suits' default `#fb9fb1` for `#6fc2ef`. Outlined-glyph differentiation remains active in *all* modes. +1. **Color-blind mode** (Settings → Gameplay): swaps red suits' default `#fb9fb1` 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/back.svg b/solitaire_engine/assets/themes/default/back.svg index 4bdc34e..993e14a 100644 --- a/solitaire_engine/assets/themes/default/back.svg +++ b/solitaire_engine/assets/themes/default/back.svg @@ -13,7 +13,7 @@ fill="url(#scanlines)"/> - + Color { match selected_card_back { - 0 => Color::srgb(0.435, 0.761, 0.937), // #6fc2ef cyan (Terminal canonical) + 0 => Color::srgb(0.647, 0.259, 0.259), // #a54242 brick red (Terminal canonical, ACCENT_PRIMARY) 1 => Color::srgb(0.675, 0.761, 0.404), // #acc267 lime 2 => Color::srgb(0.882, 0.639, 0.933), // #e1a3ee lavender 3 => Color::srgb(0.984, 0.624, 0.694), // #fb9fb1 pink @@ -792,11 +798,15 @@ 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). When `color_blind` is -/// enabled, red-suit cards swap to `RED_SUIT_COLOUR_CBM` (cyan) — the -/// "Settings toggle swaps red→cyan" half of the design system's +/// enabled, 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. The other half (always-on filled-vs-outlined /// glyph differentiation for ♥♠ vs ♦♣) is baked into the PNG art and /// has no constant-fallback equivalent. +/// +/// The CBM swap is lime (not the new brick-red `ACCENT_PRIMARY`) +/// because the primary accent is itself red-family — the CBM +/// alternative needs to be hue-distinct from the original red suit. fn text_colour(card: &Card, color_blind: bool) -> Color { if card.suit.is_red() { if color_blind { @@ -2066,20 +2076,20 @@ mod tests { // Pre-Terminal these were `face_colour` tests asserting that CBM // tinted the *face background* of red-suit cards. The Terminal // design system moves CBM differentiation into the suit *glyph* - // colour (red→cyan), so these tests now exercise `text_colour`. + // colour (red→lime), so these tests now exercise `text_colour`. // ----------------------------------------------------------------------- #[test] - fn text_colour_color_blind_mode_swaps_red_suits_to_cyan() { + 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); assert_eq!( cbm_colour, RED_SUIT_COLOUR_CBM, - "color-blind mode must replace the red suit colour with the CBM cyan", + "color-blind mode must replace the red suit colour with the CBM lime", ); assert_ne!( cbm_colour, RED_SUIT_COLOUR, - "CBM red must be visibly distinct from the default red suit colour", + "CBM lime must be visibly distinct from the default red suit colour", ); } diff --git a/solitaire_engine/src/replay_overlay.rs b/solitaire_engine/src/replay_overlay.rs index a444af5..4c63ea9 100644 --- a/solitaire_engine/src/replay_overlay.rs +++ b/solitaire_engine/src/replay_overlay.rs @@ -5,7 +5,7 @@ //! - A "▌ replay" label on the left so the player knows the surface is //! under playback control rather than live input. //! - A "MOVE N/M" progress chip in the centre, recomputed every frame -//! the cursor advances and bordered in cyan ACCENT_PRIMARY so it +//! the cursor advances and bordered in `ACCENT_PRIMARY` so it //! reads as a discrete callout. //! - A "Stop" button on the right that aborts playback and returns //! control to the player. @@ -105,7 +105,7 @@ pub struct ReplayStopButton; #[derive(Component, Debug)] pub struct ReplayOverlayGameCaption; -/// Marker on the cyan "fill" of the bottom-edge scrub bar. The +/// Marker on the accent "fill" of the bottom-edge scrub bar. The /// `Node`'s `width` is rewritten every frame the cursor advances to /// `cursor / total` of the bar's full width, so the player has a /// continuous visual cue of how far through the replay they are. @@ -250,7 +250,7 @@ fn spawn_overlay( ..default() }) .with_children(|row| { - // Left: column with the cyan "▌ replay" headline + // Left: column with the accent "▌ replay" headline // above and a small `GAME #YYYY-DDD` caption below. // The caption mirrors the mockup's right-anchored // game identifier but stays visually grouped with @@ -315,7 +315,7 @@ fn spawn_overlay( // Right: Stop button. Tertiary variant — the // action is available but not the loudest element - // in the banner; the "Replay" cyan accent owns + // in the banner; the "Replay" primary accent owns // that slot. `spawn_modal_button` gives us hover / // press paint and focus rings for free via the // existing `UiModalPlugin` paint system. @@ -425,7 +425,7 @@ fn update_progress_text( } } -/// Repaints the bottom-edge cyan scrub fill to mirror cursor progress. +/// Repaints the bottom-edge accent scrub fill to mirror cursor progress. /// Same change-detection guard as the text updaters — the overlay /// already early-exits when nothing moved, so an idle replay leaves the /// scrub bar's `Node` untouched. diff --git a/solitaire_engine/src/selection_plugin.rs b/solitaire_engine/src/selection_plugin.rs index a6fd588..c121ad5 100644 --- a/solitaire_engine/src/selection_plugin.rs +++ b/solitaire_engine/src/selection_plugin.rs @@ -6,7 +6,7 @@ //! //! 1. [`SelectionState`] tracks the *source-pick* mode. `Tab` / `Shift+Tab` //! cycles a focus through piles that have a face-up draggable top card. -//! The focused card is decorated with a cyan [`SelectionHighlight`]. +//! The focused card is decorated with an accent-coloured [`SelectionHighlight`]. //! //! 2. [`KeyboardDragState`] tracks the *destination-pick* mode. Pressing //! `Enter` while a pile is focused enters @@ -634,7 +634,7 @@ fn clear_selection_on_state_change( /// Maintains the `SelectionHighlight` outline sprite. /// -/// When a pile is selected (source-pick mode), a cyan sprite is placed +/// When a pile is selected (source-pick mode), an accent-coloured sprite is placed /// at the selected card's position. While /// [`KeyboardDragState::Lifted`] the source highlight tints gold and a /// second highlight follows the focused destination's top card — visually @@ -662,7 +662,7 @@ fn update_selection_highlight( let card_size = layout.0.card_size; // Highlight tints follow the Terminal palette's semantic state - // tokens: cyan focus/selection while picking the source, gold + // tokens: ACCENT_PRIMARY focus/selection while picking the source, gold // attention/commitment once the cards are lifted, lime valid-move // tint on the destination. Alphas are kept non-zero so the card // face beneath remains readable through the wash. diff --git a/solitaire_engine/src/splash_plugin.rs b/solitaire_engine/src/splash_plugin.rs index 3e9e786..be9f881 100644 --- a/solitaire_engine/src/splash_plugin.rs +++ b/solitaire_engine/src/splash_plugin.rs @@ -1,7 +1,7 @@ //! Launch splash overlay. //! //! On app start the engine spawns a fullscreen, high-Z overlay that -//! reads the Terminal-style "boot screen" — a cyan cursor block, the +//! reads the Terminal-style "boot screen" — an accent-coloured cursor block, the //! "Solitaire Quest" wordmark, a short fixture boot log, a progress //! bar, and a footer with the design-system palette swatches and the //! build version. The overlay fades in over 300 ms, holds for ~1 s, @@ -470,8 +470,8 @@ fn spawn_check_row(parent: &mut ChildSpawnerCommands, line_font: &TextFont, labe /// "▌ ready_" line — visual signature of "boot complete, awaiting /// input". The leading `▌` glyph picks up `TEXT_PRIMARY` rather than -/// `ACCENT_PRIMARY` so it doesn't compete with the big cyan cursor in -/// the header; the *trailing* 6×12 px cyan pulse Node ([`SplashCursorPulse`]) +/// `ACCENT_PRIMARY` so it doesn't compete with the big accent cursor in +/// the header; the *trailing* 6×12 px accent pulse Node ([`SplashCursorPulse`]) /// is what carries the "alive, blinking" signal called for by the /// mockup. The pulse's alpha is multiplied with the global fade /// timeline by [`pulse_splash_cursor`] so it never fights the @@ -492,7 +492,7 @@ fn spawn_ready_row(parent: &mut ChildSpawnerCommands, line_font: &TextFont) { line_font.clone(), TextColor(transparent(TEXT_PRIMARY)), )); - // Trailing 6×12 cyan pulse cursor. Node-with-explicit- + // Trailing 6×12 accent pulse cursor. Node-with-explicit- // dimensions rather than a `█` text glyph so the size // doesn't drift with the line font; matches the mockup's // 6×12 px spec literally. Pulse animation lives in @@ -511,7 +511,7 @@ fn spawn_ready_row(parent: &mut ChildSpawnerCommands, line_font: &TextFont) { } /// Progress bar — a 1 px tall track in `BORDER_SUBTLE` with a 100 %- -/// width cyan fill, plus a `DONE · 247 ASSETS` caption right-aligned +/// width accent fill, plus a `DONE · 247 ASSETS` caption right-aligned /// below. The "247" is fixture text; the bar is decorative, not a /// real progress signal. Capped at 720 px width on desktop. fn spawn_progress_bar(parent: &mut ChildSpawnerCommands, line_font: &TextFont) { diff --git a/solitaire_engine/src/ui_modal.rs b/solitaire_engine/src/ui_modal.rs index 79cc1b9..ee1177d 100644 --- a/solitaire_engine/src/ui_modal.rs +++ b/solitaire_engine/src/ui_modal.rs @@ -333,17 +333,17 @@ pub fn spawn_modal_button( }; let label_color = match variant { - // Primary buttons sit on the cyan accent — `BG_BASE` text on - // top reads well and passes AAA contrast against `#6fc2ef`. + // Primary buttons sit on the brick-red accent — `BG_BASE` text on + // top reads well and passes AAA contrast against `#a54242`. ButtonVariant::Primary => BG_BASE, ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_PRIMARY, }; let caption_color = match variant { - // Muted near-black on the cyan Primary so the hotkey chip reads + // Muted near-black on the red Primary so the hotkey chip reads // as a secondary detail without disappearing. Deliberately a // pure-black-at-alpha rather than `BG_BASE.with_alpha(...)`: - // `BG_BASE` is `#151515` (not 0,0,0), so the alpha-on-cyan - // composite would tint slightly cooler than intended here. + // `BG_BASE` is `#151515` (not 0,0,0), so the alpha-on-accent + // composite would tint slightly off from intended here. ButtonVariant::Primary => Color::srgba(0.0, 0.0, 0.0, 0.55), ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_SECONDARY, }; diff --git a/solitaire_engine/src/ui_theme.rs b/solitaire_engine/src/ui_theme.rs index 83ec616..7149086 100644 --- a/solitaire_engine/src/ui_theme.rs +++ b/solitaire_engine/src/ui_theme.rs @@ -21,8 +21,8 @@ use bevy::prelude::Val; use solitaire_data::AnimSpeed; // --------------------------------------------------------------------------- -// Colours — Terminal (base16-eighties): near-black surface ramp with a cyan -// primary accent and lime/lavender/gold/teal/pink semantic accents. +// Colours — Terminal (base16-eighties): near-black surface ramp with a brick- +// red primary accent and lime/lavender/gold/teal/pink semantic accents. // --------------------------------------------------------------------------- /// Window backstop and the default text colour on top of `ACCENT_PRIMARY`. @@ -67,14 +67,17 @@ pub const TEXT_SECONDARY: Color = Color::srgb(0.627, 0.627, 0.627); /// Disabled text — greyed-out buttons, locked items. `#505050`. pub const TEXT_DISABLED: Color = Color::srgb(0.314, 0.314, 0.314); -/// Cyan primary accent — the CTA colour of the system. Reserved for -/// primary actions (Play, Resume, Save), focus rings, and selection. -/// `BG_BASE` text on top of this colour passes AAA contrast. `#6fc2ef`. -pub const ACCENT_PRIMARY: Color = Color::srgb(0.435, 0.761, 0.937); +/// Brick-red primary accent — the CTA colour of the system. Reserved +/// for primary actions (Play, Resume, Save), focus rings, and +/// selection. `#a54242` (base16-eighties `base08`). Pre-2026-05-08 +/// this slot was cyan `#6fc2ef`; the swap was a project-wide +/// palette decision recorded in `design-system.md` and the +/// SESSION_HANDOFF entry that followed Option D. +pub const ACCENT_PRIMARY: Color = Color::srgb(0.647, 0.259, 0.259); /// Brightened `ACCENT_PRIMARY` for hover states on primary buttons. -/// Picks up luminance while keeping the same hue. `#a8dcf5`. -pub const ACCENT_PRIMARY_HOVER: Color = Color::srgb(0.659, 0.863, 0.961); +/// Picks up luminance while keeping the same hue. `#c25e5e`. +pub const ACCENT_PRIMARY_HOVER: Color = Color::srgb(0.761, 0.369, 0.369); /// Lavender secondary accent — celebratory states (level-up, /// achievement unlocked, streak milestones). Used sparingly so it stays @@ -134,8 +137,8 @@ pub const STOCK_BADGE_BG: Color = BG_ELEVATED_HI; /// Foreground (text) colour of the stock-pile remaining-count chip. /// /// `ACCENT_PRIMARY` keeps the chip readable against the elevated -/// surface background and matches the cyan accent already used for -/// other "look here" callouts. +/// surface background and matches the primary accent already used +/// for other "look here" callouts. pub const STOCK_BADGE_FG: Color = ACCENT_PRIMARY; /// Sprite-space `Transform.z` for the stock-pile remaining-count chip. @@ -172,8 +175,8 @@ pub const CARD_SHADOW_ALPHA_IDLE: f32 = 0.0; /// Alpha for the lifted/dragged card shadow. Set to 0 for the same /// reason as [`CARD_SHADOW_ALPHA_IDLE`]. Drag affordance under the -/// Terminal system is the cyan focus glow + z-index lift, not a deeper -/// shadow. +/// Terminal system is the primary-accent focus glow + z-index lift, +/// not a deeper shadow. pub const CARD_SHADOW_ALPHA_DRAG: f32 = 0.0; /// World-space pixel offset of the resting-state card shadow relative to @@ -217,7 +220,7 @@ pub const BORDER_STRONG: Color = Color::srgba(0.314, 0.314, 0.314, 1.0); /// (matches `ACCENT_PRIMARY`) at 85% alpha so the ring stays legible /// against both elevated surfaces and the modal scrim backdrop. /// `rgba(111, 194, 239, 0.85)`. -pub const FOCUS_RING: Color = Color::srgba(0.435, 0.761, 0.937, 0.85); +pub const FOCUS_RING: Color = Color::srgba(0.647, 0.259, 0.259, 0.85); // --------------------------------------------------------------------------- // Typography scale (px) — 5 rungs replace the prior diff --git a/solitaire_engine/tests/card_face_svg_pin.rs b/solitaire_engine/tests/card_face_svg_pin.rs index ab6103f..3768e3b 100644 --- a/solitaire_engine/tests/card_face_svg_pin.rs +++ b/solitaire_engine/tests/card_face_svg_pin.rs @@ -76,7 +76,7 @@ const EXPECTED: &[(&str, u64)] = &[ ("face_JS", 0x52525a2200c07246), ("face_QS", 0xb4f0251a2757cbb1), ("face_KS", 0x1e1975919bb9a029), - ("back_0", 0xf698d0e161eae13a), + ("back_0", 0xfd1742ebe330481a), ("back_1", 0x446fdc0a3c83a03a), ("back_2", 0xcf188fdec9f5819a), ("back_3", 0xcaffd02af141743a),