# Track Card CSS Architecture — eliminate `!important` from section 8

Status: completed. Author: product-designer. Date: 2026-06-05. Implementer: daniel-c-harvey. Landed: 2026-06-05.

Predecessor: [`track-card-theming.md`](track-card-theming.md) (completed 2026-06-05) landed the
navy-glass + moss-green treatment and, in doing so, introduced the two `!important` rules this
spec exists to remove. That note is the *what it should look like*; this note is the *how the
CSS should be structured so it looks like that without `!important`*. Nothing here changes the
visual design — only the mechanism.

---

## §1. Root cause analysis — why MudCard/MudPaper fight our CSS

### 1.1 The two `!important` rules in question

`DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` section 8:

```css
.deepdrft-track-card-container { ... background: transparent !important; }          /* line 222 */
.deepdrft-track-card-fallback  { ... background: var(--deepdrft-navy-mid, #162437) !important; }  /* line 268 */
.deepdrft-theme-dark  .deepdrft-track-card-fallback { ... !important; }             /* line 273 */
.deepdrft-theme-light .deepdrft-track-card-fallback { ... !important; }             /* line 280 */
```

Four `!important` declarations, all on `background`, all on elements rendered by MudBlazor
(`MudCard` → container, `MudPaper` → fallback).

### 1.2 What MudBlazor paints and with what selector

MudBlazor ships a single global `<style>` block (injected by `MudThemeProvider`, served as
`_content/MudBlazor/MudBlazor.min.css`). The relevant rules are roughly:

```css
.mud-paper {
    background-color: var(--mud-palette-surface);   /* the conflict */
    color: var(--mud-palette-text-primary);
    ...
}
.mud-card { /* composes .mud-paper */ }
```

`MudCard` and `MudPaper` both render a root element carrying the `.mud-paper` class, and
`--mud-palette-surface` resolves to `DeepDrftPalettes.Surface` — `#162437` (navy-mid) in dark,
`#FAFAF8` (off-white) in light. So **every** track card root gets a painted surface color we do
not want, because we layer our own album-art div / fallback glass / scrim underneath the content.

### 1.3 Why our scoped CSS can't reach it and `!important` was the escape hatch

Two compounding facts:

1. **Specificity / source-order.** `.mud-paper { background-color: ... }` is a single class
   selector (specificity `0,1,0`). Our `.deepdrft-track-card-container { background: ... }` is
   *also* a single class selector (`0,1,0`). When specificity ties, **source order wins** — and
   MudBlazor's stylesheet load order relative to `deepdrft-styles.css` is not something section 8
   controls reliably (framework CSS, theme-injected CSS, and our sheet are separate links). To
   guarantee we win regardless of order, the author reached for `!important`. That is the smell:
   `!important` is being used to paper over an *ordering/specificity* uncertainty, not a genuine
   need.

2. **CSS isolation cannot help here.** This is the deeper structural reason. Blazor scoped CSS
   (`*.razor.css`) works by stamping a `b-<hash>` attribute onto elements *that Blazor renders*
   and rewriting the scoped selector to `.foo[b-<hash>]` (specificity `0,2,0` — would beat
   `.mud-paper`). But `MudCard`/`MudPaper` render their **own** inner HTML; Blazor never stamps
   the scope attribute onto MudBlazor's internal elements. So a `TrackCard.razor.css` rule
   targeting `.mud-paper` would compile to `.mud-paper[b-<hash>]` and match **nothing**. (This is
   also why no `TrackCard.razor.css` exists today — it would be inert against the very elements
   that need overriding.) The team correctly fell back to global rules in section 8 — but global
   single-class rules don't beat MudBlazor without either higher specificity or `!important`.

**The fundamental mismatch:** we are using MudBlazor surface components (`MudCard`, `MudPaper`)
as *layout shells* while wanting to own their backgrounds entirely. MudBlazor's whole contract is
"I paint the surface from the palette." We are fighting the component's purpose. Every option
below is a different way to stop fighting it.

### 1.4 What we actually need from these two elements

Stripped to first principles, the container and fallback need:
- **Container:** a `position: relative`, fixed-size, `overflow: hidden` box. A *transparent*
  stacking context so the album-art div, fallback glass, and content scrim layer correctly.
  No surface paint, no Material elevation (already `Elevation="0"`), no ripple.
