From 1026fa30c4b1f9d8c1d295204fd2f8398636e525 Mon Sep 17 00:00:00 2001 From: rucolastico Date: Tue, 6 Oct 2026 21:41:23 -0600 Subject: [PATCH 1/2] feat(ui): add Solarized Light and Dark themes Add Light and Dark variants of Ethan Schoonover's Solarized palette, built on the Tokyo Night theme structure so they cover the app bar, sidebar, tables, album grid, album/playlist details, login and the player. Text colors are one step stronger than canonical Solarized body text (base01 on light, base1 on dark) so table text keeps WCAG AA contrast. The Dark variant uses cyan instead of violet as the secondary accent, since violet is too dark on base03. Co-Authored-By: Claude Opus 5.5 Signed-off-by: rucolastico --- ui/src/themes/index.js | 4 + ui/src/themes/solarizedDark.css.js | 148 +++++++++++ ui/src/themes/solarizedDark.js | 373 ++++++++++++++++++++++++++++ ui/src/themes/solarizedLight.css.js | 123 +++++++++ ui/src/themes/solarizedLight.js | 373 ++++++++++++++++++++++++++++ 5 files changed, 1021 insertions(+) create mode 100644 ui/src/themes/solarizedDark.css.js create mode 100644 ui/src/themes/solarizedDark.js create mode 100644 ui/src/themes/solarizedLight.css.js create mode 100644 ui/src/themes/solarizedLight.js diff --git a/ui/src/themes/index.js b/ui/src/themes/index.js index 391a97f2c..285e579ae 100644 --- a/ui/src/themes/index.js +++ b/ui/src/themes/index.js @@ -25,6 +25,8 @@ import MoonbaseAlphaTheme from './moonbaseAlpha' import MoonbaseBravoTheme from './moonbaseBravo' import TokyoNightLightTheme from './tokyoNightLight' import TokyoNightTheme from './tokyoNight' +import SolarizedDarkTheme from './solarizedDark' +import SolarizedLightTheme from './solarizedLight' export default { // Classic default themes @@ -53,6 +55,8 @@ export default { RosePineDawnTheme, RosePineMoonTheme, RosePineTheme, + SolarizedDarkTheme, + SolarizedLightTheme, SpotifyTheme, SquiddiesGlassTheme, TokyoNightLightTheme, diff --git a/ui/src/themes/solarizedDark.css.js b/ui/src/themes/solarizedDark.css.js new file mode 100644 index 000000000..b357b8a0b --- /dev/null +++ b/ui/src/themes/solarizedDark.css.js @@ -0,0 +1,148 @@ +const stylesheet = ` + +.react-jinke-music-player-main svg:active, .react-jinke-music-player-main svg:hover { + color: #268bd2 +} + +.react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-handle, .react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-track { + background-color: #268bd2 +} + +.react-jinke-music-player-main ::-webkit-scrollbar-thumb { + background-color: #268bd2; +} + +.react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-handle:active { + box-shadow: 0 0 2px #268bd2 +} + +.react-jinke-music-player-main .audio-item.playing svg { + color: #268bd2 +} + +.react-jinke-music-player-main .audio-item.playing .player-singer { + color: #268bd2 !important +} + +.react-jinke-music-player-main .loading svg { + color: #268bd2 !important +} + +.react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-handle { + border: hidden; + box-shadow: rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px; +} + +.rc-slider-rail, .rc-slider-track { + height: 6px; +} + +.react-jinke-music-player-main .rc-slider-rail, +.react-jinke-music-player-mobile-progress .rc-slider-rail { + background-color: rgba(147, 161, 161, .2) !important +} + +.rc-slider { + padding: 3px 0; +} + +.sound-operation > div:nth-child(4) { + transform: translateX(-50%) translateY(5%) !important; +} + +.sound-operation { + padding: 4px 0; +} + +.react-jinke-music-player-main .music-player-panel { + background-color: #073642; + color: #93a1a1; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.25); +} + +.audio-lists-panel { + background-color: #073642; + bottom: 6.25rem; + box-shadow: rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px; +} + +.audio-lists-panel-content .audio-item.playing { + background-color: rgba(0, 0, 0, 0); +} + +.audio-lists-panel-content .audio-item:nth-child(2n+1) { + background-color: rgba(0, 0, 0, 0); +} + +.audio-lists-panel-content .audio-item:active, +.audio-lists-panel-content .audio-item:hover { + background-color: #0d4251; +} + +.audio-lists-panel-header { + border-bottom: 1px solid rgba(0, 0, 0, 0.25); + box-shadow: none; +} + +.react-jinke-music-player-main .music-player-panel .panel-content .player-content .audio-lists-btn { + background-color: rgba(0, 0, 0, 0); + box-shadow: 0 0 0 0; +} + +.audio-lists-panel-content .audio-item { + line-height: 32px; +} + +.react-jinke-music-player-main .music-player-panel .panel-content .img-content { + box-shadow: rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px; +} + +.react-jinke-music-player-main .music-player-lyric { + color: #93a1a1; + -webkit-text-stroke: 0.5px #002b36; + font-weight: bolder; +} + +.react-jinke-music-player-main .lyric-btn-active, .react-jinke-music-player-main .lyric-btn-active svg { + color: #268bd2 !important; +} + +.audio-lists-panel-content .audio-item.playing, .audio-lists-panel-content .audio-item.playing svg { + color: #268bd2 +} + +.audio-lists-panel-content .audio-item:active .group:not(.player-delete) svg, .audio-lists-panel-content .audio-item:hover .group:not(.player-delete) svg { + color: #268bd2 +} + +.audio-lists-panel-content .audio-item .player-icons { + scale: 75%; +} + +/* Mobile */ + +.react-jinke-music-player-mobile-cover { + border: none; + box-shadow: rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px; +} + +.react-jinke-music-player .music-player-controller { + border: none; + box-shadow: rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px; + color: #268bd2; +} + +.react-jinke-music-player .music-player-controller .music-player-controller-setting { + color: rgba(38, 139, 210, .3); +} + +.react-jinke-music-player-mobile-progress .rc-slider-handle, .react-jinke-music-player-mobile-progress .rc-slider-track { + background-color: #268bd2; +} + +.react-jinke-music-player-mobile-progress .rc-slider-handle { + border: none; +} +` + +export default stylesheet diff --git a/ui/src/themes/solarizedDark.js b/ui/src/themes/solarizedDark.js new file mode 100644 index 000000000..7690c266d --- /dev/null +++ b/ui/src/themes/solarizedDark.js @@ -0,0 +1,373 @@ +import stylesheet from './solarizedDark.css.js' + +// Solarized palette by Ethan Schoonover: https://ethanschoonover.com/solarized/ +const background = '#002b36' // base03 +const surface = '#073642' // base02 +const currentLine = '#0d4251' // base02, slightly lighter for hover and headers +const foreground = '#93a1a1' // base1 +const comment = '#839496' // base0 +const blue = '#268bd2' +const link = '#2aa198' // cyan +const accent = '#2aa198' // cyan, since violet is too dark on base03 +const red = '#dc322f' + +// For Album, Playlist play button +const musicListActions = { + alignItems: 'center', + '@global': { + 'button:first-child:not(:only-child)': { + '@media screen and (max-width: 720px)': { + transform: 'scale(1.5)', + margin: '1rem', + '&:hover': { + transform: 'scale(1.6) !important', + }, + }, + transform: 'scale(2)', + margin: '1.5rem', + minWidth: 0, + padding: 5, + transition: 'transform .3s ease', + backgroundColor: `${blue} !important`, + color: background, + borderRadius: 500, + border: 0, + '&:hover': { + transform: 'scale(2.1)', + backgroundColor: `${blue} !important`, + border: 0, + }, + }, + 'button:only-child': { + margin: '1.5rem', + }, + 'button:first-child>span:first-child': { + padding: 0, + }, + 'button:first-child>span:first-child>span': { + display: 'none', + }, + 'button>span:first-child>span, button:not(:first-child)>span:first-child>svg': + { + color: foreground, + }, + }, +} + +export default { + themeName: 'Solarized Dark', + palette: { + primary: { + main: blue, + }, + secondary: { + main: accent, + contrastText: foreground, + }, + error: { + main: red, + }, + type: 'dark', + background: { + default: background, + paper: surface, + }, + }, + overrides: { + MuiPaper: { + root: { + color: foreground, + backgroundColor: surface, + }, + }, + MuiAppBar: { + positionFixed: { + backgroundColor: `${surface} !important`, + boxShadow: + 'rgba(15, 17, 21, 0.25) 0px 4px 6px, rgba(15, 17, 21, 0.1) 0px 5px 7px', + }, + }, + MuiDrawer: { + root: { + background: background, + }, + }, + MuiButton: { + textPrimary: { + color: blue, + }, + textSecondary: { + color: foreground, + }, + }, + MuiIconButton: { + root: { + color: foreground, + }, + }, + MuiChip: { + root: { + backgroundColor: currentLine, + }, + }, + MuiFormGroup: { + root: { + color: foreground, + }, + }, + MuiFormLabel: { + root: { + color: comment, + '&$focused': { + color: blue, + }, + }, + }, + MuiFormHelperText: { + error: { + color: red, + }, + }, + MuiToolbar: { + root: { + backgroundColor: `${surface} !important`, + }, + }, + MuiOutlinedInput: { + root: { + '& $notchedOutline': { + borderColor: currentLine, + }, + '&:hover $notchedOutline': { + borderColor: comment, + }, + '&$focused $notchedOutline': { + borderColor: blue, + }, + }, + }, + MuiFilledInput: { + root: { + backgroundColor: currentLine, + '&:hover': { + backgroundColor: comment, + }, + '&$focused': { + backgroundColor: currentLine, + }, + }, + }, + MuiTableRow: { + root: { + transition: 'background-color .3s ease', + '&:hover': { + backgroundColor: `${currentLine} !important`, + }, + }, + }, + MuiTableHead: { + root: { + color: foreground, + background: surface, + }, + }, + MuiTableCell: { + root: { + color: foreground, + background: `${surface} !important`, + borderBottom: `1px solid ${currentLine}`, + }, + head: { + color: `${foreground} !important`, + background: `${currentLine} !important`, + }, + body: { + color: `${foreground} !important`, + }, + }, + NDAlbumGridView: { + albumName: { + marginTop: '0.5rem', + fontWeight: 700, + color: foreground, + }, + albumSubtitle: { + color: comment, + }, + albumContainer: { + backgroundColor: surface, + borderRadius: '8px', + padding: '.75rem', + transition: 'background-color .3s ease', + '&:hover': { + backgroundColor: currentLine, + }, + }, + albumPlayButton: { + backgroundColor: blue, + borderRadius: '50%', + boxShadow: '0 8px 8px rgb(0 0 0 / 30%)', + padding: '0.35rem', + transition: 'padding .3s ease', + '&:hover': { + background: `${blue} !important`, + padding: '0.45rem', + }, + }, + }, + NDPlaylistDetails: { + container: { + background: `linear-gradient(${currentLine}, transparent)`, + borderRadius: 0, + paddingTop: '2.5rem !important', + boxShadow: 'none', + }, + title: { + fontWeight: 700, + color: foreground, + }, + details: { + fontSize: '.875rem', + color: comment, + }, + }, + NDAlbumDetails: { + root: { + background: `linear-gradient(${currentLine}, transparent)`, + borderRadius: 0, + boxShadow: 'none', + }, + cardContents: { + alignItems: 'center', + paddingTop: '1.5rem', + }, + recordName: { + fontWeight: 700, + color: foreground, + }, + recordArtist: { + fontSize: '.875rem', + fontWeight: 700, + color: accent, + }, + recordMeta: { + fontSize: '.875rem', + color: comment, + }, + }, + NDCollapsibleComment: { + commentBlock: { + fontSize: '.875rem', + color: comment, + }, + }, + NDAlbumShow: { + albumActions: musicListActions, + }, + NDPlaylistShow: { + playlistActions: musicListActions, + }, + NDAudioPlayer: { + audioTitle: { + color: foreground, + fontSize: '0.875rem', + }, + songTitle: { + fontWeight: 400, + }, + songInfo: { + fontSize: '0.675rem', + color: comment, + }, + }, + NDLogin: { + systemNameLink: { + color: blue, + }, + welcome: { + color: foreground, + }, + card: { + minWidth: 300, + background: surface, + }, + button: { + boxShadow: '3px 3px 5px #00212b', + }, + }, + NDMobileArtistDetails: { + bgContainer: { + background: `linear-gradient(to bottom, rgba(0 43 54 / 72%), ${background})!important`, + }, + }, + RaLayout: { + content: { + padding: '0 !important', + background: background, + }, + root: { + backgroundColor: background, + }, + }, + RaList: { + content: { + backgroundColor: background, + }, + }, + RaListToolbar: { + toolbar: { + backgroundColor: background, + padding: '0 .55rem !important', + }, + }, + RaSidebar: { + fixed: { + backgroundColor: background, + }, + drawerPaper: { + backgroundColor: `${background} !important`, + }, + }, + RaMenuItemLink: { + root: { + color: foreground, + '&[aria-current="page"]': { + color: `${blue} !important`, + }, + '&[aria-current="page"] .MuiListItemIcon-root': { + color: `${blue} !important`, + }, + }, + active: { + color: `${blue} !important`, + '& .MuiListItemIcon-root': { + color: `${blue} !important`, + }, + }, + }, + RaLink: { + link: { + color: link, + }, + }, + RaButton: { + button: { + margin: '0 5px 0 5px', + }, + }, + RaPaginationActions: { + currentPageButton: { + border: `2px solid ${blue}`, + }, + button: { + backgroundColor: currentLine, + minWidth: 48, + margin: '0 4px', + }, + }, + }, + player: { + theme: 'dark', + stylesheet, + }, +} diff --git a/ui/src/themes/solarizedLight.css.js b/ui/src/themes/solarizedLight.css.js new file mode 100644 index 000000000..c6a599562 --- /dev/null +++ b/ui/src/themes/solarizedLight.css.js @@ -0,0 +1,123 @@ +const stylesheet = ` +.react-jinke-music-player-main.light-theme .loading svg { + color: #268bd2; + font-size: 24px +} + +.react-jinke-music-player-mobile-play-model-tip { + background-color: #268bd2; +} + +.react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-handle, .react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-track { + background-color: #268bd2 +} + +.react-jinke-music-player-main .music-player-panel .panel-content .rc-slider-handle:active { + box-shadow: 0 0 2px #268bd2 +} + +.react-jinke-music-player-main.light-theme .audio-item.playing svg { + color: #268bd2 +} + +.react-jinke-music-player-main.light-theme .audio-item.playing .player-singer { + color: #268bd2 !important +} + +.audio-lists-panel-content .audio-item.playing, .audio-lists-panel-content .audio-item.playing svg { + color: #268bd2 +} + +.audio-lists-panel-content .audio-item:active .group:not(.player-delete) svg, .audio-lists-panel-content .audio-item:hover .group:not(.player-delete) svg { + color: #268bd2 +} + +.react-jinke-music-player-main.light-theme ::-webkit-scrollbar-thumb { + background-color: #268bd2; +} + +.react-jinke-music-player-main.light-theme svg { + color: #586e75 +} + +.react-jinke-music-player-main.light-theme svg:active, .react-jinke-music-player-main.light-theme svg:hover { + color: #268bd2 +} + +.react-jinke-music-player-main.light-theme .rc-slider-rail { + background-color: rgba(88, 110, 117, .12) !important +} + +.react-jinke-music-player-main.light-theme .music-player-controller { + background-color: #fdf6e3; + border-color: #fdf6e3 +} + +.react-jinke-music-player-main.light-theme .music-player-panel { + background-color: #fdf6e3; + box-shadow: 0 1px 2px 0 rgba(0, 43, 54, .05); + color: #586e75 +} + +.react-jinke-music-player-main.light-theme .music-player-panel .img-content { + box-shadow: 0 0 10px #e4ddc6 +} + +.react-jinke-music-player-main.light-theme .music-player-panel .progress-load-bar { + background-color: rgba(88, 110, 117, .08) !important +} + +.react-jinke-music-player-main.light-theme .rc-switch { + color: #fdf6e3 +} + +.react-jinke-music-player-main.light-theme .rc-switch:after { + background-color: #fdf6e3 +} + +.react-jinke-music-player-main.light-theme .rc-switch-checked { + background-color: #268bd2 !important; + border: 1px solid #268bd2 +} + +.react-jinke-music-player-main.light-theme .rc-switch-inner { + color: #fdf6e3 +} + +.react-jinke-music-player-main.light-theme .audio-lists-btn { + background-color: #eee8d5 !important +} + +.react-jinke-music-player-main.light-theme .audio-lists-btn:active, .react-jinke-music-player-main.light-theme .audio-lists-btn:hover { + background-color: #e4ddc6; + color: #586e75 +} + +.react-jinke-music-player-main.light-theme .audio-lists-btn > .group:hover, .react-jinke-music-player-main.light-theme .audio-lists-btn > .group:hover > svg { + color: #268bd2 +} + +.react-jinke-music-player-main.light-theme .audio-lists-panel { + background-color: #fdf6e3; + box-shadow: 0 0 2px #e4ddc6; + color: #586e75 +} + +.react-jinke-music-player-main.light-theme .audio-lists-panel .audio-item { + background-color: #fdf6e3 +} + +.react-jinke-music-player-main.light-theme .audio-lists-panel .audio-item:nth-child(odd) { + background-color: #f5efdc !important +} + +.react-jinke-music-player-main.light-theme .audio-lists-panel .audio-item.playing { + background-color: #e4ddc6 !important +} + +.react-jinke-music-player-main.light-theme .audio-lists-panel .audio-item.playing, .react-jinke-music-player-main.light-theme .audio-lists-panel .audio-item.playing svg { + color: #268bd2 !important +} +` + +export default stylesheet diff --git a/ui/src/themes/solarizedLight.js b/ui/src/themes/solarizedLight.js new file mode 100644 index 000000000..253d7d68f --- /dev/null +++ b/ui/src/themes/solarizedLight.js @@ -0,0 +1,373 @@ +import stylesheet from './solarizedLight.css.js' + +// Solarized palette by Ethan Schoonover: https://ethanschoonover.com/solarized/ +const background = '#eee8d5' // base2 +const surface = '#fdf6e3' // base3 +const currentLine = '#e4ddc6' // base2, slightly darker for hover and headers +const foreground = '#586e75' // base01 +const comment = '#657b83' // base00 +const blue = '#268bd2' +const link = '#268bd2' // blue +const accent = '#6c71c4' // violet +const red = '#dc322f' + +// For Album, Playlist play button +const musicListActions = { + alignItems: 'center', + '@global': { + 'button:first-child:not(:only-child)': { + '@media screen and (max-width: 720px)': { + transform: 'scale(1.5)', + margin: '1rem', + '&:hover': { + transform: 'scale(1.6) !important', + }, + }, + transform: 'scale(2)', + margin: '1.5rem', + minWidth: 0, + padding: 5, + transition: 'transform .3s ease', + backgroundColor: `${blue} !important`, + color: background, + borderRadius: 500, + border: 0, + '&:hover': { + transform: 'scale(2.1)', + backgroundColor: `${blue} !important`, + border: 0, + }, + }, + 'button:only-child': { + margin: '1.5rem', + }, + 'button:first-child>span:first-child': { + padding: 0, + }, + 'button:first-child>span:first-child>span': { + display: 'none', + }, + 'button>span:first-child>span, button:not(:first-child)>span:first-child>svg': + { + color: foreground, + }, + }, +} + +export default { + themeName: 'Solarized Light', + palette: { + primary: { + main: blue, + }, + secondary: { + main: accent, + contrastText: foreground, + }, + error: { + main: red, + }, + type: 'light', + background: { + default: background, + paper: surface, + }, + }, + overrides: { + MuiPaper: { + root: { + color: foreground, + backgroundColor: surface, + }, + }, + MuiAppBar: { + positionFixed: { + backgroundColor: `${surface} !important`, + boxShadow: + 'rgba(0, 43, 54, 0.12) 0px 4px 6px, rgba(0, 43, 54, 0.06) 0px 5px 7px', + }, + }, + MuiDrawer: { + root: { + background: background, + }, + }, + MuiButton: { + textPrimary: { + color: blue, + }, + textSecondary: { + color: foreground, + }, + }, + MuiIconButton: { + root: { + color: foreground, + }, + }, + MuiChip: { + root: { + backgroundColor: currentLine, + }, + }, + MuiFormGroup: { + root: { + color: foreground, + }, + }, + MuiFormLabel: { + root: { + color: comment, + '&$focused': { + color: blue, + }, + }, + }, + MuiFormHelperText: { + error: { + color: red, + }, + }, + MuiToolbar: { + root: { + backgroundColor: `${surface} !important`, + }, + }, + MuiOutlinedInput: { + root: { + '& $notchedOutline': { + borderColor: currentLine, + }, + '&:hover $notchedOutline': { + borderColor: comment, + }, + '&$focused $notchedOutline': { + borderColor: blue, + }, + }, + }, + MuiFilledInput: { + root: { + backgroundColor: currentLine, + '&:hover': { + backgroundColor: comment, + }, + '&$focused': { + backgroundColor: currentLine, + }, + }, + }, + MuiTableRow: { + root: { + transition: 'background-color .3s ease', + '&:hover': { + backgroundColor: `${currentLine} !important`, + }, + }, + }, + MuiTableHead: { + root: { + color: foreground, + background: surface, + }, + }, + MuiTableCell: { + root: { + color: foreground, + background: `${surface} !important`, + borderBottom: `1px solid ${currentLine}`, + }, + head: { + color: `${foreground} !important`, + background: `${currentLine} !important`, + }, + body: { + color: `${foreground} !important`, + }, + }, + NDAlbumGridView: { + albumName: { + marginTop: '0.5rem', + fontWeight: 700, + color: foreground, + }, + albumSubtitle: { + color: comment, + }, + albumContainer: { + backgroundColor: surface, + borderRadius: '8px', + padding: '.75rem', + transition: 'background-color .3s ease', + '&:hover': { + backgroundColor: currentLine, + }, + }, + albumPlayButton: { + backgroundColor: blue, + borderRadius: '50%', + boxShadow: '0 8px 8px rgb(0 0 0 / 20%)', + padding: '0.35rem', + transition: 'padding .3s ease', + '&:hover': { + background: `${blue} !important`, + padding: '0.45rem', + }, + }, + }, + NDPlaylistDetails: { + container: { + background: `linear-gradient(${currentLine}, transparent)`, + borderRadius: 0, + paddingTop: '2.5rem !important', + boxShadow: 'none', + }, + title: { + fontWeight: 700, + color: foreground, + }, + details: { + fontSize: '.875rem', + color: comment, + }, + }, + NDAlbumDetails: { + root: { + background: `linear-gradient(${currentLine}, transparent)`, + borderRadius: 0, + boxShadow: 'none', + }, + cardContents: { + alignItems: 'center', + paddingTop: '1.5rem', + }, + recordName: { + fontWeight: 700, + color: foreground, + }, + recordArtist: { + fontSize: '.875rem', + fontWeight: 700, + color: accent, + }, + recordMeta: { + fontSize: '.875rem', + color: comment, + }, + }, + NDCollapsibleComment: { + commentBlock: { + fontSize: '.875rem', + color: comment, + }, + }, + NDAlbumShow: { + albumActions: musicListActions, + }, + NDPlaylistShow: { + playlistActions: musicListActions, + }, + NDAudioPlayer: { + audioTitle: { + color: foreground, + fontSize: '0.875rem', + }, + songTitle: { + fontWeight: 400, + }, + songInfo: { + fontSize: '0.675rem', + color: comment, + }, + }, + NDLogin: { + systemNameLink: { + color: blue, + }, + welcome: { + color: foreground, + }, + card: { + minWidth: 300, + background: surface, + }, + button: { + boxShadow: '3px 3px 5px #93a1a1', + }, + }, + NDMobileArtistDetails: { + bgContainer: { + background: `linear-gradient(to bottom, rgba(238 232 213 / 72%), ${background})!important`, + }, + }, + RaLayout: { + content: { + padding: '0 !important', + background: background, + }, + root: { + backgroundColor: background, + }, + }, + RaList: { + content: { + backgroundColor: background, + }, + }, + RaListToolbar: { + toolbar: { + backgroundColor: background, + padding: '0 .55rem !important', + }, + }, + RaSidebar: { + fixed: { + backgroundColor: background, + }, + drawerPaper: { + backgroundColor: `${background} !important`, + }, + }, + RaMenuItemLink: { + root: { + color: foreground, + '&[aria-current="page"]': { + color: `${blue} !important`, + }, + '&[aria-current="page"] .MuiListItemIcon-root': { + color: `${blue} !important`, + }, + }, + active: { + color: `${blue} !important`, + '& .MuiListItemIcon-root': { + color: `${blue} !important`, + }, + }, + }, + RaLink: { + link: { + color: link, + }, + }, + RaButton: { + button: { + margin: '0 5px 0 5px', + }, + }, + RaPaginationActions: { + currentPageButton: { + border: `2px solid ${blue}`, + }, + button: { + backgroundColor: currentLine, + minWidth: 48, + margin: '0 4px', + }, + }, + }, + player: { + theme: 'light', + stylesheet, + }, +} From 0c99921e81db6a3f0e2fe10be979c3098df229ef Mon Sep 17 00:00:00 2001 From: rucolastico Date: Tue, 6 Oct 2026 22:01:10 -0600 Subject: [PATCH 2/2] fix(ui): improve Solarized contrast and table row hover Address review feedback on the Solarized themes: - Use text-specific shades for links and the login system name, keeping them above 4.5:1 on table surfaces and hover rows (darkened blue on Light, lightened cyan on Dark). Canonical blue stays for controls. - Darken the Light theme's violet accent, which is also used as text (album artist), to keep it above 4.5:1. - Give secondary buttons readable text: base3 on violet (Light) and base03 on cyan (Dark), instead of the body text color. - Apply the row hover color to table cells, whose opaque background was hiding the row hover. Co-Authored-By: Claude Opus 5.5 Signed-off-by: rucolastico --- ui/src/themes/solarizedDark.js | 10 +++++++--- ui/src/themes/solarizedLight.js | 12 ++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ui/src/themes/solarizedDark.js b/ui/src/themes/solarizedDark.js index 7690c266d..ef703e75c 100644 --- a/ui/src/themes/solarizedDark.js +++ b/ui/src/themes/solarizedDark.js @@ -7,7 +7,7 @@ const currentLine = '#0d4251' // base02, slightly lighter for hover and headers const foreground = '#93a1a1' // base1 const comment = '#839496' // base0 const blue = '#268bd2' -const link = '#2aa198' // cyan +const link = '#57b5ae' // cyan, lightened to keep text links above 4.5:1 const accent = '#2aa198' // cyan, since violet is too dark on base03 const red = '#dc322f' @@ -62,7 +62,7 @@ export default { }, secondary: { main: accent, - contrastText: foreground, + contrastText: background, }, error: { main: red, @@ -163,6 +163,10 @@ export default { '&:hover': { backgroundColor: `${currentLine} !important`, }, + // Cells have an opaque background, so the hover must be applied to them too + '&:hover > .MuiTableCell-root': { + background: `${currentLine} !important`, + }, }, }, MuiTableHead: { @@ -282,7 +286,7 @@ export default { }, NDLogin: { systemNameLink: { - color: blue, + color: link, }, welcome: { color: foreground, diff --git a/ui/src/themes/solarizedLight.js b/ui/src/themes/solarizedLight.js index 253d7d68f..a2a906b54 100644 --- a/ui/src/themes/solarizedLight.js +++ b/ui/src/themes/solarizedLight.js @@ -7,8 +7,8 @@ const currentLine = '#e4ddc6' // base2, slightly darker for hover and headers const foreground = '#586e75' // base01 const comment = '#657b83' // base00 const blue = '#268bd2' -const link = '#268bd2' // blue -const accent = '#6c71c4' // violet +const link = '#1c6599' // blue, darkened to keep text links above 4.5:1 +const accent = '#5d61a9' // violet, darkened to keep text above 4.5:1 const red = '#dc322f' // For Album, Playlist play button @@ -62,7 +62,7 @@ export default { }, secondary: { main: accent, - contrastText: foreground, + contrastText: surface, }, error: { main: red, @@ -163,6 +163,10 @@ export default { '&:hover': { backgroundColor: `${currentLine} !important`, }, + // Cells have an opaque background, so the hover must be applied to them too + '&:hover > .MuiTableCell-root': { + background: `${currentLine} !important`, + }, }, }, MuiTableHead: { @@ -282,7 +286,7 @@ export default { }, NDLogin: { systemNameLink: { - color: blue, + color: link, }, welcome: { color: foreground,