# 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
<MudPaper Class="deepdrft-track-card-fallback mud-theme-secondary" Elevation="0">
```

`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
<MudPaper Class="deepdrft-track-card-fallback" Elevation="0">
```

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: `<MudChip Color="Color.Primary">`. 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: `<MudFab Color="Color.Primary">`. 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.
