fix(shopfloor): explicit hex colors like Odoo's own kanban
Why the borders weren't showing: the previous approach used
color-mix(var(--bs-body-color) 4%, var(--o-view-background-color)) for
card/column backgrounds. Under Odoo 19 the resolved values for those
variables were nearly identical to var(--bs-body-bg), so the card
surfaces visually merged into the page. Same problem for borders:
var(--bs-border-color) can render extremely faint depending on theme.
Checked what Odoo's native kanban does — dug through the compiled
CSS and found:
.o_kanban_record { background-color: white;
border: 1px solid #d8dadd; }
.o_kanban_group { background: var(--KanbanGroup-background); }
Odoo uses EXPLICIT hex values and card-specific tokens, not the
generic body/border variables. Adopted the same approach.
New tokens in _fp_shopfloor_tokens.scss — all explicit, plus a
dark-mode override block keyed off [data-bs-theme="dark"] and
.o_dark_mode (Odoo 19 uses both):
light dark
------------------------ ------------------
--fp-page-bg: #f3f4f6 #13161a
--fp-column-bg: #e9ebef #1a1e24
--fp-card-bg: #ffffff #22262d
--fp-card-soft-bg: #f8fafc #1c2027
--fp-border-color: #d8dadd #343942
--fp-ink: #1f2937 #e5e7eb
--fp-ink-mute: #6b7280 #8a909a
shadow scale switched from color-mix to explicit rgba(0,0,0,...)
so it renders identically across browsers.
All three SCSS files updated via sed to swap
var(--bs-border-color) -> #{$fp-border}
...then $fp-border resolves to var(--fp-border-color, #d8dadd) — a
proper card-level border that is VISIBLE (28 refs to --fp-card-bg
and 35 refs to --fp-border-color confirmed in the compiled bundle).
Plant Overview specifically now has:
* Column: #f8fafc bg + #d8dadd border + shadow
(column is brighter than the page it sits on)
* Column HEADER: #ffffff inside the column, with bottom border
(clear separator between stages)
* Card: solid #ffffff bg + #d8dadd border + shadow
(brightest surface, pops off the column)
* Gap between columns: 16px so the column borders don't touch
Module version bumped to 19.0.3.0.0. Bundle regenerated at
/web/assets/0cd8bc1/web.assets_backend.min.css (1.45 MB, id 1939).
Verified by parsing compiled CSS:
.o_fp_po_card: background-color: var(--fp-card-bg, #ffffff);
border: 1px solid var(--fp-border-color, #d8dadd);
.o_fp_po_column: background-color: var(--fp-card-soft-bg, #f8fafc);
border: 1px solid var(--fp-border-color, #d8dadd);
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Shop Floor',
|
||||
'version': '19.0.2.0.0',
|
||||
'version': '19.0.3.0.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, '
|
||||
'first-piece inspection gates.',
|
||||
|
||||
@@ -31,32 +31,39 @@ $fp-radius-lg : 20px;
|
||||
$fp-radius-xl : 28px;
|
||||
$fp-radius-pill: 999px;
|
||||
|
||||
// ---------- Surfaces — depth by TINT, not by border --------------------------
|
||||
// The page gets a slightly tinted background; cards sit on a lighter
|
||||
// surface. That tint difference replaces the need for card borders.
|
||||
$fp-page : color-mix(in srgb, var(--bs-body-color) 2.5%,
|
||||
var(--o-view-background-color, var(--bs-body-bg)));
|
||||
$fp-card : var(--o-view-background-color, var(--bs-body-bg));
|
||||
$fp-card-soft : color-mix(in srgb, var(--bs-body-color) 4%,
|
||||
var(--o-view-background-color, var(--bs-body-bg)));
|
||||
// ---------- Surfaces — explicit values, matching Odoo kanban conventions -----
|
||||
// Odoo's own kanban uses hex values (not CSS vars) because --bs-border-color
|
||||
// and --bs-body-bg drift between themes and addons. We follow the same
|
||||
// approach with clear, strong contrast: tinted page, tinted column, SOLID
|
||||
// WHITE card with a VISIBLE border.
|
||||
//
|
||||
// Dark mode: overrides are applied at the bottom of each file via
|
||||
// :root[data-bs-theme="dark"], body.o_dark_mode, or @media dark-pref.
|
||||
$fp-page : var(--fp-page-bg, #f3f4f6);
|
||||
$fp-column : var(--fp-column-bg, #e9ebef);
|
||||
$fp-card : var(--fp-card-bg, #ffffff);
|
||||
$fp-card-soft : var(--fp-card-soft-bg, #f8fafc);
|
||||
$fp-border : var(--fp-border-color, #d8dadd);
|
||||
$fp-border-strong : var(--fp-border-strong, #b6babf);
|
||||
|
||||
// ---------- Text tiers -------------------------------------------------------
|
||||
$fp-ink : var(--bs-body-color);
|
||||
$fp-ink-soft : color-mix(in srgb, var(--bs-body-color) 70%, transparent);
|
||||
$fp-ink-mute : color-mix(in srgb, var(--bs-body-color) 48%, transparent);
|
||||
$fp-ink-faint : color-mix(in srgb, var(--bs-body-color) 28%, transparent);
|
||||
$fp-ink : var(--fp-ink, #1f2937);
|
||||
$fp-ink-soft : var(--fp-ink-soft, #4b5563);
|
||||
$fp-ink-mute : var(--fp-ink-mute, #6b7280);
|
||||
$fp-ink-faint : var(--fp-ink-faint, #9ca3af);
|
||||
|
||||
// ---------- Elevation — soft, layered, theme-safe ----------------------------
|
||||
// Shadows built on the foreground colour so they darken appropriately in
|
||||
// light mode and show a subtle halo in dark mode.
|
||||
$fp-elev-1 : 0 1px 2px color-mix(in srgb, var(--bs-body-color) 5%, transparent),
|
||||
0 1px 3px color-mix(in srgb, var(--bs-body-color) 7%, transparent);
|
||||
$fp-elev-2 : 0 2px 4px color-mix(in srgb, var(--bs-body-color) 6%, transparent),
|
||||
0 6px 14px color-mix(in srgb, var(--bs-body-color) 9%, transparent);
|
||||
$fp-elev-3 : 0 4px 8px color-mix(in srgb, var(--bs-body-color) 8%, transparent),
|
||||
0 12px 28px color-mix(in srgb, var(--bs-body-color) 12%, transparent);
|
||||
$fp-elev-hover : 0 6px 12px color-mix(in srgb, var(--bs-body-color) 10%, transparent),
|
||||
0 18px 36px color-mix(in srgb, var(--bs-body-color) 14%, transparent);
|
||||
// ---------- Elevation — explicit rgba shadows --------------------------------
|
||||
// Explicit rgba values (not color-mix) so they render identically across
|
||||
// browsers and themes. In dark mode the shadows still work against the
|
||||
// darker surfaces because they're translucent.
|
||||
$fp-elev-1 : 0 1px 2px rgba(0, 0, 0, 0.06),
|
||||
0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
$fp-elev-2 : 0 2px 4px rgba(0, 0, 0, 0.06),
|
||||
0 6px 14px rgba(0, 0, 0, 0.10);
|
||||
$fp-elev-3 : 0 4px 8px rgba(0, 0, 0, 0.10),
|
||||
0 12px 28px rgba(0, 0, 0, 0.14);
|
||||
$fp-elev-hover : 0 6px 12px rgba(0, 0, 0, 0.12),
|
||||
0 18px 36px rgba(0, 0, 0, 0.16);
|
||||
|
||||
// ---------- Semantic colour helpers (NOT gradients) --------------------------
|
||||
$fp-accent : var(--o-action); // the one action colour
|
||||
@@ -129,3 +136,24 @@ $fp-touch-min : 48px; // larger than Apple's 44px minimum — shop floor
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Dark-mode palette overrides
|
||||
// Odoo 19 flips dark mode via [data-bs-theme="dark"] on the root.
|
||||
// We provide a matching-level body selector for compatibility.
|
||||
// =============================================================================
|
||||
:root[data-bs-theme="dark"],
|
||||
body.o_dark_mode,
|
||||
.o_dark_mode {
|
||||
--fp-page-bg : #13161a;
|
||||
--fp-column-bg : #1a1e24;
|
||||
--fp-card-bg : #22262d;
|
||||
--fp-card-soft-bg : #1c2027;
|
||||
--fp-border-color : #343942;
|
||||
--fp-border-strong : #4a505a;
|
||||
--fp-ink : #e5e7eb;
|
||||
--fp-ink-soft : #c8ccd2;
|
||||
--fp-ink-mute : #8a909a;
|
||||
--fp-ink-faint : #5a606b;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
min-width: 240px;
|
||||
min-height: $fp-touch-min;
|
||||
padding: $fp-space-2 $fp-space-4;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
@@ -102,7 +102,7 @@
|
||||
.o_fp_scan_toggle {
|
||||
min-height: $fp-touch-min;
|
||||
padding: 0 $fp-space-5;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
@@ -117,7 +117,7 @@
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
box-shadow: $fp-elev-2;
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
}
|
||||
}
|
||||
&:active { transform: scale(0.97); }
|
||||
@@ -206,7 +206,7 @@
|
||||
.o_fp_kpi {
|
||||
position: relative;
|
||||
padding: $fp-space-5;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-lg;
|
||||
background-color: $fp-card;
|
||||
box-shadow: $fp-elev-1;
|
||||
@@ -217,7 +217,7 @@
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: $fp-elev-2;
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 30%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 30%, #{$fp-border});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@
|
||||
// -------------------------------------------------------------------------
|
||||
.o_fp_panel {
|
||||
background-color: $fp-card;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-lg;
|
||||
box-shadow: $fp-elev-1;
|
||||
padding: $fp-space-5;
|
||||
@@ -346,7 +346,7 @@
|
||||
align-items: center;
|
||||
margin-bottom: $fp-space-4;
|
||||
padding-bottom: $fp-space-3;
|
||||
border-bottom: 1px solid var(--bs-border-color);
|
||||
border-bottom: 1px solid #{$fp-border};
|
||||
|
||||
h3 {
|
||||
font-size: $fp-text-lg;
|
||||
@@ -411,7 +411,7 @@
|
||||
align-items: center;
|
||||
gap: $fp-space-3;
|
||||
padding: $fp-space-3 $fp-space-4;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
min-height: 64px;
|
||||
@@ -423,7 +423,7 @@
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, #{$fp-accent} 4%, $fp-card);
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
box-shadow: $fp-elev-2;
|
||||
}
|
||||
}
|
||||
@@ -486,7 +486,7 @@
|
||||
}
|
||||
.o_fp_tile {
|
||||
padding: $fp-space-4;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
cursor: pointer;
|
||||
@@ -499,7 +499,7 @@
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
background-color: color-mix(in srgb, #{$fp-accent} 4%, $fp-card);
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
box-shadow: $fp-elev-2;
|
||||
}
|
||||
}
|
||||
@@ -550,7 +550,7 @@
|
||||
align-items: center;
|
||||
gap: $fp-space-3;
|
||||
padding: $fp-space-3 $fp-space-4;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
min-height: 64px;
|
||||
@@ -569,7 +569,7 @@
|
||||
&[data-state="missed_window"], &[data-state="fail"], &[data-state="on_hold"] {
|
||||
box-shadow: inset 4px 0 0 0 $fp-bad;
|
||||
background-color: fp-wash(--bs-danger, 5%);
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, #{$fp-border});
|
||||
}
|
||||
&[data-state="baked"], &[data-state="pass"] {
|
||||
box-shadow: inset 4px 0 0 0 $fp-ok;
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
padding: 0 $fp-space-4;
|
||||
border-radius: $fp-radius-md;
|
||||
font-weight: $fp-weight-semibold;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
box-shadow: $fp-elev-1;
|
||||
@@ -94,7 +94,7 @@
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
box-shadow: $fp-elev-2;
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
}
|
||||
}
|
||||
&:active { transform: scale(0.97); }
|
||||
@@ -149,7 +149,7 @@
|
||||
.o_fp_kpi {
|
||||
position: relative;
|
||||
padding: $fp-space-5;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-lg;
|
||||
background-color: $fp-card;
|
||||
box-shadow: $fp-elev-1;
|
||||
@@ -160,7 +160,7 @@
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: $fp-elev-2;
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 30%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 30%, #{$fp-border});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
// -------------------------------------------------------------------------
|
||||
.o_fp_panel {
|
||||
background-color: $fp-card;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-lg;
|
||||
box-shadow: $fp-elev-1;
|
||||
padding: $fp-space-5;
|
||||
@@ -238,7 +238,7 @@
|
||||
align-items: center;
|
||||
margin-bottom: $fp-space-4;
|
||||
padding-bottom: $fp-space-3;
|
||||
border-bottom: 1px solid var(--bs-border-color);
|
||||
border-bottom: 1px solid #{$fp-border};
|
||||
|
||||
h3 {
|
||||
font-size: $fp-text-lg;
|
||||
@@ -309,7 +309,7 @@
|
||||
.o_fp_mgr_card {
|
||||
position: relative;
|
||||
background-color: $fp-card;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
overflow: hidden;
|
||||
transition: transform $fp-dur-fast $fp-ease,
|
||||
@@ -318,7 +318,7 @@
|
||||
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
box-shadow: $fp-elev-2;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
@@ -327,7 +327,7 @@
|
||||
// Priority stripe (4px) on the left — only when priority is set
|
||||
&[data-priority="2"] {
|
||||
background-color: color-mix(in srgb, #{$fp-bad} 4%, $fp-card);
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, #{$fp-border});
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute; left: 0; top: 0; bottom: 0;
|
||||
@@ -376,7 +376,7 @@
|
||||
align-items: center;
|
||||
padding: $fp-space-2 $fp-space-3;
|
||||
background-color: $fp-card;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-sm;
|
||||
font-size: $fp-text-sm;
|
||||
|
||||
@@ -401,7 +401,7 @@
|
||||
min-width: 140px; max-width: 220px;
|
||||
min-height: 40px;
|
||||
padding: 0 $fp-space-3;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-sm;
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
@@ -454,7 +454,7 @@
|
||||
align-items: center;
|
||||
gap: $fp-space-3;
|
||||
padding: $fp-space-3 $fp-space-4;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
cursor: pointer;
|
||||
@@ -467,7 +467,7 @@
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, #{$fp-accent} 4%, $fp-card);
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
box-shadow: $fp-elev-2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
.o_fp_po_search_input {
|
||||
padding: 0 $fp-space-4 0 $fp-space-7;
|
||||
min-height: $fp-touch-min;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
@@ -95,7 +95,7 @@
|
||||
.o_fp_po_refresh_btn {
|
||||
width: $fp-touch-min; height: $fp-touch-min;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
background-color: $fp-card;
|
||||
color: $fp-ink;
|
||||
@@ -132,7 +132,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: $fp-card-soft;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-lg;
|
||||
box-shadow: $fp-elev-1;
|
||||
max-height: calc(100vh - 180px);
|
||||
@@ -148,7 +148,7 @@
|
||||
.o_fp_po_col_header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: $fp-space-4;
|
||||
border-bottom: 1px solid var(--bs-border-color);
|
||||
border-bottom: 1px solid #{$fp-border};
|
||||
background-color: $fp-card;
|
||||
|
||||
.o_fp_po_col_name {
|
||||
@@ -214,7 +214,7 @@
|
||||
.o_fp_po_card {
|
||||
position: relative;
|
||||
background-color: $fp-card;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
border-radius: $fp-radius-md;
|
||||
padding: $fp-space-3 $fp-space-4;
|
||||
margin-bottom: $fp-space-2;
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
@include fp-hover-only {
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-accent} 45%, #{$fp-border});
|
||||
box-shadow: $fp-elev-2;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
@@ -251,7 +251,7 @@
|
||||
}
|
||||
&[data-priority="2"], &.o_fp_po_card_hot {
|
||||
background-color: color-mix(in srgb, #{$fp-bad} 5%, $fp-card);
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, var(--bs-border-color));
|
||||
border-color: color-mix(in srgb, #{$fp-bad} 35%, #{$fp-border});
|
||||
&::before { background-color: $fp-bad; }
|
||||
}
|
||||
&[data-priority="1"], &.o_fp_po_card_urgent {
|
||||
@@ -271,7 +271,7 @@
|
||||
border-radius: $fp-radius-sm;
|
||||
object-fit: contain;
|
||||
background-color: $fp-card-soft;
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border: 1px solid #{$fp-border};
|
||||
padding: 2px;
|
||||
}
|
||||
.o_fp_po_card_avatar_blank {
|
||||
|
||||
Reference in New Issue
Block a user