The Material theme setup in apps/web/src/m3-theme.scss uses the older M3
API (mat.define-theme + mat.all-component-themes), which emits component
tokens but never emits the --mat-sys-* system tokens. As a result ~540
var(--mat-sys-*) references across the app were silent no-ops: the
declarations were invalid at computed-value time and fell back to
inherited/initial values, so many backgrounds, borders and dividers never
rendered and several text/icon colors sat at the wrong contrast.
Rather than migrate to mat.theme() (which would flip every no-op at once
with M3 azure-palette values foreign to the hand-tuned design), remap the
high-visibility usages to the existing --app-* design tokens defined for
both themes in m3-theme.scss, verified screenshot-driven in light and dark
via Electron CDP.
Changes:
- Add --app-error-color and --app-accent-color tokens (light + dark).
- Fix high-visibility surfaces: settings area, workspace shell header,
playlist switcher, downloads, channel-details dialog, account-info,
group/category management dialogs, sources context/filters panels,
nav-list, and the .dark-theme app background.
- Point the Tailwind color palette at --app-* tokens instead of --mat-sys-*.
- Strip ~150 dead var(--app-x, var(--mat-sys-y)) fallbacks repo-wide (the
fallback can never be taken).
- UI polish surfaced by the remap: suppress Material's circular icon-button
state layer in the header (keep the rounded-rectangle hover), remove the
orphan divider before the download button, remove the redundant right-edge
accent bar on active rail items (the filled chip is enough), and raise the
header/switcher control icons from the muted tier to --app-body-color so
they no longer read as disabled.
Guard against regressions:
- tools/styles/check-mat-sys-usage.mjs fails on any new var(--mat-sys-*)
reference; legacy usages are grandfathered in a shrink-only baseline
(tools/styles/mat-sys-baseline.mjs). Wired into the CI Lint job and
exposed as `pnpm run lint:styles`.
Docs:
- docs/architecture/theme-design-tokens.md documents why the tokens are
no-ops, the --app-* mapping table, and the screenshot-driven migration
protocol. CLAUDE.md updated with the token policy and the new lint check.
Scrollbar-related --mat-sys-* usages are intentionally left untouched here;
they belong to the separate branch claude/iptvnator-dark-scrollbar-2600aa.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`--settings-action-bar-space: 80px` added padding-bottom to the form
*after* its last child (the action bar), giving 80px of scrollable
empty space below the bar — visible when scrolling to the end as a
big dark band the page kept scrolling past the last setting.
The reservation was originally tuned for the OLD floating-chip
layout, where the bar hovered 24px above the bottom edge and content
needed to be pushed above it. In the new sticky-footer model the bar
IS the bottom edge, so no padding is needed below it.
Set to 0px. CDP verifies form.bottom === bar.bottom after the change
— the scroll now ends cleanly at the action bar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`--settings-action-bar-offset: 24px` was tuned for the previous
floating-chip layout (border-radius:18px, margin-left:auto) so the bar
visually hovered above the bottom edge like a card. After converting
it to a full-width sticky footer the 24px gap just left a strip of
empty content peeking out below the bar — the user-reported "not
perfectly aligned to the footer" gap.
Set the offset to 0px so the bar sits flush against the bottom of the
scroll container. CDP verifies: barBottom === viewport height after
the change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>