diff --git a/apps/web/src/m3-theme.scss b/apps/web/src/m3-theme.scss index 60b860acc..4f8ac3325 100644 --- a/apps/web/src/m3-theme.scss +++ b/apps/web/src/m3-theme.scss @@ -15,11 +15,17 @@ $my-typography: mat.m2-define-typography-config( @include mat.typography-hierarchy($my-typography); // ─── M3 Themes ─────────────────────────────────────────────────────────────── +// One primary color across the app. Azure is the closest M3 prebuilt to the +// product's "active/selection" blue (#2f7bff / #78adff), so checkboxes, radio +// buttons, raised CTAs, etc. now read as the same blue as rail selection. +// +// Cyan stays on tertiary so the EPG "now" indicator (which uses Material +// tertiary surfaces) keeps its existing visual role. $light-theme: mat.define-theme( ( color: ( theme-type: light, - primary: mat.$violet-palette, + primary: mat.$azure-palette, tertiary: mat.$cyan-palette, ), typography: ( @@ -33,7 +39,7 @@ $dark-theme: mat.define-theme( ( color: ( theme-type: dark, - primary: mat.$violet-palette, + primary: mat.$azure-palette, tertiary: mat.$cyan-palette, ), typography: ( @@ -102,17 +108,17 @@ html { --app-eyebrow-color: #7c8495; // Provider accent colors - --app-provider-xtream: #6366f1; + --app-provider-xtream: #2f7bff; --app-provider-stalker: #10b981; --app-provider-m3u: #d97706; // Hero --app-accent-gradient: linear-gradient( 135deg, - rgba(99, 102, 241, 0.08) 0%, - rgba(168, 85, 247, 0.05) 100% + rgba(47, 123, 255, 0.08) 0%, + rgba(92, 214, 255, 0.05) 100% ); - --app-hero-surface: rgba(99, 102, 241, 0.04); + --app-hero-surface: rgba(47, 123, 255, 0.04); .dark-theme { @include mat.all-component-colors($dark-theme); @@ -178,15 +184,15 @@ html { --app-eyebrow-color: #6b7494; // eyebrow labels // Provider accents (slightly brighter for dark bg) - --app-provider-xtream: #818cf8; + --app-provider-xtream: #78adff; --app-provider-stalker: #34d399; --app-provider-m3u: #fbbf24; // Hero section --app-accent-gradient: linear-gradient( 135deg, - rgba(99, 102, 241, 0.12) 0%, - rgba(168, 85, 247, 0.07) 100% + rgba(120, 173, 255, 0.12) 0%, + rgba(92, 214, 255, 0.07) 100% ); --app-hero-surface: rgba(30, 35, 48, 0.9); } diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html index 25fd20642..e1cb858d0 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.html @@ -90,7 +90,7 @@ @case ('url') { - diff --git a/libs/ui/epg/src/lib/multi-epg/multi-epg-container.component.scss b/libs/ui/epg/src/lib/multi-epg/multi-epg-container.component.scss index 6b4724f7b..05885b7ba 100644 --- a/libs/ui/epg/src/lib/multi-epg/multi-epg-container.component.scss +++ b/libs/ui/epg/src/lib/multi-epg/multi-epg-container.component.scss @@ -6,10 +6,12 @@ $bg-hover: #24243a; $border-subtle: rgba(255, 255, 255, 0.06); $border-medium: rgba(255, 255, 255, 0.12); -// Accent colors - TV broadcast vibes -$accent-primary: #00d4aa; // Cyan/teal for time -$accent-secondary: #8b5cf6; // Purple for programs -$accent-tertiary: #f472b6; // Pink for highlights +// Accent colors — unified with the rest of the app: +// cyan is reserved for the EPG "now" indicator, blue is the app primary, +// purple/pink had no semantic role and are dropped per the UX audit. +$accent-primary: #5cd6ff; // Cyan — reserved for "now" indicator +$accent-secondary: #4f8eff; // Blue — programs (matches --app-selection-color) +$accent-tertiary: #4f8eff; // Blue — highlights $accent-warning: #fbbf24; // Text colors @@ -20,17 +22,17 @@ $text-muted: rgba(248, 250, 252, 0.4); // Gradients $gradient-program: linear-gradient( 135deg, - rgba(139, 92, 246, 0.15) 0%, - rgba(139, 92, 246, 0.05) 100% + rgba(79, 142, 255, 0.15) 0%, + rgba(79, 142, 255, 0.05) 100% ); $gradient-program-hover: linear-gradient( 135deg, - rgba(139, 92, 246, 0.3) 0%, - rgba(139, 92, 246, 0.15) 100% + rgba(79, 142, 255, 0.3) 0%, + rgba(79, 142, 255, 0.15) 100% ); $gradient-header: linear-gradient( 180deg, - rgba(0, 212, 170, 0.08) 0%, + rgba(92, 214, 255, 0.08) 0%, transparent 100% ); diff --git a/libs/ui/playback/src/lib/audio-player/audio-player.component.scss b/libs/ui/playback/src/lib/audio-player/audio-player.component.scss index b1618faec..148235b42 100644 --- a/libs/ui/playback/src/lib/audio-player/audio-player.component.scss +++ b/libs/ui/playback/src/lib/audio-player/audio-player.component.scss @@ -155,10 +155,13 @@ background: color-mix(in srgb, var(--mat-sys-surface-container) 60%, transparent); backdrop-filter: blur(6px); + // LIVE stays in the app's red "broadcasting now" role across every + // surface (channel rows, EPG now-bar, badges). The station's extracted + // dominant color is only used as a soft aura on the artwork. &.live { - color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); - border-color: color-mix(in srgb, var(--radio-accent, var(--app-selection-color)) 52%, transparent); - background: color-mix(in srgb, var(--radio-accent, var(--app-selection-color)) 14%, transparent); + color: var(--app-live-color, #ff8f8f); + border-color: color-mix(in srgb, var(--app-live-color, #ff8f8f) 52%, transparent); + background: color-mix(in srgb, var(--app-live-color, #ff8f8f) 14%, transparent); } } @@ -199,9 +202,11 @@ } } +// Play / Pause uses the unified app primary, not the station's dominant color +// — same blue as every other CTA so radio doesn't feel like a different app. .play-btn { - --mdc-fab-container-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); - --mat-fab-container-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); + --mdc-fab-container-color: var(--app-selection-color, var(--mat-sys-primary)); + --mat-fab-container-color: var(--app-selection-color, var(--mat-sys-primary)); --mdc-fab-icon-color: #fff; --mat-fab-foreground-color: #fff; --mat-fab-icon-color: #fff; @@ -238,10 +243,10 @@ .vol-slider { flex: 1; - --mdc-slider-active-track-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); - --mdc-slider-handle-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); - --mat-slider-active-track-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); - --mat-slider-handle-color: var(--radio-accent, var(--app-selection-color, var(--mat-sys-primary))); + --mdc-slider-active-track-color: var(--app-selection-color, var(--mat-sys-primary)); + --mdc-slider-handle-color: var(--app-selection-color, var(--mat-sys-primary)); + --mat-slider-active-track-color: var(--app-selection-color, var(--mat-sys-primary)); + --mat-slider-handle-color: var(--app-selection-color, var(--mat-sys-primary)); --mdc-slider-inactive-track-color: var(--mat-sys-outline-variant); --mdc-slider-handle-width: 12px; --mdc-slider-handle-height: 12px; diff --git a/libs/ui/playback/src/lib/external-player-info-dialog/external-player-info-dialog.component.ts b/libs/ui/playback/src/lib/external-player-info-dialog/external-player-info-dialog.component.ts index 61f8d1eae..5ee4e0a0e 100644 --- a/libs/ui/playback/src/lib/external-player-info-dialog/external-player-info-dialog.component.ts +++ b/libs/ui/playback/src/lib/external-player-info-dialog/external-player-info-dialog.component.ts @@ -37,7 +37,7 @@ import { TranslateModule } from '@ngx-translate/core'; >Don't show anymore - diff --git a/libs/ui/styles/_detail-view-actions.scss b/libs/ui/styles/_detail-view-actions.scss index 867a4cac0..46cfe6e58 100644 --- a/libs/ui/styles/_detail-view-actions.scss +++ b/libs/ui/styles/_detail-view-actions.scss @@ -29,6 +29,10 @@ } } +// Secondary detail-view actions (Favorite, Download). Per UX audit: only Play +// should read as the primary CTA, so these stay ghost/neutral. State accents +// (favorite=red destructive, download=green completed) keep their semantic +// meaning but ride on transparent fills so they never out-shout Play. @mixin secondary-action-button { display: inline-flex; align-items: center; @@ -37,15 +41,16 @@ padding: 0 24px; font-size: 1rem; font-weight: 600; - background: rgba(109, 109, 110, 0.7); - color: #fff; - border: none; + background: rgba(255, 255, 255, 0.08); + color: var(--text-primary, #fff); + border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 16px; cursor: pointer; transition: all 0.2s ease; &:hover { - background: rgba(109, 109, 110, 0.5); + background: rgba(255, 255, 255, 0.14); + border-color: rgba(255, 255, 255, 0.2); } mat-icon { @@ -57,36 +62,37 @@ @mixin favorite-button-state { &--active { - background: rgba(229, 9, 20, 0.8); + background: rgba(255, 91, 110, 0.16); + border-color: rgba(255, 91, 110, 0.4); + color: #ff8f8f; &:hover { - background: rgba(229, 9, 20, 0.6); + background: rgba(255, 91, 110, 0.24); } } } @mixin download-button-state { - background: rgba(33, 150, 243, 0.8); - - &:hover { - background: rgba(33, 150, 243, 0.6); - } + // Idle download stays neutral — extends secondary-action-button. &--completed { - background: rgba(76, 175, 80, 0.8); + background: rgba(74, 222, 128, 0.16); + border-color: rgba(74, 222, 128, 0.4); + color: #4ade80; &:hover { - background: rgba(76, 175, 80, 0.6); + background: rgba(74, 222, 128, 0.24); } } &--downloading { - background: rgba(109, 109, 110, 0.5); + background: rgba(255, 255, 255, 0.04); + border-color: rgba(255, 255, 255, 0.08); cursor: not-allowed; - opacity: 0.7; + opacity: 0.6; &:hover { - background: rgba(109, 109, 110, 0.5); + background: rgba(255, 255, 255, 0.04); } } } diff --git a/libs/ui/styles/_detail-view.scss b/libs/ui/styles/_detail-view.scss index 98b012e2b..e4b248f00 100644 --- a/libs/ui/styles/_detail-view.scss +++ b/libs/ui/styles/_detail-view.scss @@ -24,18 +24,21 @@ --text-primary: #1a1a1a; --text-secondary: rgba(26, 26, 26, 0.75); --text-muted: rgba(26, 26, 26, 0.5); - --accent-color: #b81d24; + // Single primary CTA color — matches --app-selection-color so the + // Play button reads as the same blue used everywhere else for + // "active / call to action". + --accent-color: #2f7bff; --tag-bg: rgba(0, 0, 0, 0.06); --tag-border: rgba(0, 0, 0, 0.12); --play-idle-bg: linear-gradient( 135deg, - #fffaf2 0%, - #ffffff 52%, - #f7ecdf 100% + #2f7bff 0%, + #3b86ff 52%, + #4a90ff 100% ); - --play-idle-fg: #161310; - --play-idle-border: rgba(88, 48, 18, 0.12); - --play-idle-shadow: 0 18px 38px rgba(91, 56, 24, 0.14); + --play-idle-fg: #ffffff; + --play-idle-border: rgba(47, 123, 255, 0.4); + --play-idle-shadow: 0 18px 38px rgba(47, 123, 255, 0.32); --play-launching-bg: linear-gradient( 135deg, #ffd36f 0%, @@ -74,19 +77,19 @@ --text-primary: #fff; --text-secondary: rgba(255, 255, 255, 0.7); --text-muted: rgba(255, 255, 255, 0.5); - --accent-color: #e50914; + --accent-color: #78adff; --tag-bg: rgba(255, 255, 255, 0.1); --tag-border: rgba(255, 255, 255, 0.2); --poster-shadow: 0 8px 40px rgba(0, 0, 0, 0.5); --play-idle-bg: linear-gradient( 135deg, - #fff5e6 0%, - #f6efe5 58%, - #e7dac9 100% + #2f7bff 0%, + #4f8eff 58%, + #78adff 100% ); - --play-idle-fg: #15110d; - --play-idle-border: rgba(255, 255, 255, 0.18); - --play-idle-shadow: 0 20px 42px rgba(0, 0, 0, 0.36); + --play-idle-fg: #ffffff; + --play-idle-border: rgba(120, 173, 255, 0.32); + --play-idle-shadow: 0 20px 42px rgba(47, 123, 255, 0.32); --play-launching-bg: linear-gradient( 135deg, #ffdd79 0%,