From 43358e10f2bd6a62d0b3d5b73bd8a91232a9ecf2 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sun, 11 Oct 2026 12:22:23 +0200 Subject: [PATCH] fix(ui): welcome screens and drop overlay follow the app theme (#1886) * fix(ui): welcome screens and drop overlay follow the app theme The welcome dashboard, the empty Sources page and the playlist drop overlay switched on `prefers-color-scheme`, so with the app set to dark on a light OS (or light on a dark OS) they painted the other theme's colours. They now read `--app-*` tokens, which follow the `.dark-theme` class set from Settings, and the hard-coded blues are derived from the selection colour (a local "strong" accent mixed toward the heading ink keeps chips and filled labels at 4.5:1 in both themes). White rgba() fills that vanished in the light theme (season empty panel, catalog refinement chips and menu divider, Xtream archive banner and disabled paginator icons, shell download-activity track, search field) now use the widget surface, on-surface mixes or the search tokens. Reads of custom properties that nothing declares are fixed: the release notes error, the search-layout empty state and the collection reload dim now use declared tokens; unset hooks are replaced by their fallback value. New guard `pnpm run styles:theme-references:validate` (CI) rejects undeclared var() reads and prefers-color-scheme outside the settings resolver. Electron E2E os-color-scheme.e2e.ts flips the OS scheme under each explicit app theme and checks colours, contrast and screenshots. Co-Authored-By: Claude Opus 5.5 * test(e2e): retry the rejected-drop comparison when the card dismisses mid-read The rejection hides itself after 1.8s, and the OS-scheme comparison reads the overlay twice with an emulateMedia call between. A slow runner could lose the card between the reads; the comparison now drops again until both reads see it. A colour that follows the OS still fails every attempt. Co-Authored-By: Claude Opus 5.5 * test(e2e): measure welcome text contrast from rendered pixels The feature and source cards paint gradients, which a backgroundColor walk ignores, so card titles, descriptions and chips could pass while falling short on the actual card. Every text on the three surfaces is now measured against the pixels under it, as the filled button labels were. Co-Authored-By: Claude Opus 5.5 * fix(tools): scan only runtime sources in the theme-references guard The guard read every tracked file under apps/ and libs/ except specs, so an E2E, mock-server or test-helper declaration could satisfy a runtime var() read that nothing in the app declares (dashboard-rail-focus.e2e.ts sets --cover-rail-width), and a test-only read could fail the check. It now skips spec/test/e2e files, test helpers and stubs, testing projects, the E2E and mock-server apps and the marketing website. Co-Authored-By: Claude Opus 5.5 * fix(xtream): set live paginator tokens through mat.paginator-overrides The live layout hand-declared --mat-paginator-* tokens, which the UI guidelines route through the overrides mixin so a mistyped name fails the build instead of silently doing nothing. Same values, same output. Co-Authored-By: Claude Opus 5.5 * fix(tools): skip every test-only naming convention in the theme guard The runtime scan still read .stub, .harness, .fixtures, .spec-stubs, .spec-helpers, .spec-fixtures, test-setup and test-double sources, and test-stubs/ or *fixtures/ directories, so a declaration in one could mask an undeclared runtime read. A file is now test-only when a dot segment after its name marks it (spec, stub, fixture, harness, mock, spec-*, test-*, *-fixtures), its stem is a test bootstrap, or it sits in a test directory. Runtime names such as xtream-connection-test.service.ts stay in the scan; no runtime source imports an excluded file. Co-Authored-By: Claude Opus 5.5 * fix(ui): keep welcome card text legible while hovered At the 16%/10% hover tint the dark theme's body text on a feature card measured 4.45:1. The hover fill steps up to 10%/6% instead (4.78:1 in dark, 6.6:1 in light); lift, border and shadow carry the rest. The E2E now measures feature and source card text while hovered, in both themes; with the old tint it fails at 4.45. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- .changes/ui-welcome-follows-app-theme.md | 6 + .codex/skills/iptvnator-theme-style/SKILL.md | 7 +- .github/workflows/ci.yml | 5 + .../src/os-color-scheme.e2e.ts | 334 ++++++++++++++++++ ...p-update-release-notes-dialog.component.ts | 2 +- docs/architecture/iptvnator-ui-guidelines.md | 14 + .../empty-state/empty-state.component.scss | 33 +- .../empty-state/empty-state.component.ts | 1 - .../empty-state/empty-state.themes.scss | 38 -- .../empty-state.welcome-dashboard.scss | 185 ++++------ .../empty-state.welcome-sources.scss | 96 ++--- .../category-content-view.component.scss | 6 +- .../search-layout.component.scss | 4 +- .../unified-collection-page.component.scss | 2 + .../stalker-itv-all-items.component.scss | 2 +- .../live-stream-layout.component.scss | 34 +- .../season-container.component.scss | 45 +-- .../src/lib/vod-sources/_source-tag.scss | 2 +- .../vod-source-copy-row.component.scss | 2 +- .../vod-sources/vod-source-row.component.scss | 2 +- .../vod-sources-chip.component.scss | 2 +- .../vod-sources-menu.component.scss | 2 +- libs/ui/epg/src/lib/_epg-theme.scss | 2 +- .../epg-item-description.component.scss | 4 +- .../epg-timeline-track.component.scss | 2 +- .../playback-diagnostic-panel.component.scss | 2 +- .../lib/rails/_dashboard-rail-geometry.scss | 2 +- .../playlist-drop-overlay.component.scss | 69 ++-- .../workspace-shell-header.component.scss | 2 +- package.json | 3 + tools/nx/check-theme-references.mjs | 234 ++++++++++++ tools/nx/check-theme-references.test.mjs | 223 ++++++++++++ 32 files changed, 1033 insertions(+), 334 deletions(-) create mode 100644 .changes/ui-welcome-follows-app-theme.md create mode 100644 apps/electron-backend-e2e/src/os-color-scheme.e2e.ts delete mode 100644 libs/playlist/shared/ui/src/lib/recent-playlists/empty-state/empty-state.themes.scss create mode 100644 tools/nx/check-theme-references.mjs create mode 100644 tools/nx/check-theme-references.test.mjs diff --git a/.changes/ui-welcome-follows-app-theme.md b/.changes/ui-welcome-follows-app-theme.md new file mode 100644 index 000000000..b44e3f43c --- /dev/null +++ b/.changes/ui-welcome-follows-app-theme.md @@ -0,0 +1,6 @@ +--- +type: fix +area: ui +--- + +The welcome screen, the empty Sources page and the drop-a-playlist overlay now follow the theme chosen in Settings instead of the system's light or dark mode. Panels, borders and the search box that were invisible in the light theme now show, and a few labels that fell back to the wrong colour use the theme's colours. diff --git a/.codex/skills/iptvnator-theme-style/SKILL.md b/.codex/skills/iptvnator-theme-style/SKILL.md index 0ff05e5e4..14ff5151d 100644 --- a/.codex/skills/iptvnator-theme-style/SKILL.md +++ b/.codex/skills/iptvnator-theme-style/SKILL.md @@ -15,13 +15,14 @@ description: Use when changing IPTVnator SCSS tokens, shared layout mixins, port `libs/ui/styles/_detail-view-actions.scss` - UI policy and migration debt: `docs/architecture/iptvnator-ui-guidelines.md` -The index is a barrel, not a configured Sass include path. Production -consumers currently use relative `@use` paths to the needed partial. +The index is a barrel, not a Sass include path: consumers `@use` each +partial by relative path. ## Token Boundary - App-owned surfaces, text, separators, hover states, selections, and provider - accents use `--app-*` tokens from `m3-theme.scss`. + accents use `--app-*` tokens from `m3-theme.scss`, switched by + `.dark-theme`, not `prefers-color-scheme`. - Use `--app-selection-on-color` for foregrounds placed on the selection accent; do not assume white has enough contrast in both themes. - Angular Material mixins and Material-component overrides may use Material diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66cbc9a91..f5a2e1418 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -418,6 +418,11 @@ jobs: - name: Validate Material icon names run: pnpm run styles:icon-ligatures:validate + # A var() read nothing declares always falls back, and a + # prefers-color-scheme query follows the OS, not the app theme. + - name: Validate theme references + run: pnpm run styles:theme-references:validate + # A tooltip is not an accessible name: an icon-only