feat(glass): light-theme background images + enable the toggle in light

Add a light set of the 6 per-scheme backdrops (public/backdrops/light/*.svg) —
soft, pastel-mid, non-figurative washes tuned to each accent so they read through
the light translucent glass. Wire them up centrally:
- App: the backdrop is on whenever bgImage && !perf (was dark-only), so light
  gets it too.
- index.css: per-scheme rules are now [data-theme]-qualified — dark uses the dark
  set, light the /light/ set; shared cover/fixed size rule for both.
- Settings: the Background image toggle + Image-fade slider work in light now
  (removed the dark-only gates); hint no longer says 'dark only'. Trilingual.
The light glass (62% translucent tier) refracts the light backdrop just like dark
refracts its own; one fade slider controls both.
This commit is contained in:
2026-07-13 00:14:20 +02:00
parent 8019b97ca8
commit a7f9032e0e
12 changed files with 134 additions and 20 deletions
+5 -5
View File
@@ -382,13 +382,13 @@ export default function App() {
setAccountRaw(PERF_KEY, perf ? "1" : "0");
}, [perf]);
// The per-scheme background image (the "glass over image" look) is on only in dark theme, when
// the user hasn't opted out, and not in perf mode. Drives `html[data-backdrop]` (see index.css),
// which both paints the image on <body> and switches the glass to its translucent tier.
// The per-scheme background image (the "glass over image" look) is on when the user hasn't opted
// out and we're not in perf mode. Drives `html[data-backdrop]` (see index.css), which paints the
// theme-appropriate image on <body> (dark or /light/ set) and switches the glass to translucent.
useEffect(() => {
const on = theme.mode === "dark" && theme.bgImage && !perf;
const on = theme.bgImage && !perf;
document.documentElement.dataset.backdrop = on ? "on" : "off";
}, [theme.mode, theme.bgImage, perf]);
}, [theme.bgImage, perf]);
useEffect(() => setHintsEnabled(hints), [hints]);
useEffect(() => configureNotifications(notif), [notif]);
+1 -2
View File
@@ -165,11 +165,10 @@ function Appearance({ prefs }: { prefs: PrefsController }) {
<Switch
label={t("settings.appearance.backgroundImage")}
checked={theme.bgImage}
disabled={theme.mode !== "dark"}
onChange={(v) => setTheme({ ...theme, bgImage: v })}
/>
</SettingRow>
{theme.mode === "dark" && theme.bgImage && (
{theme.bgImage && (
<div className="pl-1 pb-1">
<div className="flex items-center justify-between text-xs text-muted mb-1">
<span>{t("settings.appearance.backgroundFade")}</span>
+1 -1
View File
@@ -34,7 +34,7 @@
"display": "Anzeige",
"darkMode": "Dunkler Modus",
"backgroundImage": "Hintergrundbild",
"backgroundImageHint": "Zeigt ein dezentes, themenbezogenes Hintergrundbild hinter der App, damit die Glasflächen etwas zum Brechen haben. Nur im dunklen Design; aus = einfarbig.",
"backgroundImageHint": "Zeigt ein dezentes, themenbezogenes Hintergrundbild hinter der App, damit die Glasflächen etwas zum Brechen haben. Aus = einfarbig.",
"backgroundFade": "Bildabblendung",
"listView": "Listenansicht",
"listViewHint": "Zeigt den Feed als kompakte Liste statt als Kartenraster an.",
+1 -1
View File
@@ -34,7 +34,7 @@
"display": "Display",
"darkMode": "Dark mode",
"backgroundImage": "Background image",
"backgroundImageHint": "Show a subtle per-theme backdrop image behind the app so the glass surfaces have something to refract. Dark theme only; off = flat colour.",
"backgroundImageHint": "Show a subtle per-scheme backdrop image behind the app so the glass surfaces have something to refract. Off = flat colour.",
"backgroundFade": "Image fade",
"listView": "List view",
"listViewHint": "Show the feed as a compact list instead of a grid of cards.",
+1 -1
View File
@@ -34,7 +34,7 @@
"display": "Megjelenítés",
"darkMode": "Sötét mód",
"backgroundImage": "Háttérkép",
"backgroundImageHint": "Halvány, témánkénti háttérkép az app mögött, hogy az üveges felületeknek legyen mit megtörniük. Csak sötét témában; kikapcsolva sima szín.",
"backgroundImageHint": "Halvány, sémánkénti háttérkép az app mögött, hogy az üveges felületeknek legyen mit megtörniük. Kikapcsolva sima szín.",
"backgroundFade": "Kép elhalványítása",
"listView": "Listanézet",
"listViewHint": "A hírfolyam kompakt listaként jelenik meg a kártyarács helyett.",
+17 -10
View File
@@ -58,16 +58,23 @@ html[data-theme="light"] {
/* Per-scheme background image, painted on <body> (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 {
doesn't scroll. Dark theme uses the dark backdrops, light theme the lighter set in /light/. Only
when the backdrop is on (the "Background image" setting; off in perf). The [data-theme] qualifier
also out-specifies the ambient `background` shorthand (which would else reset background-size). */
html[data-backdrop="on"][data-theme="dark"][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-theme="dark"][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-theme="dark"][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-theme="dark"][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-theme="dark"][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-theme="dark"][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"); }
html[data-backdrop="on"][data-theme="light"][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/light/midnight.svg"); }
html[data-backdrop="on"][data-theme="light"][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/light/forest.svg"); }
html[data-backdrop="on"][data-theme="light"][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/light/slate.svg"); }
html[data-backdrop="on"][data-theme="light"][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/light/youtube.svg"); }
html[data-backdrop="on"][data-theme="light"][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/light/starship.svg"); }
html[data-backdrop="on"][data-theme="light"][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/light/matrix.svg"); }
html[data-backdrop="on"][data-theme="dark"] body,
html[data-backdrop="on"][data-theme="light"] body {
background-size: cover;
background-position: center;
background-repeat: no-repeat;