UI cleanup, visual and keyboard navigation

This commit is contained in:
2026-09-11 13:32:07 +02:00
parent df89acd9a8
commit fbf03a9847
13 changed files with 445 additions and 105 deletions

70
web/src/lib/theme.ts Normal file
View File

@@ -0,0 +1,70 @@
/** The browse screen's shelf/row look, in one place: per-group colors and identity,
* and the feature toggles that have gone back and forth while this design was under
* review. Flip a toggle here rather than hunting through BrowseView.tsx/covers.ts.
*
* `SHOW_ROW_TITLES` and `SHOW_ROW_ICONS` both `true` reproduces the original shelf
* header - an icon badge plus label button above each row's tiles. */
import type { Group } from "./search";
// ---------------------------------------------------------------- toggles --
/** Tint each shelf panel and its rows with the group's hue below. */
export const SHOW_ROW_TINT = false;
/** Show each shelf's title text ("Musik", "Hörbücher", "Podcasts") above its tiles. */
export const SHOW_ROW_TITLES = true;
/** Show the small icon badge (🎵/📖/🎙️) beside the title. Stands alone fine with
* `SHOW_ROW_TITLES` off (an icon-only header), but the original look needs both. */
export const SHOW_ROW_ICONS = true;
/** Vertical gap between shelves on the root browse screen, in px. */
export const ROW_SPACING = 25;
// ------------------------------------------------------------------ colors --
/** Per-group hue (oklch degrees). The play view's ambient water uses the same
* family (`ambienceBaseFor` in `lib/ambience.ts`), so a tinted shelf already hints
* at the mood its albums play into - and so does the book/music card tint in
* `lib/covers.ts`, which reads `GROUP_HUE.audiobooks`/`GROUP_HUE.music` too. */
export const GROUP_HUE: Record<Group, number> = {
music: 210, // today's sea - Dolphin Beats' baseline hue
audiobooks: 55, // warm amber - richer than the old shared hue (80)'s dull brown
podcasts: 300, // the one otherwise-unused family
};
export const GROUP_ICON: Record<Group, string> = {
music: "🎵",
audiobooks: "📖",
podcasts: "🎙️",
};
export const GROUP_LABEL: Record<Group, string> = {
music: "Musik",
audiobooks: "Hörbücher",
podcasts: "Podcasts",
};
/** A shelf/row's frosted background and border, tinted with its group's hue - or,
* with `SHOW_ROW_TINT` off, `{}` so `.glass-panel`'s own neutral CSS shows through. */
export function glassTint(hue: number): React.CSSProperties {
if (!SHOW_ROW_TINT) return {};
return {
background: `linear-gradient(160deg, oklch(55% 0.1 ${hue} / .32), oklch(30% 0.06 ${hue} / .14))`,
borderColor: `oklch(75% 0.09 ${hue} / .4)`,
};
}
/** One list row's background, tinted with its group's hue and brighter/more opaque
* while it's the currently-playing row - or, with `SHOW_ROW_TINT` off, the same
* neutral highlight the row used before tinting existed. */
export function rowTint(hue: number, highlighted: boolean): React.CSSProperties {
if (!SHOW_ROW_TINT) {
return { background: `oklch(97% 0.01 210 / ${highlighted ? ".22" : ".10"})` };
}
return {
background: `oklch(${highlighted ? "62% 0.12" : "42% 0.08"} ${hue} / ${highlighted ? ".34" : ".2"})`,
borderColor: `oklch(78% 0.09 ${hue} / ${highlighted ? ".5" : ".28"})`,
};
}