# Track Card Theming — align fallback + text with NowPlayingCard Status: completed. Author: product-designer. Date: 2026-06-05. Implementer: maintenance-engineer. Landed 2026-06-05. ## Goal Make the public tracks-page track cards match the NowPlayingCard's established visual language: - **Fallback background (no album art)** → navy-blue glassy look (translucent + blur), as used by `.now-playing`. - **Card text** → moss-green accent (`--deepdrft-green-accent` = `#3D7A68`) for the label-grade text, matching `.np-label` / waveform bars. ## Scope constraint (read before implementing) `TrackCard.razor` lives in **`DeepDrftShared.Client`**, which is consumed by **both** the public site (light + dark) **and** the CMS host (`DeepDrftManager`, light-only). Any change to TrackCard's defaults is inherited by the CMS. The NowPlayingCard aesthetic is a *dark-on-dark* treatment; applying it unconditionally would break legibility on the CMS's off-white surfaces. **Therefore the fix must be theme-aware**, not a flat recolor. The dark/navy-glass + moss-green treatment applies under `.deepdrft-theme-dark`; light/CMS keeps a legible on-light treatment. The hooks below are written so a single CSS file carries both. The card CSS lives in `DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` section 8. That stylesheet is served by the public host and (per its own header comment) is shared across server and client of the public app. **It is not loaded by the CMS host.** This is actually convenient: putting the navy-glass treatment in section 8 means it only reaches the public site. The CMS gets TrackCard with no `.deepdrft-track-card-*` styling beyond whatever MudBlazor defaults apply. Confirm during implementation that the CMS still renders TrackCard legibly (it currently relies on the same classes; if the CMS does not link this stylesheet, the fallback `mud-theme-secondary` is the only thing styling it). If the CMS does not actually use TrackCard in any live page, this concern is moot — verify before spending effort on the light path. --- ## 1. Current problems ### 1a. Fallback background resolves to white `TrackCard.razor` line 11: ```razor ``` `mud-theme-secondary` paints the element with the theme's **Secondary** palette color. In the dark palette (`DeepDrftPalettes.Dark`), `Secondary = "#FAFAF8"` (off-white). So a track with no album art renders as a **white card** on the navy page — the opposite of the intended navy-glass look, and it makes the white-intended text invisible-by-collision. (In the light/CMS palette, Secondary = `#1A3C34` deep green — a dark card on a light page, also not the intended look but at least legible. Either way `mud-theme-secondary` is the wrong abstraction here.) ### 1b. All card text resolves to near-invisible navy Every `MudText` in TrackCard uses `Color="Color.Surface"` (lines 20, 26, 36, 57). MudBlazor's `Color.Surface` maps to the palette **Surface** color. In the dark palette, `Surface = "#162437"` (navy-mid). So all text is navy-mid: - On the *current buggy* white fallback: barely legible (low contrast dark-on-white, but the white itself is wrong). - On the *intended* navy-glass fallback: **near-invisible** (navy-mid text on a navy-translucent ground — contrast well below WCAG AA). - On a real album-art background (`.deepdrft-track-card-bg`, `brightness(0.7)`): unreliable, depends entirely on the artwork. `Color.Surface` is semantically "the color of a surface," never intended as a text color. This is the root mistake. ### 1c. No per-component scoped CSS exists There is **no** `TrackCard.razor.css`. All TrackCard styling is global in section 8 of `deepdrft-styles.css` plus MudBlazor utility classes applied inline. The fix can stay in section 8 (preferred — keeps the public-only scoping described above) rather than introducing a scoped file. --- ## 2. Proposed changes Two coordinated edits: (A) remove the wrong MudBlazor color utilities from the Razor so CSS can own the colors, (B) add navy-glass + moss-green rules to section 8. ### 2a. Razor changes — `DeepDrftShared.Client/Components/TrackCard.razor` Goal: stop hard-binding to palette Secondary/Surface; hand color control to CSS via stable class hooks. **Fallback `MudPaper` (line 11):** remove `mud-theme-secondary`. Replace the class list with a single semantic hook: ```razor ``` The navy-glass background moves into the `.deepdrft-track-card-fallback` CSS rule (2b). **Text elements (lines 19–23, 25–29, 35–39, 57–60):** remove the `Color="Color.Surface"` attribute from each `MudText`. Add class hooks so CSS can assign the moss-green / hierarchy colors. Suggested hooks (match the NowPlayingCard's title/label/sub hierarchy): - Track name (line 19, `Typo.subtitle1`) → add `deepdrft-track-title` to its `Class`. Currently `Class="text-truncate mb-1"` → `Class="deepdrft-track-title text-truncate mb-1"`. - Artist (line 25, `Typo.caption`) → add `deepdrft-track-artist`. `Class="text-truncate mb-2"` → `Class="deepdrft-track-artist text-truncate mb-2"`. - Album (line 35, `Typo.caption`) → add `deepdrft-track-meta`. `Class="text-truncate"` → `Class="deepdrft-track-meta text-truncate"`. - Release year (line 57, `Typo.caption`) → add `deepdrft-track-meta`. Has no `Class` today → add `Class="deepdrft-track-meta"`. Do **not** add inline `Style` or MudBlazor `Color` values; all color lives in CSS so the theme-aware split works. ### 2b. CSS changes — `DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` section 8 Reference values from NowPlayingCard (`NowPlayingCard.razor.css`): - glass background: `rgba(250, 250, 248, 0.06)` - glass border: `1px solid rgba(250, 250, 248, 0.12)` - blur: `backdrop-filter: blur(8px)` - label/accent text: `var(--deepdrft-green-accent)` (`#3D7A68`) - title text: `var(--deepdrft-white)` (`#FAFAF8`) - sub text: `rgba(250, 250, 248, 0.45)` **Note:** NowPlayingCard's glass is a *light-tinted* translucency (`rgba(250,250,248,…)`) over the navy page, which reads as navy-glass because the navy page shows through. To make the fallback unambiguously "navy-blue glassy" even where the page behind it is not pure navy (e.g. over a gradient), tint with navy explicitly. Use the navy token at low alpha plus the same light border/blur. Both options below are acceptable; **Option A** matches NowPlayingCard literally, **Option B** is more robustly navy. Recommend **Option B** for the fallback since the card is a discrete object on a varied page, where NowPlayingCard sits in a known navy context. Replace the existing `.deepdrft-track-card-fallback` rule (currently only position/size) and add the text rules. Proposed block (dark-mode treatment scoped under `.deepdrft-theme-dark`; base rule keeps layout only): ```css /* Fallback panel — layout (theme-agnostic) */ .deepdrft-track-card-fallback { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } /* Fallback panel — navy-glass treatment, dark theme only. Mirrors NowPlayingCard's .now-playing glass. */ .deepdrft-theme-dark .deepdrft-track-card-fallback { /* Option A (literal NowPlayingCard match): background: rgba(250, 250, 248, 0.06); */ /* Option B (recommended — explicit navy tint): */ background: color-mix(in srgb, var(--deepdrft-navy) 55%, transparent); border: 1px solid rgba(250, 250, 248, 0.12); backdrop-filter: blur(8px); } /* Card text — dark theme. Moss-green for label-grade text, off-white title. */ .deepdrft-theme-dark .deepdrft-track-title { color: var(--deepdrft-white); } .deepdrft-theme-dark .deepdrft-track-artist { color: var(--deepdrft-green-accent); } .deepdrft-theme-dark .deepdrft-track-meta { color: rgba(250, 250, 248, 0.55); } ``` **Decision needed (1 of these):** the user said "track card text → the moss-green color." Two readings: 1. **Title in moss-green, supporting text muted** — strongest match to "card text is the moss green," puts the green on the most prominent line. Use: `.deepdrft-track-title { color: var(--deepdrft-green-accent); }` and demote the artist to off-white or muted. 2. **NowPlayingCard hierarchy** (title off-white `#FAFAF8`, accent green on the *label*, sub muted) — the block above. This is the literal NowPlayingCard mapping: green is the accent/label color, not the title color. The block above implements a hybrid (artist line in green). **Recommend reading 1** — it most directly satisfies "card text → moss green" and reads well on the navy-glass ground: title in moss-green, artist + meta in muted off-white. If the user wants the literal NowPlayingCard hierarchy instead, swap to reading 2. Flag this back to the user if ambiguous; otherwise default to reading 1: ```css /* Reading 1 (recommended default) */ .deepdrft-theme-dark .deepdrft-track-title { color: var(--deepdrft-green-accent); } .deepdrft-theme-dark .deepdrft-track-artist { color: rgba(250, 250, 248, 0.70); } .deepdrft-theme-dark .deepdrft-track-meta { color: rgba(250, 250, 248, 0.55); } ``` ### 2c. Text legibility over real album art When album art *is* present (`.deepdrft-track-card-bg`), the same text classes now apply. Moss-green/off-white over arbitrary artwork at `brightness(0.7)` can still be low-contrast. NowPlayingCard never sits over artwork, so it gives no precedent. Recommend a scrim behind the content to guarantee contrast in both art and fallback cases: ```css .deepdrft-track-card-content { /* existing rules unchanged; add: */ background: linear-gradient(to top, rgba(13, 27, 42, 0.75) 0%, rgba(13, 27, 42, 0.35) 45%, rgba(13, 27, 42, 0.0) 100%); } ``` This is optional but recommended — it makes the green/off-white text legible over both the fallback glass and any album art, and reinforces the navy identity. If the user prefers the fallback glass to read cleaner without a scrim, gate the scrim to art-only by moving it to `.deepdrft-track-card-bg` as an `::after` overlay instead. **Recommend the content-level scrim** for uniformity. Confirm with the user if the glassy fallback should stay scrim-free. ### 2d. Light / CMS path With the dark treatment scoped under `.deepdrft-theme-dark`, light mode currently gets **no** explicit text color (the removed `Color.Surface`) and **no** fallback background beyond the base layout rule. That means: - Light fallback card → transparent (shows the page behind it). Likely undesirable. - Light text → inherits MudBlazor body text (navy `#0D1B2A`), which is legible on a light ground. Acceptable. Add a minimal light treatment so the public site's light mode and the CMS don't regress to a transparent fallback: ```css .deepdrft-theme-light .deepdrft-track-card-fallback { background: color-mix(in srgb, var(--deepdrft-navy) 8%, var(--deepdrft-white)); border: 1px solid var(--deepdrft-border); } ``` Text in light mode can be left to inherit (navy on light reads fine) or, for consistency with the green identity, set the title to `--deepdrft-green-accent` in light too — the accent green has adequate contrast on a near-white card. Recommend leaving light text to inherit unless the user wants the green identity carried into light mode. **CMS caveat:** the CMS host does not link `deepdrft-styles.css` (it is in `DeepDrftPublic/wwwroot`). If the CMS renders TrackCard, none of these rules reach it and the fallback will be unstyled. Verify whether the CMS uses TrackCard at all before investing in the CMS path; if it does, the light-mode rules need to live in the shared token/style layer the CMS *does* load, not in `deepdrft-styles.css`. **This is the one open structural question** — resolve it before implementing the light path. --- ## 3. Other theming issues spotted on the tracks page Beyond the two the user called out: ### 3a. Genre chip uses `Color.Primary` — green-on-navy collision (dark) `TrackCard.razor` line 48: ``. In the dark palette `Primary = "#3D7A68"` (the same moss-green we're about to use for text). A green chip sitting next to green text on a navy-glass card flattens the hierarchy — everything is one green. Recommend giving the chip its own treatment: filled navy-mid with green text/border, or a subtle outlined variant, so it reads as a distinct tag rather than blending into the text. Suggested: `Variant="Variant.Outlined"` with a `.deepdrft-genre-chip` color override to `--deepdrft-green-accent` border + text on transparent. Low priority; raise with user. ### 3b. Play FAB uses `Color.Primary` — same green, but here it's correct Line 67: ``. Green FAB on navy is the intended interactive accent (matches the dark palette's "green-accent is the primary interactive color" note in `DeepDrftPalettes.Dark`). **No change** — this is the one place the green-as-primary mapping is right. Noting it so the implementer doesn't "fix" it while touching the chip. ### 3c. Loading skeletons are theme-default gray `TracksView.razor` lines 30, 37 use bare `MudSkeleton`. MudBlazor skeletons render in a neutral gray that does not match the navy-glass language — on the navy dark page they'll be light-gray rectangles, a jarring pre-load flash before the navy cards appear. Recommend tinting skeletons toward the navy-glass treatment (`rgba(250,250,248,0.06)` pulse) so the loading state previews the real cards. Low priority, but it's the most visible remaining mismatch with the NowPlayingCard aesthetic on this page. Defer unless the user wants polish. ### 3d. Card container has no border / elevation language matching the glass `.deepdrft-track-card-container` (section 8) sets size + `overflow: hidden` and relies on MudCard `Elevation="4"` (a drop shadow). NowPlayingCard uses a **1px translucent border + no shadow** for its flat-glass look. The track cards' Material drop-shadow is a different visual vocabulary. For full alignment, consider `Elevation="0"` on the MudCard plus a `1px solid rgba(250,250,248,0.12)` border on the container (dark) to match the glass edge. Medium priority — this is what most distinguishes "Material card" from "NowPlayingCard glass panel." Worth doing if the goal is genuine aesthetic match, not just color. ### 3e. `--deepdrft-green-accent` is not defined inside `.deepdrft-theme-dark` The token block (`deepdrft-tokens.css`) defines `--deepdrft-green-accent: #3D7A68` only in `:root`. The `.deepdrft-theme-dark` block re-declares the *alias* layer but inherits the raw `--deepdrft-*` wireframe tokens from `:root`. NowPlayingCard already uses `var(--deepdrft-green-accent)` successfully under dark, so the cascade resolves fine — **no action needed**, just confirming the token is in scope for the new rules. Noting it so the implementer doesn't worry the var is undefined in dark. --- ## 4. Summary of edits for the implementer | File | Change | Priority | |------|--------|----------| | `DeepDrftShared.Client/Components/TrackCard.razor` | Drop `mud-theme-secondary` from fallback; drop `Color="Color.Surface"` from all 4 MudText; add class hooks `deepdrft-track-title` / `-artist` / `-meta` | required | | `DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` §8 | Navy-glass fallback (dark), moss-green/off-white text (dark), light fallback fallback-bg, optional content scrim | required | | same §8 / §9 | Genre chip distinct treatment (3a) | optional | | `DeepDrftShared.Client/Components/TrackCard.razor` | MudCard `Elevation="0"` + glass border on container (3d) | recommended for true match | | `TracksView.razor` skeleton tint (3c) | polish | defer | ## 5. Open questions to resolve before / during implementation 1. **Green on title vs. green on label** (§2b reading 1 vs 2). Default to reading 1 (title in moss-green) unless the user says otherwise. 2. **Does the CMS host render TrackCard, and does it link `deepdrft-styles.css`?** Determines whether the light-mode rules belong in `deepdrft-styles.css` or the shared token layer. This is the only blocker for the light path; the dark path (what the user actually asked for) is unblocked. 3. **Scrim or no scrim** over album art (§2c). Recommend yes; confirm. 4. **Match the glass edge** (Elevation 0 + border, §3d) — confirm the user wants full aesthetic match vs. color-only.