Background Image Rendering#
Background images in homepage are configured in settings.yaml and rendered by the Wrapper component in src/pages/index.jsx. The image appears as a fixed, full-viewport <div id="background"> behind all page content.
Configuration (settings.yaml)#
Two YAML formats are supported :
Flat — image URL or path only:
background: https://example.com/photo.jpg
# or a local path:
background: /images/background.png
Nested — with filter options:
background:
image: /images/background.png
blur: sm # Tailwind backdrop-blur suffix
saturate: 50 # Tailwind backdrop-saturate value
brightness: 50 # Tailwind backdrop-brightness value
opacity: 50 # 0–100; blends theme color over image
Local images must be volume-mounted into /app/public/images/ in the container. The container must be restarted after adding new images .
How It Works#
Settings Parsing#
Wrapper reads initialSettings.background and normalizes both formats :
- String →
backgroundImage = bg(flat format) - Object →
backgroundImage = bg.image; extractsblur,saturate,brightness, andopacity
The opacity value is inverted on read: opacity = 1 - bg.opacity / 100. A higher configured value means less color overlay (more image visible). Setting opacity: 0 in config yields an opacity of 1 — a fully opaque theme-color overlay that hides the image entirely. This was a known bug fixed in PR #5622.
The #background Div#
When backgroundImage is set, Wrapper renders :
linear-gradient(rgb(var(--bg-color) / {opacity}), rgb(var(--bg-color) / {opacity})), url('{backgroundImage}')
This composites the theme color as a translucent overlay on top of the image. --bg-color resolves to --color-50 in light mode and --color-800 in dark mode .
The #background div is styled in globals.css as:
position: fixed; inset: 0; z-index: 0— pinned behind all contentbackground-size: cover; background-position: center; background-repeat: no-repeatpointer-events: none— transparent to mouse/touch events
Filter Classes#
blur, saturate, and brightness are applied via Tailwind backdrop-* classes on the #inner_wrapper div — not on the background div itself. This means they filter everything rendered above the background layer. These options are incompatible with cardBlur .
Network Request Triggering#
The image URL is embedded directly in the style prop of #background at render time. The browser issues a network request for the image as soon as the div renders — there is no lazy-loading or explicit preloading .
A useEffect cleans up any stale body.style background properties on each re-render , a remnant of an earlier approach that applied the background directly to document.body (reverted in PR #5828).
Key Files#
| File | Role |
|---|---|
src/pages/index.jsx | Wrapper component — parses settings, renders #background, applies filter classes |
src/styles/globals.css | CSS for #background positioning; --bg-color variable per theme |
config/settings.yaml | User configuration source |
Relevant PRs#
- PR #5622 — Fix bg opacity calculation: Corrected inverted opacity formula (
bg.opacity / 100→1 - bg.opacity / 100). - PR #5828 — Fix: restore bg image to body again: Reverted background application from
document.bodyinline styles back to the#backgrounddiv approach.