feat(engine): swap ACCENT_PRIMARY from cyan #6fc2ef to brick red #a54242
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 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -15,12 +15,12 @@ colors:
|
|||||||
inverse-on-surface: '#151515'
|
inverse-on-surface: '#151515'
|
||||||
outline: '#505050'
|
outline: '#505050'
|
||||||
outline-variant: '#353535'
|
outline-variant: '#353535'
|
||||||
surface-tint: '#6fc2ef'
|
surface-tint: '#a54242'
|
||||||
primary: '#6fc2ef'
|
primary: '#a54242'
|
||||||
on-primary: '#151515'
|
on-primary: '#151515'
|
||||||
primary-container: '#1f3a4a'
|
primary-container: '#3a1f1f'
|
||||||
on-primary-container: '#a8dcf5'
|
on-primary-container: '#d5a8a8'
|
||||||
inverse-primary: '#0e6e99'
|
inverse-primary: '#993e3e'
|
||||||
secondary: '#acc267'
|
secondary: '#acc267'
|
||||||
on-secondary: '#151515'
|
on-secondary: '#151515'
|
||||||
secondary-container: '#2a3320'
|
secondary-container: '#2a3320'
|
||||||
@@ -38,7 +38,7 @@ colors:
|
|||||||
surface-variant: '#353535'
|
surface-variant: '#353535'
|
||||||
suit-red: '#fb9fb1'
|
suit-red: '#fb9fb1'
|
||||||
suit-black: '#d0d0d0'
|
suit-black: '#d0d0d0'
|
||||||
suit-red-cb: '#6fc2ef'
|
suit-red-cb: '#acc267'
|
||||||
highlight-valid: '#acc267'
|
highlight-valid: '#acc267'
|
||||||
highlight-celebration: '#e1a3ee'
|
highlight-celebration: '#e1a3ee'
|
||||||
highlight-warning: '#ddb26f'
|
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 |
|
| base09 | `#ddb26f` | orange — used for warning chips |
|
||||||
| base0A | `#acc267` | yellow/lime — used for `highlight-valid` (drag targets, valid moves) |
|
| base0A | `#acc267` | yellow/lime — used for `highlight-valid` (drag targets, valid moves) |
|
||||||
| base0B | `#12cfc0` | green/teal — used for `highlight-info` (toasts, neutral status) |
|
| 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) |
|
| 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) |
|
| base0E | `#e1a3ee` | violet — used for celebration (level-up, achievement unlock) |
|
||||||
| base0F | `#fb9fb1` | (alias) |
|
| base0F | `#fb9fb1` | (alias) |
|
||||||
|
|
||||||
### Semantic assignments
|
### 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.
|
- **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.
|
- **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.
|
- **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 |
|
| Suit | Default | Color-blind mode | Glyph differentiation |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Hearts | `#fb9fb1` (pink) | `#6fc2ef` (cyan) | Solid filled glyph |
|
| Hearts | `#fb9fb1` (pink) | `#acc267` (lime) | Solid filled glyph |
|
||||||
| Diamonds | `#fb9fb1` (pink) | `#6fc2ef` (cyan) | **Outlined glyph (1.5px stroke)** |
|
| Diamonds | `#fb9fb1` (pink) | `#acc267` (lime) | **Outlined glyph (1.5px stroke)** |
|
||||||
| Spades | `#d0d0d0` (foreground) | `#d0d0d0` | Solid filled glyph |
|
| Spades | `#d0d0d0` (foreground) | `#d0d0d0` | Solid filled glyph |
|
||||||
| Clubs | `#d0d0d0` (foreground) | `#d0d0d0` | **Outlined glyph (1.5px stroke)** |
|
| 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 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
|
## 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 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 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 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.
|
- **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.
|
- **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`.
|
- **Avatars / circular indicators**: `rounded-full`.
|
||||||
- **Card-back pattern corners**: matches the card's `rounded-md`.
|
- **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
|
## Motion
|
||||||
|
|
||||||
@@ -228,17 +230,17 @@ Flat face design.
|
|||||||
- Background: `#151515`
|
- Background: `#151515`
|
||||||
- Pattern: horizontal scanlines at 2px pitch in `#1a1a1a` (1px line, 1px gap), full bleed
|
- Pattern: horizontal scanlines at 2px pitch in `#1a1a1a` (1px line, 1px gap), full bleed
|
||||||
- Border: 1px solid `#353535`
|
- 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
|
- Bottom-right monogram: the characters `▌RS` in JetBrains Mono 12px, color `#505050`, 6px from the corner
|
||||||
- Corner radius: 8px (matches face)
|
- Corner radius: 8px (matches face)
|
||||||
|
|
||||||
### Primary Buttons
|
### 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
|
### 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
|
### HUD Chips
|
||||||
|
|
||||||
@@ -258,7 +260,7 @@ Full-screen backdrop at 95% opacity. Centered panel: `#202020` fill, 1px `#50505
|
|||||||
|
|
||||||
### Navigation Bar
|
### 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
|
### Status / Sync Indicator
|
||||||
|
|
||||||
@@ -270,7 +272,7 @@ Top-right corner of the HUD: a 6px circular dot.
|
|||||||
|
|
||||||
## Accessibility
|
## 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`.
|
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.
|
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.
|
4. **Tabular figures** are mandatory for any number that updates live (timer, score, moves) so they don't reflow.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
fill="url(#scanlines)"/>
|
fill="url(#scanlines)"/>
|
||||||
|
|
||||||
<!-- Top-left accent badge (the only theme-varying element). -->
|
<!-- Top-left accent badge (the only theme-varying element). -->
|
||||||
<rect x="12" y="12" width="24" height="32" fill="#6fc2ef"/>
|
<rect x="12" y="12" width="24" height="32" fill="#a54242"/>
|
||||||
|
|
||||||
<!-- Bottom-right "▌RS" monogram in JetBrains-Mono-styled FiraMono. -->
|
<!-- Bottom-right "▌RS" monogram in JetBrains-Mono-styled FiraMono. -->
|
||||||
<text x="244" y="368" font-family="Fira Mono" font-size="24"
|
<text x="244" y="368" font-family="Fira Mono" font-size="24"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 956 B |
@@ -38,11 +38,11 @@ const BACK_BORDER: &str = "#353535";
|
|||||||
const BACK_MONOGRAM: &str = "#505050";
|
const BACK_MONOGRAM: &str = "#505050";
|
||||||
|
|
||||||
/// Five back-theme accent colours. Slot 0 is the canonical "Terminal"
|
/// Five back-theme accent colours. Slot 0 is the canonical "Terminal"
|
||||||
/// back from the design system; the other four cycle through the
|
/// back from the design system (matches `ACCENT_PRIMARY`); the other
|
||||||
/// remaining base16-eighties accents so all 5 slots stay visually
|
/// four cycle through the remaining base16-eighties accents so all 5
|
||||||
/// distinct without leaving the palette.
|
/// slots stay visually distinct without leaving the palette.
|
||||||
pub const BACK_ACCENTS: [&str; 5] = [
|
pub const BACK_ACCENTS: [&str; 5] = [
|
||||||
"#6fc2ef", // 0 — cyan (Terminal canonical)
|
"#a54242", // 0 — brick red (Terminal canonical, ACCENT_PRIMARY)
|
||||||
"#acc267", // 1 — lime
|
"#acc267", // 1 — lime
|
||||||
"#e1a3ee", // 2 — lavender
|
"#e1a3ee", // 2 — lavender
|
||||||
"#fb9fb1", // 3 — pink
|
"#fb9fb1", // 3 — pink
|
||||||
|
|||||||
@@ -98,12 +98,18 @@ pub struct CardImageSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Suit-colour swap for red-suit cards in colour-blind mode — Terminal
|
/// Suit-colour swap for red-suit cards in colour-blind mode — Terminal
|
||||||
/// `#6fc2ef` (cyan). Replaces `RED_SUIT_COLOUR` (pink) when CBM is on,
|
/// `#acc267` (lime). Replaces `RED_SUIT_COLOUR` (pink) when CBM is on,
|
||||||
/// providing a hue-distinct alternative that survives the most common
|
/// providing a hue-distinct alternative that survives the most common
|
||||||
/// red/green deficiencies. Pre-Terminal this was a *face tint*; the new
|
/// red/green deficiencies. Pre-Terminal this was a *face tint*; the new
|
||||||
/// design moves CBM differentiation into the suit glyph colour itself
|
/// design moves CBM differentiation into the suit glyph colour itself
|
||||||
/// and keeps the face uniformly `CARD_FACE_COLOUR` regardless of CBM.
|
/// and keeps the face uniformly `CARD_FACE_COLOUR` regardless of CBM.
|
||||||
const RED_SUIT_COLOUR_CBM: Color = Color::srgb(0.435, 0.761, 0.937);
|
///
|
||||||
|
/// The CBM swap is lime (not the `ACCENT_PRIMARY` brick-red) because
|
||||||
|
/// the primary accent is itself in the red family — using it for
|
||||||
|
/// "the not-red CBM alternative" would defeat the purpose. Lime is
|
||||||
|
/// the next-best non-red base16-eighties accent; deuteranopia and
|
||||||
|
/// protanopia readers see it as visibly distinct from pink.
|
||||||
|
const RED_SUIT_COLOUR_CBM: Color = Color::srgb(0.675, 0.761, 0.404);
|
||||||
|
|
||||||
/// Returns the fallback card-back colour for the given unlocked card-back
|
/// Returns the fallback card-back colour for the given unlocked card-back
|
||||||
/// index. Production renders backs from PNG artwork; this fallback only
|
/// index. Production renders backs from PNG artwork; this fallback only
|
||||||
@@ -112,7 +118,7 @@ const RED_SUIT_COLOUR_CBM: Color = Color::srgb(0.435, 0.761, 0.937);
|
|||||||
/// in the same hue family as the on-disk PNG art for that index.
|
/// in the same hue family as the on-disk PNG art for that index.
|
||||||
fn card_back_colour(selected_card_back: usize) -> Color {
|
fn card_back_colour(selected_card_back: usize) -> Color {
|
||||||
match selected_card_back {
|
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
|
1 => Color::srgb(0.675, 0.761, 0.404), // #acc267 lime
|
||||||
2 => Color::srgb(0.882, 0.639, 0.933), // #e1a3ee lavender
|
2 => Color::srgb(0.882, 0.639, 0.933), // #e1a3ee lavender
|
||||||
3 => Color::srgb(0.984, 0.624, 0.694), // #fb9fb1 pink
|
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
|
/// Suit colour for the rank/suit overlay rendered atop the constant
|
||||||
/// fallback sprite (only fires under `MinimalPlugins` — production
|
/// fallback sprite (only fires under `MinimalPlugins` — production
|
||||||
/// renders the suit glyph baked into the PNG). When `color_blind` is
|
/// renders the suit glyph baked into the PNG). When `color_blind` is
|
||||||
/// enabled, red-suit cards swap to `RED_SUIT_COLOUR_CBM` (cyan) — the
|
/// enabled, red-suit cards swap to `RED_SUIT_COLOUR_CBM` (lime) — the
|
||||||
/// "Settings toggle swaps red→cyan" half of the design system's
|
/// "Settings toggle swaps red→lime" half of the design system's
|
||||||
/// colour-blind support. The other half (always-on filled-vs-outlined
|
/// colour-blind support. The other half (always-on filled-vs-outlined
|
||||||
/// glyph differentiation for ♥♠ vs ♦♣) is baked into the PNG art and
|
/// glyph differentiation for ♥♠ vs ♦♣) is baked into the PNG art and
|
||||||
/// has no constant-fallback equivalent.
|
/// 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 {
|
fn text_colour(card: &Card, color_blind: bool) -> Color {
|
||||||
if card.suit.is_red() {
|
if card.suit.is_red() {
|
||||||
if color_blind {
|
if color_blind {
|
||||||
@@ -2066,20 +2076,20 @@ mod tests {
|
|||||||
// Pre-Terminal these were `face_colour` tests asserting that CBM
|
// Pre-Terminal these were `face_colour` tests asserting that CBM
|
||||||
// tinted the *face background* of red-suit cards. The Terminal
|
// tinted the *face background* of red-suit cards. The Terminal
|
||||||
// design system moves CBM differentiation into the suit *glyph*
|
// 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]
|
#[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 red_card = Card { id: 0, suit: Suit::Diamonds, rank: Rank::Queen, face_up: true };
|
||||||
let cbm_colour = text_colour(&red_card, true);
|
let cbm_colour = text_colour(&red_card, true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cbm_colour, RED_SUIT_COLOUR_CBM,
|
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!(
|
assert_ne!(
|
||||||
cbm_colour, RED_SUIT_COLOUR,
|
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",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//! - A "▌ replay" label on the left so the player knows the surface is
|
//! - A "▌ replay" label on the left so the player knows the surface is
|
||||||
//! under playback control rather than live input.
|
//! under playback control rather than live input.
|
||||||
//! - A "MOVE N/M" progress chip in the centre, recomputed every frame
|
//! - 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.
|
//! reads as a discrete callout.
|
||||||
//! - A "Stop" button on the right that aborts playback and returns
|
//! - A "Stop" button on the right that aborts playback and returns
|
||||||
//! control to the player.
|
//! control to the player.
|
||||||
@@ -105,7 +105,7 @@ pub struct ReplayStopButton;
|
|||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct ReplayOverlayGameCaption;
|
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
|
/// `Node`'s `width` is rewritten every frame the cursor advances to
|
||||||
/// `cursor / total` of the bar's full width, so the player has a
|
/// `cursor / total` of the bar's full width, so the player has a
|
||||||
/// continuous visual cue of how far through the replay they are.
|
/// continuous visual cue of how far through the replay they are.
|
||||||
@@ -250,7 +250,7 @@ fn spawn_overlay(
|
|||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|row| {
|
.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.
|
// above and a small `GAME #YYYY-DDD` caption below.
|
||||||
// The caption mirrors the mockup's right-anchored
|
// The caption mirrors the mockup's right-anchored
|
||||||
// game identifier but stays visually grouped with
|
// game identifier but stays visually grouped with
|
||||||
@@ -315,7 +315,7 @@ fn spawn_overlay(
|
|||||||
|
|
||||||
// Right: Stop button. Tertiary variant — the
|
// Right: Stop button. Tertiary variant — the
|
||||||
// action is available but not the loudest element
|
// 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 /
|
// that slot. `spawn_modal_button` gives us hover /
|
||||||
// press paint and focus rings for free via the
|
// press paint and focus rings for free via the
|
||||||
// existing `UiModalPlugin` paint system.
|
// 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
|
/// Same change-detection guard as the text updaters — the overlay
|
||||||
/// already early-exits when nothing moved, so an idle replay leaves the
|
/// already early-exits when nothing moved, so an idle replay leaves the
|
||||||
/// scrub bar's `Node` untouched.
|
/// scrub bar's `Node` untouched.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//!
|
//!
|
||||||
//! 1. [`SelectionState`] tracks the *source-pick* mode. `Tab` / `Shift+Tab`
|
//! 1. [`SelectionState`] tracks the *source-pick* mode. `Tab` / `Shift+Tab`
|
||||||
//! cycles a focus through piles that have a face-up draggable top card.
|
//! 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
|
//! 2. [`KeyboardDragState`] tracks the *destination-pick* mode. Pressing
|
||||||
//! `Enter` while a pile is focused enters
|
//! `Enter` while a pile is focused enters
|
||||||
@@ -634,7 +634,7 @@ fn clear_selection_on_state_change(
|
|||||||
|
|
||||||
/// Maintains the `SelectionHighlight` outline sprite.
|
/// 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
|
/// at the selected card's position. While
|
||||||
/// [`KeyboardDragState::Lifted`] the source highlight tints gold and a
|
/// [`KeyboardDragState::Lifted`] the source highlight tints gold and a
|
||||||
/// second highlight follows the focused destination's top card — visually
|
/// 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;
|
let card_size = layout.0.card_size;
|
||||||
|
|
||||||
// Highlight tints follow the Terminal palette's semantic state
|
// 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
|
// attention/commitment once the cards are lifted, lime valid-move
|
||||||
// tint on the destination. Alphas are kept non-zero so the card
|
// tint on the destination. Alphas are kept non-zero so the card
|
||||||
// face beneath remains readable through the wash.
|
// face beneath remains readable through the wash.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Launch splash overlay.
|
//! Launch splash overlay.
|
||||||
//!
|
//!
|
||||||
//! On app start the engine spawns a fullscreen, high-Z overlay that
|
//! 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
|
//! "Solitaire Quest" wordmark, a short fixture boot log, a progress
|
||||||
//! bar, and a footer with the design-system palette swatches and the
|
//! 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,
|
//! 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
|
/// "▌ ready_" line — visual signature of "boot complete, awaiting
|
||||||
/// input". The leading `▌` glyph picks up `TEXT_PRIMARY` rather than
|
/// input". The leading `▌` glyph picks up `TEXT_PRIMARY` rather than
|
||||||
/// `ACCENT_PRIMARY` so it doesn't compete with the big cyan cursor in
|
/// `ACCENT_PRIMARY` so it doesn't compete with the big accent cursor in
|
||||||
/// the header; the *trailing* 6×12 px cyan pulse Node ([`SplashCursorPulse`])
|
/// the header; the *trailing* 6×12 px accent pulse Node ([`SplashCursorPulse`])
|
||||||
/// is what carries the "alive, blinking" signal called for by the
|
/// is what carries the "alive, blinking" signal called for by the
|
||||||
/// mockup. The pulse's alpha is multiplied with the global fade
|
/// mockup. The pulse's alpha is multiplied with the global fade
|
||||||
/// timeline by [`pulse_splash_cursor`] so it never fights the
|
/// 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(),
|
line_font.clone(),
|
||||||
TextColor(transparent(TEXT_PRIMARY)),
|
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
|
// dimensions rather than a `█` text glyph so the size
|
||||||
// doesn't drift with the line font; matches the mockup's
|
// doesn't drift with the line font; matches the mockup's
|
||||||
// 6×12 px spec literally. Pulse animation lives in
|
// 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 %-
|
/// 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
|
/// below. The "247" is fixture text; the bar is decorative, not a
|
||||||
/// real progress signal. Capped at 720 px width on desktop.
|
/// real progress signal. Capped at 720 px width on desktop.
|
||||||
fn spawn_progress_bar(parent: &mut ChildSpawnerCommands, line_font: &TextFont) {
|
fn spawn_progress_bar(parent: &mut ChildSpawnerCommands, line_font: &TextFont) {
|
||||||
|
|||||||
@@ -333,17 +333,17 @@ pub fn spawn_modal_button<M: Component>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let label_color = match variant {
|
let label_color = match variant {
|
||||||
// Primary buttons sit on the cyan accent — `BG_BASE` text on
|
// Primary buttons sit on the brick-red accent — `BG_BASE` text on
|
||||||
// top reads well and passes AAA contrast against `#6fc2ef`.
|
// top reads well and passes AAA contrast against `#a54242`.
|
||||||
ButtonVariant::Primary => BG_BASE,
|
ButtonVariant::Primary => BG_BASE,
|
||||||
ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_PRIMARY,
|
ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_PRIMARY,
|
||||||
};
|
};
|
||||||
let caption_color = match variant {
|
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
|
// as a secondary detail without disappearing. Deliberately a
|
||||||
// pure-black-at-alpha rather than `BG_BASE.with_alpha(...)`:
|
// pure-black-at-alpha rather than `BG_BASE.with_alpha(...)`:
|
||||||
// `BG_BASE` is `#151515` (not 0,0,0), so the alpha-on-cyan
|
// `BG_BASE` is `#151515` (not 0,0,0), so the alpha-on-accent
|
||||||
// composite would tint slightly cooler than intended here.
|
// composite would tint slightly off from intended here.
|
||||||
ButtonVariant::Primary => Color::srgba(0.0, 0.0, 0.0, 0.55),
|
ButtonVariant::Primary => Color::srgba(0.0, 0.0, 0.0, 0.55),
|
||||||
ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_SECONDARY,
|
ButtonVariant::Secondary | ButtonVariant::Tertiary => TEXT_SECONDARY,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ use bevy::prelude::Val;
|
|||||||
use solitaire_data::AnimSpeed;
|
use solitaire_data::AnimSpeed;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Colours — Terminal (base16-eighties): near-black surface ramp with a cyan
|
// Colours — Terminal (base16-eighties): near-black surface ramp with a brick-
|
||||||
// primary accent and lime/lavender/gold/teal/pink semantic accents.
|
// red primary accent and lime/lavender/gold/teal/pink semantic accents.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Window backstop and the default text colour on top of `ACCENT_PRIMARY`.
|
/// 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`.
|
/// Disabled text — greyed-out buttons, locked items. `#505050`.
|
||||||
pub const TEXT_DISABLED: Color = Color::srgb(0.314, 0.314, 0.314);
|
pub const TEXT_DISABLED: Color = Color::srgb(0.314, 0.314, 0.314);
|
||||||
|
|
||||||
/// Cyan primary accent — the CTA colour of the system. Reserved for
|
/// Brick-red primary accent — the CTA colour of the system. Reserved
|
||||||
/// primary actions (Play, Resume, Save), focus rings, and selection.
|
/// for primary actions (Play, Resume, Save), focus rings, and
|
||||||
/// `BG_BASE` text on top of this colour passes AAA contrast. `#6fc2ef`.
|
/// selection. `#a54242` (base16-eighties `base08`). Pre-2026-05-08
|
||||||
pub const ACCENT_PRIMARY: Color = Color::srgb(0.435, 0.761, 0.937);
|
/// 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.
|
/// Brightened `ACCENT_PRIMARY` for hover states on primary buttons.
|
||||||
/// Picks up luminance while keeping the same hue. `#a8dcf5`.
|
/// Picks up luminance while keeping the same hue. `#c25e5e`.
|
||||||
pub const ACCENT_PRIMARY_HOVER: Color = Color::srgb(0.659, 0.863, 0.961);
|
pub const ACCENT_PRIMARY_HOVER: Color = Color::srgb(0.761, 0.369, 0.369);
|
||||||
|
|
||||||
/// Lavender secondary accent — celebratory states (level-up,
|
/// Lavender secondary accent — celebratory states (level-up,
|
||||||
/// achievement unlocked, streak milestones). Used sparingly so it stays
|
/// 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.
|
/// Foreground (text) colour of the stock-pile remaining-count chip.
|
||||||
///
|
///
|
||||||
/// `ACCENT_PRIMARY` keeps the chip readable against the elevated
|
/// `ACCENT_PRIMARY` keeps the chip readable against the elevated
|
||||||
/// surface background and matches the cyan accent already used for
|
/// surface background and matches the primary accent already used
|
||||||
/// other "look here" callouts.
|
/// for other "look here" callouts.
|
||||||
pub const STOCK_BADGE_FG: Color = ACCENT_PRIMARY;
|
pub const STOCK_BADGE_FG: Color = ACCENT_PRIMARY;
|
||||||
|
|
||||||
/// Sprite-space `Transform.z` for the stock-pile remaining-count chip.
|
/// 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
|
/// Alpha for the lifted/dragged card shadow. Set to 0 for the same
|
||||||
/// reason as [`CARD_SHADOW_ALPHA_IDLE`]. Drag affordance under the
|
/// reason as [`CARD_SHADOW_ALPHA_IDLE`]. Drag affordance under the
|
||||||
/// Terminal system is the cyan focus glow + z-index lift, not a deeper
|
/// Terminal system is the primary-accent focus glow + z-index lift,
|
||||||
/// shadow.
|
/// not a deeper shadow.
|
||||||
pub const CARD_SHADOW_ALPHA_DRAG: f32 = 0.0;
|
pub const CARD_SHADOW_ALPHA_DRAG: f32 = 0.0;
|
||||||
|
|
||||||
/// World-space pixel offset of the resting-state card shadow relative to
|
/// 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
|
/// (matches `ACCENT_PRIMARY`) at 85% alpha so the ring stays legible
|
||||||
/// against both elevated surfaces and the modal scrim backdrop.
|
/// against both elevated surfaces and the modal scrim backdrop.
|
||||||
/// `rgba(111, 194, 239, 0.85)`.
|
/// `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
|
// Typography scale (px) — 5 rungs replace the prior
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const EXPECTED: &[(&str, u64)] = &[
|
|||||||
("face_JS", 0x52525a2200c07246),
|
("face_JS", 0x52525a2200c07246),
|
||||||
("face_QS", 0xb4f0251a2757cbb1),
|
("face_QS", 0xb4f0251a2757cbb1),
|
||||||
("face_KS", 0x1e1975919bb9a029),
|
("face_KS", 0x1e1975919bb9a029),
|
||||||
("back_0", 0xf698d0e161eae13a),
|
("back_0", 0xfd1742ebe330481a),
|
||||||
("back_1", 0x446fdc0a3c83a03a),
|
("back_1", 0x446fdc0a3c83a03a),
|
||||||
("back_2", 0xcf188fdec9f5819a),
|
("back_2", 0xcf188fdec9f5819a),
|
||||||
("back_3", 0xcaffd02af141743a),
|
("back_3", 0xcaffd02af141743a),
|
||||||
|
|||||||
Reference in New Issue
Block a user