From 00ff9e2702d13301f61961184cd2eeaa7230e589 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sat, 20 Jun 2026 03:03:18 -0400 Subject: [PATCH 1/2] fix(dark-theme): PlayStateIcon glyph beats .dd-accent-icon; GasLampLit self-colored frame PlayStateIcon.razor.css adds a .mud-icon-root rule !important so the play chip always shows navy on moss-green in dark. GasLampLit frame path changed from currentColor to #2A5C4F; dead nav dark rule removed. --- DeepDrftPublic.Client/CLAUDE.md | 6 +++++- .../Controls/PlayStateIcon.razor.css | 11 +++++++++++ DeepDrftPublic/wwwroot/styles/deepdrft-styles.css | 11 ++++------- DeepDrftShared.Client/Common/DDIcons.cs | 9 +++++++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/DeepDrftPublic.Client/CLAUDE.md b/DeepDrftPublic.Client/CLAUDE.md index 2f771cb..5c97f9c 100644 --- a/DeepDrftPublic.Client/CLAUDE.md +++ b/DeepDrftPublic.Client/CLAUDE.md @@ -144,7 +144,11 @@ Component state lives in ViewModels (registered scoped in DI). Components render Green-accent interactive icon affordances (Play / Share / Add-to-Queue / lava-lamp trigger, etc.) use a **single reusable treatment** in `deepdrft-styles.css`, not per-site dark overrides. Wrap the affordance(s) in a container carrying `.dd-accent-icon`; the rule colours the inner `.mud-icon-root` glyph green-accent (`--deepdrft-green-accent`, the brand constant — same value in both palettes) in **both** themes. Add `.dd-accent-fill` to the same container when it also holds a filled `Color.Secondary` `MudButton` whose fill must go green-accent in **dark** (dark-only — light already renders green fill + white text). -Two reasons this is needed and why it's a class, not a palette colour: (1) no MudBlazor `Color` enum is green in both themes (`Dark.Secondary` is off-white), so palette-only solutions can't express "green in both"; (2) MudBlazor stamps the standalone rule `.mud-secondary-text { color: …secondary !important }` (0,1,0) on the glyph ``, so wrapper-level overrides never reach it — the reusable rule targets `.dd-accent-icon .mud-icon-button .mud-icon-root` (0,3,0) `!important`, which beats it on specificity alone; source order is not load-bearing for the glyph clause. The Session/Mix release-detail hero Share/Play glyphs use this class too: they were already green-accent in light (via `Color.Secondary` → `Light.Secondary`), so folding them in keeps light pixel-identical while fixing the dark over-image glyphs — they are not actually theme-divergent. The one genuinely theme-divergent affordance (the gas-lamp toggle = inherited nav text in light) does **not** use this class — it keeps a narrow dark-only rule. **Add new green-accent icon affordances by applying this class, not by spawning a new dark override.** +Two reasons this is needed and why it's a class, not a palette colour: (1) no MudBlazor `Color` enum is green in both themes (`Dark.Secondary` is off-white), so palette-only solutions can't express "green in both"; (2) MudBlazor stamps the standalone rule `.mud-secondary-text { color: …secondary !important }` (0,1,0) on the glyph ``, so wrapper-level overrides never reach it — the reusable rule targets `.dd-accent-icon .mud-icon-button .mud-icon-root` (0,3,0) `!important`, which beats it on specificity alone; source order is not load-bearing for the glyph clause. The Session/Mix release-detail hero Share/Play glyphs use this class too: they were already green-accent in light (via `Color.Secondary` → `Light.Secondary`), so folding them in keeps light pixel-identical while fixing the dark over-image glyphs — they are not actually theme-divergent. **Add new green-accent icon affordances by applying this class, not by spawning a new dark override.** + +**Self-themed components are authoritative over `.dd-accent-icon`.** `PlayStateIcon` owns its glyph colour inside `.icon-container` and must beat a surrounding `.dd-accent-icon` in dark — its scoped CSS rule targets `.mud-icon-root` at (0,4,0) `!important` (after Blazor's scope attribute is applied), which outranks the consolidation rule's (0,3,0) `!important`. Do not wrap a `PlayStateIcon` in `.dd-accent-icon` expecting to recolor its play-chip glyph — the play chip always shows navy (`--deepdrft-play-glyph`) against the moss-green chip in dark. + +**Gas-lamp toggle is self-colored in its SVG.** `DDIcons.GasLampLit` (dark-mode icon) carries `fill="#2A5C4F"` directly on its frame path — no CSS colour override is needed. The former dark nav rule (`.deepdrft-theme-dark .dd-nav-actions .mud-icon-button`) has been removed as dead. `DDIcons.GasLamp` (light-mode icon) continues to use `currentColor` and inherits nav text colour in light (the unlit toggle is theme-divergent by design). ## Development commands diff --git a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css index 9d996ec..548b042 100644 --- a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css +++ b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css @@ -22,4 +22,15 @@ component's scope attribute. */ .deepdrft-theme-dark .icon-container ::deep .mud-icon-button { color: var(--deepdrft-play-glyph); +} + +/* PlayStateIcon is authoritative over its own glyph colour — a surrounding .dd-accent-icon + must NOT recolor the play-chip glyph in dark. The consolidation rule is: + .dd-accent-icon .mud-icon-button .mud-icon-root (0,3,0) !important + After Blazor scoped-CSS compilation this rule becomes: + .deepdrft-theme-dark .icon-container[b-xxx] .mud-icon-button .mud-icon-root (0,4,0) !important + (0,4,0) beats (0,3,0) — wins on specificity; !important parity is irrelevant. + Dark only: light already renders the navy glyph via the MudBlazor Color prop. */ +.deepdrft-theme-dark .icon-container ::deep .mud-icon-button .mud-icon-root { + color: var(--deepdrft-play-glyph) !important; } \ No newline at end of file diff --git a/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css b/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css index 9fa8fbf..5580ebf 100644 --- a/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css +++ b/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css @@ -809,13 +809,10 @@ body:has(.waveform-visualizer-control-overlay) { color: var(--deepdrft-navy) !important; } -/* Theme-divergent affordance — gas-lamp dark-mode toggle. - Uses Color.Inherit, so in LIGHT it inherits the nav text colour (the contract to keep). - In dark theme it goes green-accent to match the convention. Scoped to .dd-nav-actions - (covers both desktop and mobile nav, which both render the gas-lamp there); dark-only. */ -.deepdrft-theme-dark .dd-nav-actions .mud-icon-button { - color: var(--deepdrft-green-accent); -} +/* Gas-lamp dark-mode toggle: the frame now carries an explicit #2A5C4F fill in its SVG + (DDIcons.GasLampLit), so no CSS colour override is needed here in dark. The nav rule + that previously set green-accent on the MudIconButton has been removed — it was the + only .mud-icon-button in .dd-nav-actions and is now dead. */ /* ============================================================================= RELEASE DESCRIPTION BLURB diff --git a/DeepDrftShared.Client/Common/DDIcons.cs b/DeepDrftShared.Client/Common/DDIcons.cs index 21aa355..deba9ef 100644 --- a/DeepDrftShared.Client/Common/DDIcons.cs +++ b/DeepDrftShared.Client/Common/DDIcons.cs @@ -12,11 +12,16 @@ public static class DDIcons """; /// - /// Charleston gas lamp with lit flame - for dark mode + /// Charleston gas lamp with lit flame - for dark mode. + /// Frame/body path uses an explicit darker-green fill (#2A5C4F — palette PrimaryDarken / + /// --deepdrft-green-light) instead of currentColor so it is deterministic in the nav + /// regardless of inherited colour. The flame ellipses keep their literal orange/yellow/cream + /// fills. Only rendered in dark mode (DarkLightModeButtonIcon in DeepDrftMenu.razor). + /// If the palette's PrimaryDarken changes, update #2A5C4F to match. /// public const string GasLampLit = """ - + From 4410132409fe9e7d2730142428c9ee8716c9fca3 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sat, 20 Jun 2026 03:06:59 -0400 Subject: [PATCH 2/2] docs: correct PlayStateIcon compiled-selector specificity tuple (0,4,0) to (0,5,0) The [b-xxx] Blazor scope attribute is a fifth class/attribute simple selector; the prior count dropped it. --- DeepDrftPublic.Client/CLAUDE.md | 2 +- DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DeepDrftPublic.Client/CLAUDE.md b/DeepDrftPublic.Client/CLAUDE.md index 5c97f9c..200704d 100644 --- a/DeepDrftPublic.Client/CLAUDE.md +++ b/DeepDrftPublic.Client/CLAUDE.md @@ -146,7 +146,7 @@ Green-accent interactive icon affordances (Play / Share / Add-to-Queue / lava-la Two reasons this is needed and why it's a class, not a palette colour: (1) no MudBlazor `Color` enum is green in both themes (`Dark.Secondary` is off-white), so palette-only solutions can't express "green in both"; (2) MudBlazor stamps the standalone rule `.mud-secondary-text { color: …secondary !important }` (0,1,0) on the glyph ``, so wrapper-level overrides never reach it — the reusable rule targets `.dd-accent-icon .mud-icon-button .mud-icon-root` (0,3,0) `!important`, which beats it on specificity alone; source order is not load-bearing for the glyph clause. The Session/Mix release-detail hero Share/Play glyphs use this class too: they were already green-accent in light (via `Color.Secondary` → `Light.Secondary`), so folding them in keeps light pixel-identical while fixing the dark over-image glyphs — they are not actually theme-divergent. **Add new green-accent icon affordances by applying this class, not by spawning a new dark override.** -**Self-themed components are authoritative over `.dd-accent-icon`.** `PlayStateIcon` owns its glyph colour inside `.icon-container` and must beat a surrounding `.dd-accent-icon` in dark — its scoped CSS rule targets `.mud-icon-root` at (0,4,0) `!important` (after Blazor's scope attribute is applied), which outranks the consolidation rule's (0,3,0) `!important`. Do not wrap a `PlayStateIcon` in `.dd-accent-icon` expecting to recolor its play-chip glyph — the play chip always shows navy (`--deepdrft-play-glyph`) against the moss-green chip in dark. +**Self-themed components are authoritative over `.dd-accent-icon`.** `PlayStateIcon` owns its glyph colour inside `.icon-container` and must beat a surrounding `.dd-accent-icon` in dark — its scoped CSS rule targets `.mud-icon-root` at (0,5,0) `!important` (after Blazor's scope attribute is applied), which outranks the consolidation rule's (0,3,0) `!important`. Do not wrap a `PlayStateIcon` in `.dd-accent-icon` expecting to recolor its play-chip glyph — the play chip always shows navy (`--deepdrft-play-glyph`) against the moss-green chip in dark. **Gas-lamp toggle is self-colored in its SVG.** `DDIcons.GasLampLit` (dark-mode icon) carries `fill="#2A5C4F"` directly on its frame path — no CSS colour override is needed. The former dark nav rule (`.deepdrft-theme-dark .dd-nav-actions .mud-icon-button`) has been removed as dead. `DDIcons.GasLamp` (light-mode icon) continues to use `currentColor` and inherits nav text colour in light (the unlit toggle is theme-divergent by design). diff --git a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css index 548b042..186d191 100644 --- a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css +++ b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.css @@ -28,8 +28,8 @@ must NOT recolor the play-chip glyph in dark. The consolidation rule is: .dd-accent-icon .mud-icon-button .mud-icon-root (0,3,0) !important After Blazor scoped-CSS compilation this rule becomes: - .deepdrft-theme-dark .icon-container[b-xxx] .mud-icon-button .mud-icon-root (0,4,0) !important - (0,4,0) beats (0,3,0) — wins on specificity; !important parity is irrelevant. + .deepdrft-theme-dark .icon-container[b-xxx] .mud-icon-button .mud-icon-root (0,5,0) !important + (0,5,0) beats (0,3,0) — wins on specificity; !important parity is irrelevant. Dark only: light already renders the navy glyph via the MudBlazor Color prop. */ .deepdrft-theme-dark .icon-container ::deep .mud-icon-button .mud-icon-root { color: var(--deepdrft-play-glyph) !important;