Files
deepdrft/product-notes/track-card-theming.md
T
2026-06-05 15:36:40 -04:00

329 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 1923, 2529, 3539, 5760):** 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.