From aec5e6154009169ff13ae6e903bd653aa39ef344 Mon Sep 17 00:00:00 2001 From: SandObserver <260779319+SandObserver@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:27:31 -0400 Subject: [PATCH] Fix: color app titles from the background behind them Icon labels were always white with a heavy shadow. On a light solid background they fell below the WCAG 1.4.3 minimum, and a bright wallpaper washed them out. Measure the background and pick the label tone from it: one tone for a solid color, and a per-label tone over a wallpaper, sampled from a 32x18 luminance grid of the image as it is shown, brightness included. The tone that keeps the best worst-case contrast across the patch a label covers wins, so a label straddling light and dark stays readable. Sampling reads a second, cross-origin copy of the wallpaper, so an image host that refuses the read leaves the labels as they were rather than failing the wallpaper itself. --- CHANGELOG.md | 3 + tsconfig.frontend.json | 6 + ui/css/dashboard.css | 14 ++ ui/index.html | 4 +- ui/js/dashboard.js | 65 ++++++++- ui/js/label-contrast.js | 229 ++++++++++++++++++++++++++++++++ ui/test/label-contrast.test.mjs | 84 ++++++++++++ 7 files changed, 400 insertions(+), 5 deletions(-) create mode 100644 ui/js/label-contrast.js create mode 100644 ui/test/label-contrast.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bcc4669..986cba32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- App titles now take their color from the background behind them, per icon over + a wallpaper. + - An app can now be dropped between two folders, on the top or bottom edge of a folder row. diff --git a/tsconfig.frontend.json b/tsconfig.frontend.json index 4c26d695..cc399704 100644 --- a/tsconfig.frontend.json +++ b/tsconfig.frontend.json @@ -148,6 +148,12 @@ "/js/jitter.js": [ "./ui/js/jitter.js" ], + "/js/label-contrast.js": [ + "./ui/js/label-contrast.js" + ], + "/js/label-contrast.js?v=*": [ + "./ui/js/label-contrast.js" + ], "/js/layout.js": [ "./ui/js/layout.js" ], diff --git a/ui/css/dashboard.css b/ui/css/dashboard.css index 90b8218c..edfadc3f 100644 --- a/ui/css/dashboard.css +++ b/ui/css/dashboard.css @@ -15,6 +15,10 @@ --icon-sheen: linear-gradient(135deg,rgba(255,255,255,.10) 0%,transparent 60%); --label-color: rgba(255,255,255,.90); --label-shadow: 0 1px 4px rgba(0,0,0,.70); + /* The tone the labels take on a background measured as light. The shadow + is a hairline: the colour carries the contrast, not the shadow. */ + --label-color-dark: rgba(0,0,0,.92); + --label-shadow-dark: 0 0 2px rgba(255,255,255,.55); --bounce-spring: cubic-bezier(.34,1.56,.64,1); --page-ease: cubic-bezier(.77,0,.175,1); /* The page behind the wallpaper, and the boot veil over the unstyled @@ -194,6 +198,13 @@ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } + /* Labels measured against a light background. The tone is set on the + element by the dashboard, never by the theme: the background is the + user's, not the system's. */ + .ilabel[data-tone="dark"], .dyn-mob-label[data-tone="dark"], .dyn-fold-label[data-tone="dark"] { + color:var(--label-color-dark); text-shadow:var(--label-shadow-dark); + } + /* ── Badge — reference size, scaled by --sc (desktop overrides below) ── */ .badge { position:absolute; @@ -720,6 +731,9 @@ a:focus-visible, button:focus-visible, [role="button"]:focus-visible, --label-shadow: 0 1px 4px rgba(0,0,0,.95); } .ilabel, .dyn-mob-label, .dyn-fold-label, .dyn-fold-inner-label { color:var(--label-primary); } + .ilabel[data-tone="dark"], .dyn-mob-label[data-tone="dark"], .dyn-fold-label[data-tone="dark"] { + color:#000000; text-shadow:0 0 2px rgba(255,255,255,.95); + } .ctrl { color:var(--label-primary); background:rgba(255,255,255,.22); } .srn { color:var(--label-primary); } .srh, .sr-section { color:rgba(235,235,245,.85); } diff --git a/ui/index.html b/ui/index.html index 746b7b4b..412fc00d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -24,7 +24,7 @@