Theme switch leaves navbar / toolbar / properties / zoom / minimap stuck in light mode #115
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Switching the board theme only re-paints the canvas. The whiteboard chrome — navbar / left toolbar / right properties panel / zoom widget / minimap — stays in light mode regardless of which theme is selected. So picking Dark / Ocean / Warm leaves a Dark canvas surrounded by a white navbar and white side panels.
Steps to reproduce
/board/<id>.Expected
The chrome follows the theme: dark themes give a dark navbar/sidebars; light themes give a white one.
Actual
The canvas updates, but every piece of chrome stays white.
Root cause (already pinpointed)
crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.js::applyThemeonly sets the canvas container'sstyle.backgroundand redraws the grid + calendars. It never touches the--wb-*CSS custom properties ordata-bs-theme.crates/hero_whiteboard_ui/static/web/css/whiteboard.cssdefines two parallel rule sets:.wb-navbar,.wb-toolbar,.wb-properties,.wb-zoom,.wb-minimapall referencevar(--wb-toolbar-bg)etc., so without adata-bs-theme="dark"attribute on<html>they always resolve to the light values.Fix direction
When
applyThemeruns, derive whether the chrome should be dark or light from the theme'scanvas-bgluminance, and toggledocument.documentElement.dataset.bsThemeaccordingly. That single attribute flip cascades to every chrome rule already defined inwhiteboard.css. No duplicated theme tokens, no new rules, and custom themes work automatically based on their canvas color.Implementation Spec for Issue #115
Objective
Make the whiteboard chrome (navbar, left toolbar, right properties panel, zoom widget, minimap) follow the active theme. Today only the canvas changes; every piece of chrome stays in light mode regardless of theme selection.
Root cause (already located)
themes.js::applyThemesetsstage.container().style.background = currentTheme['canvas-bg']and redraws the grid + calendars, but never touches the--wb-*CSS variables or thedata-bs-themeattribute that drives those variables.whiteboard.cssalready defines parallel light/dark rule sets keyed on:root[data-bs-theme="dark"]. So a single attribute toggle on<html>is enough to swing every piece of chrome that usesvar(--wb-toolbar-bg),var(--wb-text),var(--wb-border),var(--wb-hover),var(--wb-bg), etc.Approach
Derive a "chrome is dark?" boolean from the theme's
canvas-bgluminance and setdocument.documentElement.dataset.bsTheme = 'dark'(or remove it for the light fallback) insideapplyTheme. This:applyThemekeeps settingcontainer.style.backgrounddirectly fromcanvas-bg.Requirements
loadBoard → loadTheme → applyThemeflow runs at startup, so we get this for free).canvas-bgis unparsable (defensive fallback to "light").Files to Modify
crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.js— add a small luminance helper, toggledata-bs-themeinsideapplyTheme.Implementation Plan
Step 1: Toggle
data-bs-themefrom canvas-bg luminance in applyThemeFile:
crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.jsAdd a private helper inside the IIFE:
Then near the end of
applyTheme(name, opts), after the canvas/grid update:Dependencies: none.
Acceptance Criteria
applyThemepath).canvas-bg.cargo fmt,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace --libclean.Notes
customThemes) need an automatic rule. Luminance is one tiny helper and gives a deterministic answer.#ffffff) and any near-white custom themes firmly in the light bucket. Slightly off-grey backgrounds like#ccccccstill map to light, while#1c1410(Warm) and#0a192f(Ocean) are clearly dark. Edge cases at the boundary are acceptable since the user picks a theme intentionally and can always pick a different one.app.js::loadBoardalready callsloadTheme(...)which callsapplyTheme(...), so the chrome flips on boot without any extra wiring. No flash because the call happens before the user sees the canvas.whiteboard.cssalready has both rule sets. We just flip the attribute that selects between them.Test Results
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo test --workspace --lib— 0 failednode --check themes.js— cleanImplementation Summary
Make the whiteboard chrome (navbar / left toolbar / right properties / zoom / minimap) follow the active theme by toggling
data-bs-themeon<html>based on the canvas background luminance.crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.jsAdded a private
_isDarkColor(c)helper that parses#rgb/#rrggbb/rgb(...)/rgba(...)strings, computes the standard0.299R + 0.587G + 0.114Bluma, and reportsluma < 140as dark. The 140 threshold (a hair above the 128 midpoint) keeps "Minimal" (#ffffff) and any near-white custom themes in the light bucket while clearly classifying Dark (#212529), Ocean (#0a192f), and Warm (#1c1410) as dark.Inside
applyTheme, after the canvas background is set:whiteboard.cssalready defines parallel rule sets:So a single attribute flip cascades to every chrome rule that uses
var(--wb-toolbar-bg),var(--wb-text),var(--wb-border),var(--wb-hover),var(--wb-bg), etc. No CSS changes required, no duplicated theme tokens.Files Changed
crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.js—+30/-0Test Results
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo test --workspace --lib— 0 failednode --check themes.js— cleanManual smoke
app.js::loadBoard → loadTheme → applyThemeflow runs at startup, so this works without extra wiring).canvas-bg; defensive default is "dark", which matches the legacy default.Notes
customThemes) get classified automatically based on their canvas color.applyThemeruns inloadBoardbefore the user sees the canvas; the attribute flip happens during the same paint cycle.Implementation revised
The first pass only flipped between the two pre-baked light/dark palettes that already lived in
whiteboard.css. That made the chrome dark for Dark/Ocean/Warm but didn't actually follow each theme's color personality — Ocean got the same grey chrome as Dark, Warm got the same grey chrome as Dark.Revised approach
Build a chrome palette from the theme's actual
canvas-bgandtext-colorand write the--wb-*CSS variables onto:rootdirectly. The chrome consumes those variables, so each theme now produces its own coherent chrome:#0a192fcanvas) → bluish-dark chrome (#18273dtoolbar /#425165border /#ccd6f6text)#1c1410canvas) → warm-brown chrome (#2a221etoolbar /#54403aborder /#e8d5c4text)#212529canvas) → neutral dark chrome (matches existing dark palette)#f8f9facanvas) → off-white chrome (#eeeff0toolbar /#d0d1d2border)#ffffffcanvas) → near-white chrome (#f5f5f5toolbar)Helpers added in
themes.js_parseColor(c)— accepts#rgb/#rrggbb/rgb(...)/rgba(...), returns{r,g,b}ornull._luma(rgb)— standard0.299R + 0.587G + 0.114B._isDarkColor(c)—luma < 140(a hair above the midpoint to keep near-white themes in light mode)._shade(c, amt)— brighten or darken each channel byamt, clamped._applyChromePalette(theme)— togglesdata-bs-themefor fallback rules, then sets--wb-bg / --wb-toolbar-bg / --wb-surface / --wb-ctx-bg / --wb-hover / --wb-border / --wb-text / --wb-text-muted / --wb-primaryon:root. Step magnitudes (+14 / +28 / +56 for dark canvases, -10 / -22 / -40 for light) were tuned against the four built-ins.applyThemeReplaces the simple
data-bs-themetoggle with_applyChromePalette(currentTheme). The data attribute is still set as a fallback; any rule we don't override resolves to a sensible default.No CSS changes. Custom themes get classified automatically based on their
canvas-bg. Boot path (loadBoard → loadTheme → applyTheme) is untouched, so the chrome is correct on first paint.Iteration: drop
--wb-primaryoverrideSetting
--wb-primaryfrom the theme'sshape-strokemade the active toolbar button (.tool-btn.active { background: var(--wb-primary); color: white; }) unreadable on Ocean (#64ffdacyan + white text) and Warm (#f59e0bamber + white text).Removed the
--wb-primaryoverride — it now keeps the CSS default (#007AFF), which has good contrast with white text. Canvas-side accents (shape strokes, connector lines, transformer handles) still come from the theme tokens at canvas paint time, so the theme's color personality is preserved on the canvas without breaking chrome contrast.Iteration: theme accent on chrome with auto contrast text
Restored
--wb-primaryto the theme accent and added a paired--wb-primary-textthat picks black or white based on the accent's luminance. Now Ocean's active toolbar button keeps Ocean's cyan accent but uses dark text; Warm uses amber + dark text; Dark uses blue + white text.crates/hero_whiteboard_ui/static/web/js/whiteboard/themes.jscrates/hero_whiteboard_ui/static/web/css/whiteboard.css--wb-primary-text: #ffffffto both:rootrule sets as the default.color: white/color: #fffwithcolor: var(--wb-primary-text)on the five rules that paint text directly on a--wb-primarybackground:.tool-btn.active.sub-opt.active.btn-primary.prop-btn.active.comment-btn.submitOther rules that just use
--wb-primaryfor borders / outlines / accent-color keep their existing behavior.Per-theme outcome
#007AFFaccent + white text (unchanged).#64ffdacyan accent + dark#0f172atext (readable).#f59e0bamber accent + dark text (readable).#0062ccdeep blue + white text.#374151slate + white text.