- **Fallback:** an absolutely-positioned full-bleed panel painted with our navy-glass (dark) or
  navy-tint (light) treatment, with a border + `backdrop-filter`.

Neither needs anything MudBlazor's surface model provides. That observation drives the
recommendation.

---

## §2. Option A — Replace container + fallback with plain `<div>`

Drop `MudCard` → `<div class="deepdrft-track-card-container">` and the fallback `MudPaper` →
`<div class="deepdrft-track-card-fallback">`. Keep `MudCardContent`, `MudText`, `MudChip`,
`MudFab` (none of these own a competing background once the card root is a plain div — and the
text/chip/fab already work today).

**Note:** `MudCardContent` technically expects a `MudCard` ancestor, but it only emits a
`<div class="mud-card-content">` with padding — no background, no JS, no required parent context.
It renders fine inside a plain div. If we want zero MudBlazor coupling in the shell, it can also
become a plain `<div class="deepdrft-track-card-content">` (the class already carries all the
real styling — padding, flex, z-index, scrim). Recommend replacing it too, for consistency.

### Pros
- **Removes the conflict at the source.** No `.mud-paper` background means no `!important`
  anywhere. Section 8 backgrounds become plain single-class rules that win by default.
- **Re-enables CSS isolation as an option.** With plain divs, a future `TrackCard.razor.css`
  *would* work (Blazor stamps the scope attribute on divs it renders). We are not required to use
  it — section 8's public-only scoping is still convenient per the predecessor note — but the door
  reopens. This satisfies the "design the seam" instinct: we are not just fixing today, we are
  removing a structural block.
- **Precedent already exists in this codebase.** `NowPlayingCard.razor` is built entirely from
  plain `<div>`s with a `.razor.css` and achieves the *exact aesthetic these cards are imitating*.
  This option makes TrackCard structurally consistent with the component it was told to match.
  The two should be siblings in construction, not one Material and one hand-rolled.
- **Lighter DOM + less CSS the browser must override.** Removes `.mud-card` / `.mud-paper`
  rulesets from applying to these nodes entirely.

### Cons — what MudBlazor functionality is lost
- **Elevation shadow:** already `Elevation="0"` on both elements (current Razor). Nothing lost —
  the design explicitly moved to flat-glass (predecessor §3d). A plain div is the honest
  expression of `Elevation="0"`.
- **Ripple:** `MudCard`/`MudPaper` do **not** ripple (ripple is a `MudButton`/`MudFab`/`MudIconButton`
  behavior). The card is not clickable as a whole — the only interactive element is the `MudFab`,
  which stays and keeps its ripple. Nothing lost.
