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

16 KiB
Raw Permalink Blame History

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:

<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:

<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):

/* 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:

/* 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:

.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:

.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.