fix(shopfloor): proper dark-mode via \$o-webclient-color-scheme branch

Dug deeper after the user reported shop-floor pages staying white in
dark mode. Traced through Odoo 19 source:

  _dependencies/web_enterprise/static/src/
    webclient/color_scheme/color_scheme_service.js  <- reads cookie
    scss/primary_variables.scss       \$o-webclient-color-scheme: bright
    scss/primary_variables.dark.scss  \$o-webclient-color-scheme: dark

Odoo compiles TWO separate CSS bundles:
  web.assets_backend       -> compiled with \$...scheme: bright
  web.assets_web_dark      -> compiled with \$...scheme: dark
    (the .dark.scss files are layered in front of the light ones)

Our shop-floor SCSS is in web.assets_backend, which means it gets
compiled into BOTH bundles. But the previous CSS-variable fallback
chain (var(--fp-page-bg, var(--bs-tertiary-bg, #hex))) baked the
SAME hex fallback into both bundles, so cards stayed white in dark.

Odoo's own code doesn't redefine --bs-* CSS custom properties at
runtime either — it just bakes the dark palette straight into the
dark bundle via SCSS \$-variables during compile.

Fix: _fp_shopfloor_tokens.scss now branches at compile time:

    \$o-webclient-color-scheme: bright !default;

    \$_fp-page-hex: #f3f4f6;  // light defaults
    \$_fp-card-hex: #ffffff;
    ...
    @if \$o-webclient-color-scheme == dark {
        \$_fp-page-hex: #1a1d21 !global;
        \$_fp-card-hex: #22262d !global;
        ...
    }

    \$fp-page: var(--fp-page-bg, \$_fp-page-hex);
    \$fp-card: var(--fp-card-bg, \$_fp-card-hex);

The CSS-custom-property fallback stays so deployments can still skin
via --fp-* without touching SCSS; the underlying hex changes between
bundles.

Verified via odoo-shell:
  LIGHT bundle: .o_fp_plant_overview { background-color: var(...#f3f4f6) }
                .o_fp_po_card         { background-color: var(...#ffffff);
                                         border: ... #d8dadd }
  DARK bundle:  .o_fp_plant_overview { background-color: var(...#1a1d21) }
                .o_fp_po_card         { background-color: var(...#22262d);
                                         border: ... #343942 }

Two separate bundle URLs generated:
  /web/assets/a593157/web.assets_backend.min.css
  /web/assets/a9dba7d/web.assets_web_dark.min.css

=== CLAUDE.md ===
Replaced the previous (incorrect) .o_dark_mode override advice with
a proper "Branch on \$o-webclient-color-scheme at SCSS compile time"
section, including the bundle names and the verify-via-odoo-shell
snippet. Future redesigns now have a single, correct pattern to
follow.

Version bumped 19.0.4.0.0 -> 19.0.5.0.0 to force asset hash change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-18 19:30:14 -04:00
parent 11f7791c5e
commit afc01ec1d9
3 changed files with 81 additions and 38 deletions

View File

@@ -29,16 +29,39 @@ For custom OWL dashboards / client actions use the same approach:
$fp-border: var(--fp-border-color, #d8dadd);
```
- Reference those tokens everywhere (never `var(--bs-border-color)` directly)
- Override the custom properties for dark mode at the bottom of the tokens file:
```scss
:root[data-bs-theme="dark"], body.o_dark_mode, .o_dark_mode {
--fp-card-bg: #22262d;
--fp-border-color: #343942;
}
```
- Three-layer contrast: **page** (grayest) → **container/column** (mid) → **card** (brightest). That's what makes cards pop.
- Reference implementation: `fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss`.
## Dark Mode — Branch on `$o-webclient-color-scheme` at SCSS Compile Time
Odoo 19 does NOT flip dark mode via a runtime DOM class. It compiles TWO asset bundles:
- `web.assets_backend` — compiled with `$o-webclient-color-scheme: bright`
- `web.assets_web_dark` — compiled with `$o-webclient-color-scheme: dark` (dark variant primary variables loaded first)
Your SCSS file is compiled into BOTH bundles. To make the dark bundle have different colors, **branch at compile time** using the SCSS variable Odoo sets:
```scss
$o-webclient-color-scheme: bright !default;
$_my-page-hex: #f3f4f6;
$_my-card-hex: #ffffff;
@if $o-webclient-color-scheme == dark {
$_my-page-hex: #1a1d21 !global;
$_my-card-hex: #22262d !global;
}
$my-page: var(--my-page-bg, $_my-page-hex);
$my-card: var(--my-card-bg, $_my-card-hex);
```
**Do NOT use** `.o_dark_mode` class selectors, `[data-bs-theme="dark"]`, or `@media (prefers-color-scheme: dark)` — none of those fire reliably in Odoo 19. The user toggles dark mode via the user profile, which sets a `color_scheme` cookie and reloads the page; Odoo then serves the dark bundle. Your SCSS `@if` handles the rest at compile time.
Verify by inspecting the attachments — you should see two files with different URLs for the two bundles:
```python
env['ir.qweb']._get_asset_bundle('web.assets_backend').css() # light
env['ir.qweb']._get_asset_bundle('web.assets_web_dark').css() # dark
```
## Asset Bundle Cache Busting
Odoo content-hashes the compiled bundle URL (`/web/assets/<hash>/...`). When CSS changes but the hash doesn't update, the browser serves the old bundle. Fixes in order of escalation:
1. Bump the module `version` in `__manifest__.py`

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Shop Floor',
'version': '19.0.4.0.0',
'version': '19.0.5.0.0',
'category': 'Manufacturing/Plating',
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, '
'first-piece inspection gates.',

View File

@@ -31,39 +31,59 @@ $fp-radius-lg : 20px;
$fp-radius-xl : 28px;
$fp-radius-pill: 999px;
// ---------- Surfaces — tied to Odoo's Bootstrap tokens -----------------------
// ---------- Surfaces — COMPILE-TIME branch on Odoo's dark scheme -------------
//
// Odoo 19's dark mode is NOT a runtime class toggle — it's a separate
// compiled CSS bundle where --bs-body-bg, --bs-card-bg, --bs-tertiary-bg,
// --bs-border-color are baked in with dark values. So if we chain our
// tokens through those Bootstrap variables, they auto-invert when Odoo
// swaps the bundle; no .o_dark_mode override needed.
// Odoo 19 compiles TWO asset bundles: web.assets_backend (light) and
// web.assets_web_dark (dark). The two bundles differ only in the value
// of the SCSS variable $o-webclient-color-scheme — `bright` for light,
// `dark` for dark (defined in primary_variables.scss /
// primary_variables.dark.scss in web_enterprise).
//
// Fallback order:
// 1. --fp-* (custom override, e.g. shop-branded skin)
// 2. --bs-* (Odoo/Bootstrap semantic token — inverts on dark)
// 3. explicit hex (absolute fallback if vars are missing)
$fp-page : var(--fp-page-bg,
var(--bs-tertiary-bg, #f3f4f6));
$fp-card : var(--fp-card-bg,
var(--bs-card-bg,
var(--o-view-background-color, #ffffff)));
$fp-card-soft : var(--fp-card-soft-bg,
var(--bs-secondary-bg, #f1f3f5));
$fp-border : var(--fp-border-color,
var(--bs-border-color, #d8dadd));
$fp-border-strong : var(--fp-border-strong,
var(--bs-border-color-translucent, #b6babf));
// Odoo does NOT redefine --bs-body-bg / --bs-card-bg as CSS custom
// properties at runtime. It bakes the chosen palette into the bundle
// at compile time via Bootstrap SCSS variables. So our tokens must do
// the same: branch on $o-webclient-color-scheme at compile time and
// emit the right hex values into each bundle.
// ---------- Text tiers — same chain pattern ----------------------------------
$fp-ink : var(--fp-ink, var(--bs-body-color, #1f2937));
$fp-ink-soft : var(--fp-ink-soft, var(--bs-secondary-color, #4b5563));
$fp-ink-mute : var(--fp-ink-mute, var(--bs-tertiary-color, #6b7280));
$fp-ink-faint : var(--fp-ink-faint,
color-mix(in srgb, var(--bs-body-color, #000) 40%, transparent));
$o-webclient-color-scheme: bright !default;
// Action colour — Odoo's primary. Explicit hex fallback to Odoo's
// default brand purple so .btn-primary doesn't render transparent.
// Default (light / bright) palette
$_fp-page-hex : #f3f4f6;
$_fp-card-hex : #ffffff;
$_fp-card-soft-hex : #f1f3f5;
$_fp-border-hex : #d8dadd;
$_fp-border-strong-hex : #b6babf;
$_fp-ink-hex : #1f2937;
$_fp-ink-soft-hex : #4b5563;
$_fp-ink-mute-hex : #6b7280;
$_fp-ink-faint-hex : #9ca3af;
// Dark palette — engaged when the dark bundle is compiled
@if $o-webclient-color-scheme == dark {
$_fp-page-hex : #1a1d21 !global;
$_fp-card-hex : #22262d !global;
$_fp-card-soft-hex : #1c2027 !global;
$_fp-border-hex : #343942 !global;
$_fp-border-strong-hex : #4a505a !global;
$_fp-ink-hex : #e5e7eb !global;
$_fp-ink-soft-hex : #c8ccd2 !global;
$_fp-ink-mute-hex : #8a909a !global;
$_fp-ink-faint-hex : #5a606b !global;
}
// Public tokens — CSS custom property fallback chain remains so a
// deployment can still override via --fp-* without touching SCSS.
$fp-page : var(--fp-page-bg, $_fp-page-hex);
$fp-card : var(--fp-card-bg, $_fp-card-hex);
$fp-card-soft : var(--fp-card-soft-bg, $_fp-card-soft-hex);
$fp-border : var(--fp-border-color, $_fp-border-hex);
$fp-border-strong : var(--fp-border-strong, $_fp-border-strong-hex);
$fp-ink : var(--fp-ink, $_fp-ink-hex);
$fp-ink-soft : var(--fp-ink-soft, $_fp-ink-soft-hex);
$fp-ink-mute : var(--fp-ink-mute, $_fp-ink-mute-hex);
$fp-ink-faint : var(--fp-ink-faint, $_fp-ink-faint-hex);
// Action colour — Odoo's primary. Same in both bundles (brand purple).
$fp-accent : var(--o-action, #714B67);
// ---------- Elevation — explicit rgba shadows --------------------------------