- **ARIA / semantics:** `MudCard` renders a plain `<div>` with no implicit role — it adds no ARIA
  semantics a plain div lacks. The card conveys meaning through its text content and the labeled
  FAB, not through a container role. Nothing lost. (If anything, a future enhancement could add an
  explicit `role`/`aria-label` to the div — easier on a plain element than fighting MudBlazor's.)
- **Theme reactivity of the surface color:** we *want* to lose this — the whole problem is that
  MudBlazor repaints the surface from the palette. Our section-8 rules are already theme-aware via
  `.deepdrft-theme-dark` / `-light` wrappers, so theme switching still works.
- **Migration cost:** ~6 lines of Razor changed. Trivial.

**Assessment:** acceptable in full. There is no MudBlazor capability the track-card shell actually
uses. This is the option that resolves the root cause rather than the symptom.

---

## §3. Option B — Inline `Style="background: transparent"` on MudCard/MudPaper

Keep the MudBlazor components; pass an inline style to clear their background:

```razor
<MudCard Class="deepdrft-track-card-container" Elevation="0" Style="background: transparent;">
<MudPaper Class="deepdrft-track-card-fallback" Elevation="0" Style="background: transparent;">
```

Inline styles have specificity above any selector (and above `!important` from a stylesheet, for
non-`!important` inline declarations the inline wins over normal stylesheet rules; MudBlazor's
`.mud-paper` background is *not* `!important`, so inline transparent beats it cleanly).

But note: the fallback's *real* navy-glass paint then has to come from **somewhere**. If we set
the MudPaper transparent inline and paint via `.deepdrft-track-card-fallback` in CSS, we are
back to a CSS class trying to beat `.mud-paper` — except now `.mud-paper` is already transparent
(inline), so our class wins without `!important`. That works. The container is simpler: it just
wants transparent, which the inline gives directly.

### Pros
- No `!important`. Inline transparent neutralizes `.mud-paper`, then our classes paint normally.
- Smaller change than Option A (no element swap), keeps MudBlazor components if there's a reason
  to (there isn't a strong one here).
- No risk of `MudCardContent`-without-`MudCard` edge cases.

### Cons
- **Scatters styling concern into Razor.** The whole point of section 8 is centralized,
  theme-aware card styling. An inline `Style` on the component is a second place card appearance
  is decided — exactly the maintenance split the team should avoid. Next person editing the card
  background looks in section 8, doesn't find the override, and is confused.
- **Doesn't address the root mismatch.** We're still using a surface component as a layout shell
  and then telling it "don't be a surface." It reads as an apology for the wrong component choice.
- **CSS isolation stays blocked** — MudBlazor still renders the inner HTML, so a `.razor.css`
  still can't reach it. The seam stays closed.
- **Inline `transparent` is itself a quiet "magic override."** It's `!important` by another name —
  a higher-specificity hammer applied at the element. Marginally cleaner than `!important` in a
  sheet, but the same category of move: beating MudBlazor by force rather than not inviting the
  fight.

**Assessment:** cleaner than `!important` strictly speaking, but trades one smell (sheet
`!important`) for a milder one (scattered inline overrides) and leaves the structural mismatch and
the blocked isolation seam intact. A lateral move, not a fix.

---

## §4. Option C — Override `--mud-palette-surface` locally on the container

Scope a CSS custom-property override to the card so MudBlazor's own
`background-color: var(--mud-palette-surface)` resolves to transparent inside the card subtree:

```css
.deepdrft-track-card-container {
    --mud-palette-surface: transparent;
}
```

Because `.mud-paper` reads `var(--mud-palette-surface)` and custom properties inherit, both the
container's own paint and the nested `MudPaper` fallback would inherit the transparent value — no
`!important` needed (we're changing the *input* to MudBlazor's rule, not overriding the rule).

### Pros
- No `!important`, no inline styles, no element swap. One declaration.
- Works *with* MudBlazor's mechanism instead of against it — arguably the most "correct" CSS-vars
  approach. We feed the palette variable the value we want for this subtree.
- Centralized in section 8.

### Cons
- **Blast radius is the whole subtree.** `--mud-palette-surface` is read by *many* MudBlazor
  components, not just paper. Any MudBlazor component nested in the card that relies on
  `--mud-palette-surface` (now or in future) silently renders transparent. Today the card nests
  `MudChip` and `MudFab` — both read other palette vars (`--mud-palette-primary` etc.), but a
  future addition (a `MudMenu`, a `MudTooltip` surface, a `MudPaper` accent) would inherit the
  transparent surface and break subtly. This is a **spooky-action-at-a-distance** override: it
  works today but plants a trap.
- **Fragile against MudBlazor internals.** It depends on `.mud-paper` continuing to read exactly
  `--mud-palette-surface` for its background. If a MudBlazor version computes the surface
  differently (e.g. a derived var, or `color-mix`), the override misses. Coupling our fix to a
  framework implementation detail is a maintenance liability — the kind of thing that breaks on a
  minor version bump with no compile error.
- **Still doesn't reopen CSS isolation.** Same as B — MudBlazor renders the HTML.
- **Obscure.** The next maintainer sees a card rendering transparent and has no obvious thread to
  pull — the cause is a custom-property override three rules up, not a `background` declaration.
  Low discoverability.

**Assessment:** the most elegant-looking, the most dangerous. It's a clever override that depends
on framework internals and leaks into every descendant. Reject for the trap it sets, despite the
aesthetics.

---

## §5. Option D — Keep `!important` with a clear comment

Leave the four `!important`s, add a comment explaining the MudBlazor specificity conflict, accept
it as documented technical debt.

### Pros
- Zero change, zero risk. The cards render correctly today.
- A good comment turns a mystery smell into a known, explained trade-off — which is genuinely
  better than an *unexplained* `!important`.

### Cons
- **Doesn't meet the stated goal.** The task is explicitly to *eliminate* `!important` from
  section 8. Documenting it is not eliminating it.
- **`!important` is contagious.** Once the card backgrounds are `!important`, any future rule that
  needs to legitimately override them (a "now playing" highlight state on a card, a selected
  state, a hover treatment) must *also* be `!important`, escalating the war. The predecessor note
  already floats a genre-chip treatment and skeleton tints — feature growth on this card is
  expected, and each new state inherits the `!important` tax.
- **Leaves the structural mismatch and blocked isolation seam in place** — same as B and C.

**Assessment:** honest, but it's the do-nothing option dressed as a decision. Acceptable only if
all other options were blocked, which they are not. The comment is worth keeping *as a fallback*
if Option A is somehow rejected.

---

## §6. Recommendation — Option A (plain `<div>` shell)

**Replace `MudCard` and the fallback `MudPaper` with plain `<div>`s. Replace `MudCardContent` with
a plain `<div>` too (it carries no behavior). Keep `MudText`, `MudChip`, `MudFab`.** This removes
all four `!important` declarations, preserves the glass-card aesthetic exactly (the section-8 paint
rules are unchanged except for dropping `!important`), is the most maintainable, and aligns
TrackCard structurally with `NowPlayingCard` — the very component it was designed to imitate.

Why A over the others, in one line each:
- **Over B (inline transparent):** A fixes the root mismatch and reopens the isolation seam; B
  scatters overrides and leaves both problems.
- **Over C (palette-var override):** A has no blast radius and no coupling to MudBlazor internals;
  C plants a transparent-surface trap for every future nested component.
- **Over D (keep `!important`):** A meets the stated goal; D doesn't.

Trade-off being accepted with A: TrackCard's shell stops being a MudBlazor component. That is a
*feature* here (the card never used MudBlazor's surface behavior), but it does mean the shell no
longer auto-reacts to a future palette change to `Surface` — which is exactly what we want, since
our theme-aware section-8 rules own the card's appearance. No real cost.

---

### 6.1 Exact Razor changes — `DeepDrftShared.Client/Components/TrackCard.razor`

Replace the current file body with the following. Only the three shell elements change
(`MudCard`→`div`, `MudPaper`→`div`, `MudCardContent`→`div`); all class hooks, `MudText`,
`MudChip`, and `MudFab` are preserved verbatim from the current file.

```razor
<div class="deepdrft-track-card-container">

    @if (!string.IsNullOrEmpty(TrackModel?.ImagePath))
    {
        <div class="deepdrft-track-card-bg" style="background-image: url('@TrackModel.ImagePath');">
        </div>
    }
    else
    {
        <div class="deepdrft-track-card-fallback"></div>
    }

    <div class="deepdrft-track-card-content">

        <div class="deepdrft-track-info-top">
            <MudText Typo="Typo.subtitle1"
                     Class="deepdrft-track-title text-truncate mb-1">
                @TrackModel?.TrackName
            </MudText>

            <MudText Typo="Typo.caption"
                     Class="deepdrft-track-artist text-truncate mb-2">
                @TrackModel?.Artist
            </MudText>
        </div>

        <div class="deepdrft-track-info-middle">
            @if (!string.IsNullOrEmpty(TrackModel?.Album))
            {
                <MudText Typo="Typo.caption"
                         Class="deepdrft-track-meta text-truncate">
                    @TrackModel.Album
                </MudText>
            }

            @if (!string.IsNullOrEmpty(TrackModel?.Genre))
            {
                <MudChip T="string"
                         Size="Size.Small"
                         Variant="Variant.Filled"
                         Color="Color.Primary"
                         Class="deepdrft-genre-chip">
                    @TrackModel.Genre
                </MudChip>
            }
        </div>

        <div class="deepdrft-track-info-bottom">
            @if (TrackModel?.ReleaseDate.HasValue == true)
            {
                <MudText Typo="Typo.caption"
                         Class="deepdrft-track-meta">
                    @TrackModel.ReleaseDate.Value.Year
                </MudText>
            }
            else
            {
                <div></div>
            }

            <MudFab Color="Color.Primary"
                    Size="Size.Medium"
                    StartIcon="@PlayPauseIcon"
                    OnClick="@PlayClick"/>
        </div>

    </div>

</div>
```

Changes, precisely:
- Line 1–2: `<MudCard Class="deepdrft-track-card-container" Elevation="0">` →
  `<div class="deepdrft-track-card-container">`. (Drop `Elevation="0"` — meaningless on a div; the
  flat look is the default.)
- Line 11–13: `<MudPaper Class="deepdrft-track-card-fallback" Elevation="0"></MudPaper>` →
  `<div class="deepdrft-track-card-fallback"></div>`.
- Line 16: `<MudCardContent Class="deepdrft-track-card-content">` →
  `<div class="deepdrft-track-card-content">`.
- Line 72 (`</MudCard>`) → `</div>`; the `</MudCardContent>` close (line 70) → `</div>`.

No change to `TrackCard.razor.cs` — it references no MudBlazor card types (only `Icons`,
`TrackDto`, `EventCallback`). The `using MudBlazor;` stays (still used by `Icons.Material`).

### 6.2 Exact CSS changes — `DeepDrftPublic/wwwroot/styles/deepdrft-styles.css` section 8

Remove `!important` from all four declarations. No other change — the colors, layout, and
theme-aware scoping all stay exactly as they are.

**Line 216–223** — container:
```css
.deepdrft-track-card-container {
    width: 250px;
    height: 250px;
    min-width: 250px;
    position: relative;
    overflow: hidden;
    background: transparent;        /* was: transparent !important */
}
```

**Line 262–269** — fallback base:
```css
.deepdrft-track-card-fallback {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--deepdrft-navy-mid, #162437);   /* was: ... !important */
}
```

**Line 272–276** — fallback dark:
```css
.deepdrft-theme-dark .deepdrft-track-card-fallback {
    background: color-mix(in srgb, var(--deepdrft-navy) 55%, transparent);   /* was: ... !important */
    border: 1px solid rgba(250, 250, 248, 0.12);
    backdrop-filter: blur(8px);
}
```

**Line 279–282** — fallback light:
```css
.deepdrft-theme-light .deepdrft-track-card-fallback {
    background: color-mix(in srgb, var(--deepdrft-navy) 8%, var(--deepdrft-white));   /* was: ... !important */
    border: 1px solid var(--deepdrft-border);
}
```

After this change, the four backgrounds are plain single-class (and class-under-theme-wrapper)
rules painting plain `<div>`s. Nothing competes; nothing needs `!important`. The
`.deepdrft-theme-dark .deepdrft-track-card-fallback` selector is `0,2,0` and the base is `0,1,0`,
so the theme override correctly beats the base — the normal cascade, working as intended.

### 6.3 Verification checklist for the implementer
- Dark public site: card with album art shows art + scrim + green/off-white text; card without
  art shows navy-glass fallback. No grey/teal surface flash on hydration (the base
  `--deepdrft-navy-mid` fallback rule still covers the no-wrapper-class window).
- Light public site: fallback shows the navy-tint-on-white panel; text legible.
- Toggle dark/light: card repaints correctly (theme-wrapper-scoped rules still drive it).
- Confirm no other component references `.deepdrft-track-card-container` / `-fallback` expecting a
  `.mud-paper` ancestor (grep — `TracksGallery.razor` wraps cards but only positions them via
  `.deepdrft-track-gallery-item-center`).
- Confirm the rendered card root is now a plain `<div>` (devtools) with **no** `.mud-paper` /
  `.mud-card` class, and that no `background ... !important` remains anywhere in section 8.

### 6.4 What this unblocks (not in scope, noted for the seam)
With a plain-div shell, the deferred polish items from the predecessor note become cleaner to
implement and could, if desired, migrate to a real `TrackCard.razor.css` (now functional):
genre-chip distinct treatment (predecessor §3a), skeleton tints (§3c), card hover/selected/now-
playing states. None are required by this spec; flagged so a future pass knows the seam is open.

---

## §7. Fallback position

If Option A is rejected for a reason not surfaced here (e.g. a policy that shell components must
remain MudBlazor for consistency with other cards), the next-best is **Option D with comments** —
not B or C. B scatters the override and C couples to framework internals; a *documented*
`!important` is more honest and more maintainable than either clever hack. But A has no real cost
identified, so this fallback should not be needed.
```