From 454c4bb25cf4d787adee804cd96ec0a105b73cdf Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 16 Jul 2026 07:06:12 -0400 Subject: [PATCH] fix(kiosk): flatten background compositing so backgrounds don't go black On kiosk, chromium runs software-composited (--in-process-gpu or --disable-gpu, one raster thread). The dashboard's 3D-transformed will-change background layers and the infinite mix-blend-mode:multiply fixed overlays routinely fail to repaint there after the first interaction/route change, leaving the container's black fill (or a stuck multiply layer) covering the screen. A kiosk-mode class is now set on (localStorage kiosk flag, ?kiosk param, or the /kiosk boot path) and gates CSS overrides: background layers become plain 2D opacity crossfades (no preserve-3d/translateZ/ will-change) and the animated multiply overlays are disabled. Co-Authored-By: Claude Fable 5 --- neode-ui/src/App.vue | 7 +++++++ neode-ui/src/style.css | 12 ++++++++++++ neode-ui/src/views/dashboard/dashboard-styles.css | 15 +++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/neode-ui/src/App.vue b/neode-ui/src/App.vue index 730f9e47..ba28cc80 100644 --- a/neode-ui/src/App.vue +++ b/neode-ui/src/App.vue @@ -124,6 +124,13 @@ function syncKioskSafeArea() { if (typeof document === 'undefined') return const isKiosk = localStorage.getItem('kiosk') === 'true' || new URLSearchParams(window.location.search).has('kiosk') + // Very first kiosk boot: the launcher opens /kiosk before the route + // guard has had a chance to persist localStorage.kiosk. + || window.location.pathname === '/kiosk' + // Global styling hook: kiosk chromium runs software-composited + // (--in-process-gpu / --disable-gpu), where heavy compositing effects + // (3D bg layers, animated mix-blend overlays) fail and paint black. + document.documentElement.classList.toggle('kiosk-mode', isKiosk) const rawSafeArea = localStorage.getItem('archipelago_kiosk_safe_area_px') || '0' const safeArea = /^\d{1,3}$/.test(rawSafeArea) ? Number(rawSafeArea) : 0 const rawSafeAreaX = localStorage.getItem('archipelago_kiosk_safe_area_x_px') || rawSafeArea diff --git a/neode-ui/src/style.css b/neode-ui/src/style.css index 85256555..8dc4c3b9 100644 --- a/neode-ui/src/style.css +++ b/neode-ui/src/style.css @@ -1615,6 +1615,18 @@ html.no-backdrop *::after { -webkit-backdrop-filter: none !important; } +/* Kiosk: the infinite mix-blend-mode:multiply glitch overlays with + background-attachment:fixed are a known black-screen vector under the + kiosk's software compositor — a missed repaint leaves a dark multiply + layer stuck over the whole viewport. Their base opacity is 0, so + disabling the animations hides them entirely on kiosk. */ +html.kiosk-mode body::before, +html.kiosk-mode body::after, +html.kiosk-mode::before { + animation: none !important; + will-change: auto !important; +} + /* Dashboard: full viewport width, no letterboxing, no body scroll */ body.dashboard-active { overflow: hidden; diff --git a/neode-ui/src/views/dashboard/dashboard-styles.css b/neode-ui/src/views/dashboard/dashboard-styles.css index f4f58f09..88a99005 100644 --- a/neode-ui/src/views/dashboard/dashboard-styles.css +++ b/neode-ui/src/views/dashboard/dashboard-styles.css @@ -741,6 +741,21 @@ aside:not(.sidebar-animate) .sidebar-logout-btn { transform: translateZ(0) scale(1.05) rotateY(0deg) !important; } +/* Kiosk: chromium runs software-composited (--in-process-gpu or + --disable-gpu, single raster thread). 3D-transformed will-change layers + routinely fail to repaint there after the first background swap, leaving + the container's black fill on screen. Flatten the stack to plain 2D + opacity crossfades in kiosk mode. */ +html.kiosk-mode .dashboard-view .bg-perspective-container { + perspective: none; +} +html.kiosk-mode .dashboard-view .bg-layer { + transform: none !important; + transform-style: flat; + will-change: auto; + transition: opacity 0.45s ease; +} + /* Background glitch effect layers - World Fair style */ .bg-glitch-layer-1, .bg-glitch-layer-2,