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
**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.
* 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: 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)',
'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"
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>
);
}
@@ -351,7 +351,7 @@ export default function Composers() {
<button
className={`btn btn-surface ${viewMode === 'grid' ? 'btn-sort-active' : ''}`}
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')}
>
<LayoutGrid size={20} />
@@ -359,7 +359,7 @@ export default function Composers() {
<button
className={`btn btn-surface ${viewMode === 'list' ? 'btn-sort-active' : ''}`}
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')}
>
<List size={20} />
+2 -2
View File
@@ -423,7 +423,7 @@
gap: 8px;
height: 52px;
padding: 0 14px;
background: var(--bg-secondary);
background: var(--bg-card);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
@@ -792,7 +792,7 @@
.device-sync-browser-row:hover { background: var(--bg-hover); }
.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.selected { background: var(--accent-dim); }
+27 -28
View File
@@ -3,17 +3,17 @@
max-width: 880px;
margin: 0 auto;
padding: 28px 32px 48px;
color: #e4e7f5;
background: #16182a;
color: var(--text-primary);
background: var(--bg-card);
border-radius: 12px;
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 {
padding-bottom: 16px;
margin-bottom: 20px;
border-bottom: 1px solid #2a2d44;
border-bottom: 1px solid var(--border-subtle);
}
.whats-new__title-row {
display: flex;
@@ -29,32 +29,32 @@
width: 32px;
height: 32px;
border-radius: 6px;
border: 1px solid #3a3f5a;
border: 1px solid var(--border);
background: transparent;
color: #9aa0bd;
color: var(--text-muted);
cursor: pointer;
flex-shrink: 0;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.whats-new__close:hover {
background: rgba(255,255,255,0.06);
color: #e4e7f5;
border-color: #4a5072;
background: var(--bg-hover);
color: var(--text-primary);
border-color: var(--border);
}
.whats-new__icon {
color: #8ec8ff;
color: var(--accent);
flex-shrink: 0;
}
.whats-new__title {
font-size: 22px;
font-weight: 700;
margin: 0;
color: #f1f3fb;
color: var(--text-primary);
line-height: 1.2;
}
.whats-new__subtitle {
font-size: 13px;
color: #9aa0bd;
color: var(--text-muted);
margin-top: 2px;
}
.whats-new__date {
@@ -66,22 +66,22 @@
line-height: 1.65;
}
.whats-new__empty {
color: #9aa0bd;
color: var(--text-muted);
font-style: italic;
}
.whats-new-h3 {
font-size: 16px;
font-weight: 700;
color: #f1f3fb;
color: var(--text-primary);
margin: 22px 0 10px;
padding-bottom: 6px;
border-bottom: 1px solid #2a2d44;
border-bottom: 1px solid var(--border-subtle);
}
.whats-new-h4 {
font-size: 14px;
font-weight: 700;
color: #dfe3f6;
color: var(--text-primary);
margin: 14px 0 6px;
}
@@ -99,27 +99,27 @@
padding-left: 2px;
}
.whats-new-list li::marker {
color: #6b7192;
color: var(--text-muted);
}
.whats-new-quote {
border-left: 3px solid #3a3f5a;
border-left: 3px solid var(--border);
padding: 6px 12px;
margin: 10px 0;
color: #b6bcd8;
background: rgba(255,255,255,0.02);
color: var(--text-secondary);
background: var(--bg-hover);
border-radius: 0 4px 4px 0;
}
.whats-new-hr {
border: none;
border-top: 1px solid #2a2d44;
border-top: 1px solid var(--border-subtle);
margin: 18px 0;
}
.whats-new-code {
background: #262a3e;
color: #d4d7f0;
background: var(--bg-elevated);
color: var(--text-secondary);
padding: 1px 5px;
border-radius: 3px;
font-family: var(--font-mono, ui-monospace, monospace);
@@ -127,13 +127,12 @@
}
.whats-new-link {
color: #8ec8ff;
color: var(--accent);
text-decoration: none;
border-bottom: 1px dashed #4a5072;
border-bottom: 1px dashed var(--border);
cursor: pointer;
}
.whats-new-link:hover {
color: #b6dbff;
border-bottom-color: #8ec8ff;
color: var(--accent);
border-bottom-color: var(--accent);
}
@@ -1,7 +1,6 @@
/* What's New banner + page
Fixed neutral palette so it reads identically on every theme (light and
dark). The banner sits in the sidebar just above Now Playing; the page
opens in the main content area. */
The banner sits in the sidebar just above Now Playing; the page opens in
the main content area. Both follow the active theme. */
.sidebar-bottom-spacer {
margin-top: auto;
@@ -15,9 +14,9 @@
width: 100%;
padding: 8px 10px;
margin: 0 0 6px 0;
background: #1f2233;
color: #dfe3f6;
border: 1px solid #3a3f5a;
background: var(--bg-card);
color: var(--text-secondary);
border: 1px solid var(--border);
border-radius: 8px;
font: inherit;
font-size: 12px;
@@ -28,13 +27,13 @@
}
.whats-new-banner:hover {
background: #272b40;
border-color: #4a5072;
background: var(--bg-hover);
border-color: var(--border);
}
.whats-new-banner__icon {
flex-shrink: 0;
color: #8ec8ff;
color: var(--accent);
}
.whats-new-banner__text {
@@ -47,12 +46,12 @@
.whats-new-banner__title {
font-weight: 600;
color: #f1f3fb;
color: var(--text-primary);
}
.whats-new-banner__version {
font-size: 10.5px;
color: #9aa0bd;
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
@@ -64,12 +63,12 @@
width: 18px;
height: 18px;
border-radius: 4px;
color: #9aa0bd;
color: var(--text-muted);
cursor: pointer;
}
.whats-new-banner__dismiss:hover {
background: rgba(255, 255, 255, 0.08);
color: #e4e7f5;
background: var(--bg-hover);
color: var(--text-primary);
}
.whats-new-banner--collapsed {
@@ -81,4 +80,3 @@
justify-content: center;
margin: 0 auto 6px;
}
+1 -1
View File
@@ -10,6 +10,6 @@
/* Opaque fill `--accent-dim` is too transparent and disappears against
light-toned hero backdrops when the cover art is white-dominant. */
background: var(--accent);
color: var(--ctp-crust);
color: var(--text-on-accent);
}
+2 -2
View File
@@ -3,8 +3,8 @@
width: 100%;
padding: var(--space-2) var(--space-3);
padding-left: 36px;
background: var(--ctp-base);
border: 1px solid var(--ctp-overlay0);
background: var(--input-bg);
border: 1px solid var(--input-border);
border-radius: var(--radius-md);
color: var(--text-primary);
font-family: var(--font-sans);
+6 -6
View File
@@ -2,23 +2,23 @@
.star-rating {
display: flex;
gap: 2px;
color: var(--ctp-overlay1);
color: var(--text-muted);
justify-content: center;
width: 100%;
}
.star-rating .star.filled {
color: var(--ctp-yellow);
color: var(--highlight);
}
.star-rating .star:hover {
color: var(--ctp-yellow);
color: var(--highlight);
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 {
color: var(--ctp-overlay1);
color: var(--text-muted);
}
.star-rating--disabled {
@@ -40,7 +40,7 @@
}
.star-rating .star.star--locked:hover {
color: var(--ctp-overlay1);
color: var(--text-muted);
cursor: default;
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* ─── Utility Classes ─── */
.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-text-fill-color: transparent;
background-clip: text;