fix(themes): route remaining UI colours through theme tokens (#1014)

* fix(themes): route remaining UI colours through theme tokens

An audit found several surfaces still wired to the fixed Catppuccin
palette or hardcoded hex, so community themes could not recolour them:
the 5-star rating, the global search field, the gradient-text flourish,
badges and category-avatar text, and the What's New page + sidebar
banner. Rewire them to existing contract tokens — no new tokens, and the
built-in themes look identical (the values match). Community themes now
control these areas.

Also fixes device-sync rows referencing an undefined --bg-secondary
(they rendered with no background); they now use --bg-card.

* docs(themes): note the theme-coverage PR in the Theme Store entry

Add PR #1014 to the still-unreleased Theme Store changelog and credits
entry, alongside the other follow-ups.
This commit is contained in:
Psychotoxical
2026-06-07 12:22:54 +02:00
committed by GitHub
parent 10fc489ce9
commit aad1a6c3f0
10 changed files with 57 additions and 60 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Themes — community Theme Store ### Themes — community Theme Store
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1009](https://github.com/Psychotoxical/psysonic/pull/1009), [#1011](https://github.com/Psychotoxical/psysonic/pull/1011), [#1012](https://github.com/Psychotoxical/psysonic/pull/1012), [#1013](https://github.com/Psychotoxical/psysonic/pull/1013)** **By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1009](https://github.com/Psychotoxical/psysonic/pull/1009), [#1011](https://github.com/Psychotoxical/psysonic/pull/1011), [#1012](https://github.com/Psychotoxical/psysonic/pull/1012), [#1013](https://github.com/Psychotoxical/psysonic/pull/1013), [#1014](https://github.com/Psychotoxical/psysonic/pull/1014)**
* New **Settings → Themes** tab: pick a theme, set the day/night scheduler, and browse a built-in **Theme Store** to install, update and uninstall community themes — with search, a dark/light filter, and full-size thumbnail previews. * New **Settings → Themes** tab: pick a theme, set the day/night scheduler, and browse a built-in **Theme Store** to install, update and uninstall community themes — with search, a dark/light filter, and full-size thumbnail previews.
* The app now bundles six core themes (Catppuccin Mocha & Latte, Kanagawa Wave, Stark HUD, and the colour-blind-safe Vision Dark / Vision Navy); every other palette installs on demand from the [psysonic-themes](https://github.com/Psysonic/psysonic-themes) repo. Installed themes are saved locally and apply instantly at startup, even offline. * The app now bundles six core themes (Catppuccin Mocha & Latte, Kanagawa Wave, Stark HUD, and the colour-blind-safe Vision Dark / Vision Navy); every other palette installs on demand from the [psysonic-themes](https://github.com/Psysonic/psysonic-themes) repo. Installed themes are saved locally and apply instantly at startup, even offline.
+1 -1
View File
@@ -356,7 +356,7 @@ const CONTRIBUTOR_ENTRIES = [
'Performance Probe: Monitor/Toggles redesign, live CPU/RSS/thread metrics, overlay pins and sparklines, macOS snapshots (PR #890)', 'Performance Probe: Monitor/Toggles redesign, live CPU/RSS/thread metrics, overlay pins and sparklines, macOS snapshots (PR #890)',
'Performance Probe: opt-in thread-group CPU poll toggle and includeThreadGroups IPC fix (PR #891)', 'Performance Probe: opt-in thread-group CPU poll toggle and includeThreadGroups IPC fix (PR #891)',
'Queue: switchable display mode — Queue (upcoming only) vs Playlist (full list), with header toggle and settings entry (PR #922)', 'Queue: switchable display mode — Queue (upcoming only) vs Playlist (full list), with header toggle and settings entry (PR #922)',
'Community Theme Store: semantic-token refactor, dedicated Themes tab with day/night scheduler, install/update/uninstall, local .zip import with full contract validation, and 80+ palettes moved to an on-demand CDN repo (PR #1009, #1011, #1012, #1013)', 'Community Theme Store: semantic-token refactor, dedicated Themes tab with day/night scheduler, install/update/uninstall, local .zip import with full contract validation, and 80+ palettes moved to an on-demand CDN repo (PR #1009, #1011, #1012, #1013, #1014)',
], ],
}, },
{ {
+3 -3
View File
@@ -71,7 +71,7 @@ function ComposerRowAvatar({ artist }: { artist: SubsonicArtist }) {
className="artist-avatar artist-avatar-initial" className="artist-avatar artist-avatar-initial"
style={{ background: color, border: 0 }} style={{ background: color, border: 0 }}
> >
<span style={{ color: 'var(--ctp-crust)', fontWeight: 800 }}>{nameInitial(artist.name)}</span> <span style={{ color: 'var(--text-on-accent)', fontWeight: 800 }}>{nameInitial(artist.name)}</span>
</div> </div>
); );
} }
@@ -351,7 +351,7 @@ export default function Composers() {
<button <button
className={`btn btn-surface ${viewMode === 'grid' ? 'btn-sort-active' : ''}`} className={`btn btn-surface ${viewMode === 'grid' ? 'btn-sort-active' : ''}`}
onClick={() => setViewMode('grid')} onClick={() => setViewMode('grid')}
style={viewMode === 'grid' ? { background: 'var(--accent)', color: 'var(--ctp-crust)', padding: '0.5rem' } : { padding: '0.5rem' }} style={viewMode === 'grid' ? { background: 'var(--accent)', color: 'var(--text-on-accent)', padding: '0.5rem' } : { padding: '0.5rem' }}
data-tooltip={t('artists.gridView')} data-tooltip={t('artists.gridView')}
> >
<LayoutGrid size={20} /> <LayoutGrid size={20} />
@@ -359,7 +359,7 @@ export default function Composers() {
<button <button
className={`btn btn-surface ${viewMode === 'list' ? 'btn-sort-active' : ''}`} className={`btn btn-surface ${viewMode === 'list' ? 'btn-sort-active' : ''}`}
onClick={() => setViewMode('list')} onClick={() => setViewMode('list')}
style={viewMode === 'list' ? { background: 'var(--accent)', color: 'var(--ctp-crust)', padding: '0.5rem' } : { padding: '0.5rem' }} style={viewMode === 'list' ? { background: 'var(--accent)', color: 'var(--text-on-accent)', padding: '0.5rem' } : { padding: '0.5rem' }}
data-tooltip={t('artists.listView')} data-tooltip={t('artists.listView')}
> >
<List size={20} /> <List size={20} />
+2 -2
View File
@@ -423,7 +423,7 @@
gap: 8px; gap: 8px;
height: 52px; height: 52px;
padding: 0 14px; padding: 0 14px;
background: var(--bg-secondary); background: var(--bg-card);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
flex-shrink: 0; flex-shrink: 0;
} }
@@ -792,7 +792,7 @@
.device-sync-browser-row:hover { background: var(--bg-hover); } .device-sync-browser-row:hover { background: var(--bg-hover); }
.device-sync-browser-row.selected { background: var(--accent-dim); } .device-sync-browser-row.selected { background: var(--accent-dim); }
.device-sync-browser-row.indent { padding-left: 36px; background: var(--bg-secondary); } .device-sync-browser-row.indent { padding-left: 36px; background: var(--bg-card); }
.device-sync-browser-row.indent:hover { background: var(--bg-hover); } .device-sync-browser-row.indent:hover { background: var(--bg-hover); }
.device-sync-browser-row.indent.selected { background: var(--accent-dim); } .device-sync-browser-row.indent.selected { background: var(--accent-dim); }
+27 -28
View File
@@ -3,17 +3,17 @@
max-width: 880px; max-width: 880px;
margin: 0 auto; margin: 0 auto;
padding: 28px 32px 48px; padding: 28px 32px 48px;
color: #e4e7f5; color: var(--text-primary);
background: #16182a; background: var(--bg-card);
border-radius: 12px; border-radius: 12px;
box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 8px 24px rgba(0,0,0,0.25); box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 8px 24px rgba(0,0,0,0.25);
border: 1px solid #2a2d44; border: 1px solid var(--border-subtle);
} }
.whats-new__header { .whats-new__header {
padding-bottom: 16px; padding-bottom: 16px;
margin-bottom: 20px; margin-bottom: 20px;
border-bottom: 1px solid #2a2d44; border-bottom: 1px solid var(--border-subtle);
} }
.whats-new__title-row { .whats-new__title-row {
display: flex; display: flex;
@@ -29,32 +29,32 @@
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #3a3f5a; border: 1px solid var(--border);
background: transparent; background: transparent;
color: #9aa0bd; color: var(--text-muted);
cursor: pointer; cursor: pointer;
flex-shrink: 0; flex-shrink: 0;
transition: background 0.15s, color 0.15s, border-color 0.15s; transition: background 0.15s, color 0.15s, border-color 0.15s;
} }
.whats-new__close:hover { .whats-new__close:hover {
background: rgba(255,255,255,0.06); background: var(--bg-hover);
color: #e4e7f5; color: var(--text-primary);
border-color: #4a5072; border-color: var(--border);
} }
.whats-new__icon { .whats-new__icon {
color: #8ec8ff; color: var(--accent);
flex-shrink: 0; flex-shrink: 0;
} }
.whats-new__title { .whats-new__title {
font-size: 22px; font-size: 22px;
font-weight: 700; font-weight: 700;
margin: 0; margin: 0;
color: #f1f3fb; color: var(--text-primary);
line-height: 1.2; line-height: 1.2;
} }
.whats-new__subtitle { .whats-new__subtitle {
font-size: 13px; font-size: 13px;
color: #9aa0bd; color: var(--text-muted);
margin-top: 2px; margin-top: 2px;
} }
.whats-new__date { .whats-new__date {
@@ -66,22 +66,22 @@
line-height: 1.65; line-height: 1.65;
} }
.whats-new__empty { .whats-new__empty {
color: #9aa0bd; color: var(--text-muted);
font-style: italic; font-style: italic;
} }
.whats-new-h3 { .whats-new-h3 {
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
color: #f1f3fb; color: var(--text-primary);
margin: 22px 0 10px; margin: 22px 0 10px;
padding-bottom: 6px; padding-bottom: 6px;
border-bottom: 1px solid #2a2d44; border-bottom: 1px solid var(--border-subtle);
} }
.whats-new-h4 { .whats-new-h4 {
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
color: #dfe3f6; color: var(--text-primary);
margin: 14px 0 6px; margin: 14px 0 6px;
} }
@@ -99,27 +99,27 @@
padding-left: 2px; padding-left: 2px;
} }
.whats-new-list li::marker { .whats-new-list li::marker {
color: #6b7192; color: var(--text-muted);
} }
.whats-new-quote { .whats-new-quote {
border-left: 3px solid #3a3f5a; border-left: 3px solid var(--border);
padding: 6px 12px; padding: 6px 12px;
margin: 10px 0; margin: 10px 0;
color: #b6bcd8; color: var(--text-secondary);
background: rgba(255,255,255,0.02); background: var(--bg-hover);
border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0;
} }
.whats-new-hr { .whats-new-hr {
border: none; border: none;
border-top: 1px solid #2a2d44; border-top: 1px solid var(--border-subtle);
margin: 18px 0; margin: 18px 0;
} }
.whats-new-code { .whats-new-code {
background: #262a3e; background: var(--bg-elevated);
color: #d4d7f0; color: var(--text-secondary);
padding: 1px 5px; padding: 1px 5px;
border-radius: 3px; border-radius: 3px;
font-family: var(--font-mono, ui-monospace, monospace); font-family: var(--font-mono, ui-monospace, monospace);
@@ -127,13 +127,12 @@
} }
.whats-new-link { .whats-new-link {
color: #8ec8ff; color: var(--accent);
text-decoration: none; text-decoration: none;
border-bottom: 1px dashed #4a5072; border-bottom: 1px dashed var(--border);
cursor: pointer; cursor: pointer;
} }
.whats-new-link:hover { .whats-new-link:hover {
color: #b6dbff; color: var(--accent);
border-bottom-color: #8ec8ff; border-bottom-color: var(--accent);
} }
@@ -1,7 +1,6 @@
/* ─ What's New — banner + page ──────────────────────────────────────────── /* ─ What's New — banner + page ────────────────────────────────────────────
Fixed neutral palette so it reads identically on every theme (light and The banner sits in the sidebar just above Now Playing; the page opens in
dark). The banner sits in the sidebar just above Now Playing; the page the main content area. Both follow the active theme. */
opens in the main content area. */
.sidebar-bottom-spacer { .sidebar-bottom-spacer {
margin-top: auto; margin-top: auto;
@@ -15,9 +14,9 @@
width: 100%; width: 100%;
padding: 8px 10px; padding: 8px 10px;
margin: 0 0 6px 0; margin: 0 0 6px 0;
background: #1f2233; background: var(--bg-card);
color: #dfe3f6; color: var(--text-secondary);
border: 1px solid #3a3f5a; border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
font: inherit; font: inherit;
font-size: 12px; font-size: 12px;
@@ -28,13 +27,13 @@
} }
.whats-new-banner:hover { .whats-new-banner:hover {
background: #272b40; background: var(--bg-hover);
border-color: #4a5072; border-color: var(--border);
} }
.whats-new-banner__icon { .whats-new-banner__icon {
flex-shrink: 0; flex-shrink: 0;
color: #8ec8ff; color: var(--accent);
} }
.whats-new-banner__text { .whats-new-banner__text {
@@ -47,12 +46,12 @@
.whats-new-banner__title { .whats-new-banner__title {
font-weight: 600; font-weight: 600;
color: #f1f3fb; color: var(--text-primary);
} }
.whats-new-banner__version { .whats-new-banner__version {
font-size: 10.5px; font-size: 10.5px;
color: #9aa0bd; color: var(--text-muted);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
@@ -64,12 +63,12 @@
width: 18px; width: 18px;
height: 18px; height: 18px;
border-radius: 4px; border-radius: 4px;
color: #9aa0bd; color: var(--text-muted);
cursor: pointer; cursor: pointer;
} }
.whats-new-banner__dismiss:hover { .whats-new-banner__dismiss:hover {
background: rgba(255, 255, 255, 0.08); background: var(--bg-hover);
color: #e4e7f5; color: var(--text-primary);
} }
.whats-new-banner--collapsed { .whats-new-banner--collapsed {
@@ -81,4 +80,3 @@
justify-content: center; justify-content: center;
margin: 0 auto 6px; margin: 0 auto 6px;
} }
+1 -1
View File
@@ -10,6 +10,6 @@
/* Opaque fill — `--accent-dim` is too transparent and disappears against /* Opaque fill — `--accent-dim` is too transparent and disappears against
light-toned hero backdrops when the cover art is white-dominant. */ light-toned hero backdrops when the cover art is white-dominant. */
background: var(--accent); background: var(--accent);
color: var(--ctp-crust); color: var(--text-on-accent);
} }
+2 -2
View File
@@ -3,8 +3,8 @@
width: 100%; width: 100%;
padding: var(--space-2) var(--space-3); padding: var(--space-2) var(--space-3);
padding-left: 36px; padding-left: 36px;
background: var(--ctp-base); background: var(--input-bg);
border: 1px solid var(--ctp-overlay0); border: 1px solid var(--input-border);
border-radius: var(--radius-md); border-radius: var(--radius-md);
color: var(--text-primary); color: var(--text-primary);
font-family: var(--font-sans); font-family: var(--font-sans);
+6 -6
View File
@@ -2,23 +2,23 @@
.star-rating { .star-rating {
display: flex; display: flex;
gap: 2px; gap: 2px;
color: var(--ctp-overlay1); color: var(--text-muted);
justify-content: center; justify-content: center;
width: 100%; width: 100%;
} }
.star-rating .star.filled { .star-rating .star.filled {
color: var(--ctp-yellow); color: var(--highlight);
} }
.star-rating .star:hover { .star-rating .star:hover {
color: var(--ctp-yellow); color: var(--highlight);
cursor: pointer; cursor: pointer;
} }
/* While clearing: no yellow hover until pointer leaves or next click */ /* While clearing: no highlight hover until pointer leaves or next click */
.star-rating--suppress-hover .star:hover { .star-rating--suppress-hover .star:hover {
color: var(--ctp-overlay1); color: var(--text-muted);
} }
.star-rating--disabled { .star-rating--disabled {
@@ -40,7 +40,7 @@
} }
.star-rating .star.star--locked:hover { .star-rating .star.star--locked:hover {
color: var(--ctp-overlay1); color: var(--text-muted);
cursor: default; cursor: default;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* ─── Utility Classes ─── */ /* ─── Utility Classes ─── */
.gradient-text { .gradient-text {
background: linear-gradient(135deg, var(--ctp-mauve), var(--ctp-blue)); background: linear-gradient(135deg, var(--accent), var(--accent-2));
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
background-clip: text; background-clip: text;