)}
@@ -346,8 +313,7 @@ export default function GlassTuner() {
)}
-
- >
+
);
}
diff --git a/frontend/src/components/SettingsPanel.tsx b/frontend/src/components/SettingsPanel.tsx
index 0399ea3..6675db0 100644
--- a/frontend/src/components/SettingsPanel.tsx
+++ b/frontend/src/components/SettingsPanel.tsx
@@ -158,6 +158,17 @@ function Appearance({ prefs }: { prefs: PrefsController }) {
onChange={(v) => setTheme({ ...theme, mode: v ? "dark" : "light" })}
/>
+
+ setTheme({ ...theme, bgImage: v })}
+ />
+ ): how strongly the faded --bg overlay hides the
- blurred thumbnail — higher = fainter image. Only in effect while the backdrop is toggled on. */
- --feedbg-fade: 86%;
+ /* Per-scheme background image dim (see frontend/public/backdrops/): how strongly the faded --bg
+ overlay hides the image — higher = fainter. Only in effect while data-backdrop="on". */
+ --bg-fade: 60%;
}
-/* ===== Rich-backdrop scope: surfaces that float over real imagery (Plex art hero/detail, and
- later channel-page banners) want MORE translucency so the picture shows through — the "glass
- over content" look. Put `.glass-media` on such a page root; every .glass* inside inherits these
- (UAT-tuned 2026-07-12). Global chrome stays solid via the :root defaults above. ===== */
+/* ===== "Glass over image" — the translucent tier (UAT-tuned 2026-07-12). Applies whenever a
+ real image sits behind the surfaces: app-wide when the per-scheme background image is on
+ (`html[data-backdrop="on"]`), and on the Plex art-backed detail views (`.glass-media`, which
+ have their own art backdrop even when the global image is off). When there's nothing behind the
+ glass (image off / light theme / perf), the :root defaults keep it solid & readable. ===== */
+html[data-backdrop="on"],
.glass-media {
--glass-blur: 10px;
--glass-saturate: 1.6;
@@ -44,6 +46,24 @@
--glass-scrim: 30%;
}
+/* Per-scheme background image, painted on (the app root is transparent so it shows through
+ the whole app and the glass refracts it). A faded --bg overlay keeps it subtle; `fixed` so it
+ doesn't scroll. Only when the backdrop is on (dark theme + the "Background image" setting). */
+html[data-backdrop="on"][data-scheme="midnight"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/midnight.svg"); }
+html[data-backdrop="on"][data-scheme="forest"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/forest.svg"); }
+html[data-backdrop="on"][data-scheme="slate"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/slate.svg"); }
+html[data-backdrop="on"][data-scheme="youtube"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/youtube.svg"); }
+html[data-backdrop="on"][data-scheme="starship"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/starship.svg"); }
+html[data-backdrop="on"][data-scheme="matrix"] body { background-image: linear-gradient(color-mix(in srgb, var(--bg) var(--bg-fade), transparent), color-mix(in srgb, var(--bg) var(--bg-fade), transparent)), url("/backdrops/matrix.svg"); }
+/* [data-theme="dark"] added purely to out-specify the `html[data-theme="dark"] body` ambient rule
+ below, whose `background` shorthand would otherwise reset background-size back to auto. */
+html[data-backdrop="on"][data-theme="dark"] body {
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+}
+
/* Make native controls (date picker, scrollbars) follow the theme. */
html[data-theme="dark"] {
color-scheme: dark;
diff --git a/frontend/src/lib/theme.ts b/frontend/src/lib/theme.ts
index a6d080c..e3a97e6 100644
--- a/frontend/src/lib/theme.ts
+++ b/frontend/src/lib/theme.ts
@@ -16,12 +16,15 @@ export interface ThemePrefs {
mode: Mode;
scheme: Scheme;
fontScale: number;
+ /** Show the per-scheme background image (dark theme only); off = flat colour. */
+ bgImage: boolean;
}
export const DEFAULT_THEME: ThemePrefs = {
mode: "dark",
scheme: "midnight",
fontScale: 1.06,
+ bgImage: true,
};
export function applyTheme(t: ThemePrefs): void {