@meshmakers/octo-ui 3.4.1480 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,18 @@
1
1
  // ============================================================================
2
- // RUNTIME BROWSER - Theme tokens (dark default + light)
2
+ // RUNTIME BROWSER - Theme tokens (dark default + light) — "Deep Sea"
3
3
  //
4
- // `_variables.scss` defines the LCARS brand palette and *consumes* the
5
- // semantic `--theme-*` tokens with dark fallbacks. This file is where those
6
- // semantic tokens are actually defined, for both themes, so that every host
7
- // app resolves them identically instead of maintaining its own copy.
4
+ // `_variables.scss` defines the brand palette and *consumes* the semantic
5
+ // `--theme-*` tokens with dark fallbacks. This file is where those semantic
6
+ // tokens are actually defined, for both themes, so that every host app
7
+ // resolves them identically instead of maintaining its own copy.
8
+ //
9
+ // Values follow the "Deep Sea" palette (Refinery Studio concept
10
+ // `ui-concept-ai-os.md` §6.2–6.4, AB#5519): surfaces sit close to the canvas
11
+ // and carry structure, borders are neutral hairlines, mint is the accent
12
+ // (focus ring, primary action, active state) rather than chrome, glows
13
+ // resolve to `none` and the former gradients are flat. Every token name of
14
+ // the LCARS era is kept so component SCSS does not break; new tokens are
15
+ // marked "(new)".
8
16
  //
9
17
  // Usage in a host app's global stylesheet:
10
18
  //
@@ -15,55 +23,109 @@
15
23
  //
16
24
  // Dark is the default. Light is applied either by OS preference (when the
17
25
  // host has not forced dark) or by an explicit `data-theme="light"` on the
18
- // root element — see ThemeService in @meshmakers/octo-ui/branding.
26
+ // root element — see ThemeModeService in @meshmakers/octo-ui.
19
27
  // ============================================================================
20
28
 
21
29
  @use "variables" as vars;
22
30
 
31
+ // ----------------------------------------------------------------------------
32
+ // FOUNDATION (theme-invariant): typography, spacing, radius, motion.
33
+ // ----------------------------------------------------------------------------
34
+ @mixin foundation-tokens {
35
+ // Typography (new). Display = titles and KPI numbers, UI = everything else,
36
+ // mono = ids, versions, YAML/JSON, cron, URLs. `--lcars-font-*` alias these (kept for the Meshmakers App).
37
+ --theme-font-display: "Montserrat", "Roboto", "Helvetica Neue", sans-serif;
38
+ --theme-font-ui: "Roboto", "Helvetica Neue", Arial, sans-serif;
39
+ --theme-font-mono: "Roboto Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
40
+
41
+ // Spacing on a 4 px grid (new). Page gutter = --theme-space-4.
42
+ --theme-space-1: 4px;
43
+ --theme-space-2: 8px;
44
+ --theme-space-3: 12px;
45
+ --theme-space-4: 16px;
46
+ --theme-space-5: 20px;
47
+ --theme-space-6: 24px;
48
+ --theme-space-7: 28px;
49
+ --theme-space-8: 32px;
50
+
51
+ // Radius — symmetric edges, no pills (new).
52
+ --theme-radius-xs: 2px; // chips
53
+ --theme-radius-sm: 4px; // buttons, inputs, tabs
54
+ --theme-radius-md: 6px; // cards, panels
55
+ --theme-radius-lg: 8px; // dialogs, popovers, palette
56
+
57
+ // Motion (new). Collapsed to 0 under prefers-reduced-motion (see theme()).
58
+ --theme-motion-fast: 120ms;
59
+ --theme-motion-base: 200ms;
60
+ --theme-motion-easing: cubic-bezier(0.2, 0, 0, 1);
61
+ }
62
+
23
63
  // ----------------------------------------------------------------------------
24
64
  // DARK THEME (default)
25
65
  // ----------------------------------------------------------------------------
26
66
  @mixin dark-theme-tokens {
27
- // Surfaces & backgrounds
67
+ // Surfaces & backgrounds. Panels sit 1–2 % above the canvas and let a
68
+ // hairline do the rest (the LCARS Iron Navy #394555 read as a heavy slab).
28
69
  --theme-bg-app: #07172b;
29
70
  --theme-bg-app-end: #07172b;
30
- --theme-bg-surface: #394555;
31
- --theme-bg-elevated: #1f2e40;
32
- --theme-bg-overlay: rgba(31, 46, 64, 0.4);
33
- --theme-bg-input: #1f2e40;
34
-
35
- // Text
36
- --theme-text-primary: #ffffff;
37
- --theme-text-secondary: #9292a6;
38
- --theme-text-muted: #6b7689;
71
+ --theme-bg-surface: #0c1e35;
72
+ --theme-bg-elevated: #122540;
73
+ --theme-bg-overlay: #182d4a;
74
+ --theme-bg-input: #0a1a2f;
75
+ --theme-bg-sunken: #050f1f; // (new) code/YAML editors, wells
76
+ --theme-bg-selected: color-mix(in srgb, var(--theme-accent) 14%, transparent); // (new)
77
+ --theme-bg-hover: var(--theme-ink-04); // (new)
78
+
79
+ // Text (pure white is too hot on navy)
80
+ --theme-text-primary: #e6edf5;
81
+ --theme-text-secondary: #9aa8bb;
82
+ --theme-text-muted: #77869c; // concept §6.2 #687890 (4.0:1) raised to ≥ 4.5:1 on bg-input/app/surface
39
83
  --theme-text-on-accent: #07172b;
40
84
  --theme-text-accent: #64ceb9;
41
-
42
- // Borders
43
- --theme-border-subtle: rgba(100, 206, 185, 0.2);
44
- --theme-border-strong: rgba(100, 206, 185, 0.45);
45
- --theme-border-divider: rgba(255, 255, 255, 0.08);
46
-
47
- // Effects
48
- --theme-glow-primary: 0 0 10px rgba(100, 206, 185, 0.4);
49
- --theme-glow-accent: 0 0 15px rgba(100, 206, 185, 0.5);
50
- --theme-shadow-panel: 0 4px 30px rgba(0, 0, 0, 0.3);
51
- --theme-shadow-popup: 0 8px 32px rgba(0, 0, 0, 0.5);
52
-
53
- // Gradients
54
- --theme-gradient-page: linear-gradient(180deg, rgba(7, 23, 43, 0.95), rgba(7, 23, 43, 1));
55
- --theme-gradient-header: linear-gradient(180deg, #394555, #1f2e40);
56
- --theme-accent-bar: linear-gradient(180deg, var(--octo-mint), var(--neo-cyan));
57
- --theme-accent-line: linear-gradient(90deg, var(--octo-mint), var(--neo-cyan), transparent);
58
- --theme-panel-rule: linear-gradient(90deg, var(--octo-mint), var(--neo-cyan), var(--royal-violet));
59
-
60
- // Status (semantic)
61
- --theme-status-success: #37b400;
62
- --theme-status-warning: #da9162;
63
- --theme-status-error: #ec658f;
64
- --theme-status-info: #00a8dc;
65
-
66
- // Chart palette (8 series)
85
+ --theme-text-code: #c6d2de; // (new)
86
+
87
+ // Borders — neutral hairlines, not mint
88
+ --theme-border-subtle: rgba(200, 214, 229, 0.08);
89
+ --theme-border-default: rgba(200, 214, 229, 0.14); // (new) panel/card edges, inputs
90
+ --theme-border-strong: rgba(200, 214, 229, 0.26);
91
+ --theme-border-divider: var(--theme-border-subtle); // kept for compatibility
92
+ --theme-focus-ring: 0 0 0 2px #07172b, 0 0 0 4px #64ceb9; // (new) box-shadow value
93
+
94
+ // Accent & brand roles (new)
95
+ --theme-accent: #64ceb9;
96
+ --theme-accent-hover: #7fd8c6;
97
+ --theme-accent-subtle: rgba(100, 206, 185, 0.12);
98
+ --theme-accent-2: #00a8dc;
99
+ --theme-ai: #8b6fd0;
100
+ --theme-ai-subtle: rgba(139, 111, 208, 0.14);
101
+
102
+ // Effects — no glows; flat surfaces. Tokens kept so component SCSS does not break.
103
+ --theme-glow-primary: none;
104
+ --theme-glow-accent: none;
105
+ --theme-shadow-panel: 0 1px 2px rgba(0, 0, 0, 0.35);
106
+ --theme-shadow-popup: 0 8px 24px rgba(0, 0, 0, 0.45), 0 0 0 1px var(--theme-border-default);
107
+
108
+ // Former gradients, now flat (every consumer uses them as `background:`).
109
+ --theme-gradient-page: var(--theme-bg-app);
110
+ --theme-gradient-header: var(--theme-bg-elevated);
111
+ --theme-accent-bar: var(--theme-accent);
112
+ --theme-accent-line: var(--theme-accent);
113
+ --theme-panel-rule: var(--theme-accent);
114
+
115
+ // Status (semantic) — the only loud colour; re-tuned for navy
116
+ --theme-status-success: #34c98a;
117
+ --theme-status-warning: #e0a060;
118
+ --theme-status-error: #f0708f;
119
+ --theme-status-info: #3fb9e6;
120
+ --theme-status-neutral: #7c8ba1; // (new) unknown/undeployed/paused
121
+ // (new) chip backgrounds, 14 % tints
122
+ --theme-status-success-subtle: color-mix(in srgb, var(--theme-status-success) 14%, transparent);
123
+ --theme-status-warning-subtle: color-mix(in srgb, var(--theme-status-warning) 14%, transparent);
124
+ --theme-status-error-subtle: color-mix(in srgb, var(--theme-status-error) 14%, transparent);
125
+ --theme-status-info-subtle: color-mix(in srgb, var(--theme-status-info) 14%, transparent);
126
+ --theme-status-neutral-subtle: color-mix(in srgb, var(--theme-status-neutral) 14%, transparent);
127
+
128
+ // Chart palette (8 series) — brand colours stay good series colours
67
129
  --theme-chart-1: #64ceb9;
68
130
  --theme-chart-2: #00a8dc;
69
131
  --theme-chart-3: #6c4da8;
@@ -73,24 +135,21 @@
73
135
  --theme-chart-7: #546fbd;
74
136
  --theme-chart-8: #9292a6;
75
137
 
76
- // Kendo bridge
138
+ // Kendo bridge (theme-specific part; the rest is in kendo-bridge below)
77
139
  --kendo-color-on-base: #c8cdd3;
78
- --kendo-color-on-primary: #07172b;
79
140
  --kendo-color-base-on-surface: #c8cdd3;
80
141
 
81
142
  // Kendo's Material default --kendo-color-base is the Ash-Blue #9292a6,
82
- // which lands as a washed-out medium-grey on the LCARS dark surfaces and
83
- // is the source of the low-contrast "Copy ID" / kendo-dropdownbutton look
84
- // (white-ish text on light-ish grey, hard to read against the deep-sea bg).
85
- // Repoint --kendo-color-base and its subtle/emphasis siblings at the LCARS
86
- // surface palette so every solid-base button (kendo-dropdownbutton, menu
87
- // buttons, .k-button-solid-base) blends with the surrounding panels while
88
- // keeping enough contrast against the page background.
89
- --kendo-color-base: #394555; // = iron-navy / theme-bg-surface
90
- --kendo-color-base-subtle: #1f2e40; // = surface-elevated / theme-bg-elevated
91
- --kendo-color-base-emphasis: #4a5970; // one notch brighter for hover/active
92
- --kendo-color-base-hover: #475569; // explicit hover override (Material derives it from --base otherwise, which Kendo computes as still-too-light)
93
- --kendo-color-base-active: #4a5970;
143
+ // which lands as a washed-out medium-grey on the dark surfaces (white-ish
144
+ // text on light-ish grey). Repoint --kendo-color-base and its siblings at
145
+ // the Deep Sea surface ladder so every solid-base button
146
+ // (kendo-dropdownbutton, menu buttons, .k-button-solid-base) blends with
147
+ // the surrounding panels while keeping contrast against the page.
148
+ --kendo-color-base: #182d4a; // = theme-bg-overlay
149
+ --kendo-color-base-subtle: #122540; // = theme-bg-elevated
150
+ --kendo-color-base-emphasis: #22395a; // one notch brighter for hover/active
151
+ --kendo-color-base-hover: #22395a; // explicit (Material would derive a still-too-light value)
152
+ --kendo-color-base-active: #2a4366;
94
153
  }
95
154
 
96
155
  // ----------------------------------------------------------------------------
@@ -98,44 +157,60 @@
98
157
  // query and the explicit [data-theme="light"] override).
99
158
  // ----------------------------------------------------------------------------
100
159
  @mixin light-theme-tokens {
101
- // The page needs to sit BELOW the cards, or there is no depth at all:
102
- // #ffffff on #f3f6fa measured 1.08:1 — card and page were the same surface.
103
- // #e9edf3 puts it at 1.35:1, which is what the dark theme has (1.85:1).
104
- --theme-bg-app: #e9edf3;
105
- --theme-bg-app-end: #e9edf3;
160
+ // Cards are white on a slightly sunken canvas; the edge is carried by
161
+ // --theme-border-default plus --theme-shadow-panel, not by the fill alone.
162
+ --theme-bg-app: #f4f6f9;
163
+ --theme-bg-app-end: #f4f6f9;
106
164
  --theme-bg-surface: #ffffff;
107
- --theme-bg-elevated: #f4f7fa;
108
- --theme-bg-overlay: rgba(255, 255, 255, 0.92);
165
+ --theme-bg-elevated: #ffffff;
166
+ --theme-bg-overlay: #ffffff;
109
167
  --theme-bg-input: #ffffff;
168
+ --theme-bg-sunken: #e9edf3;
169
+ --theme-bg-selected: color-mix(in srgb, var(--theme-accent) 12%, transparent);
170
+ --theme-bg-hover: var(--theme-ink-04);
110
171
 
111
- --theme-text-primary: #07172b;
112
- --theme-text-secondary: #4b576b;
113
- --theme-text-muted: #6b7689;
172
+ --theme-text-primary: #0f1c2e;
173
+ --theme-text-secondary: #4b5a6e;
174
+ --theme-text-muted: #657189; // concept §6.2 #75829a (3.9:1) darkened to ≥ 4.5:1 on bg-input/app
114
175
  --theme-text-on-accent: #ffffff;
115
- --theme-text-accent: #2e8473;
176
+ --theme-text-accent: #2c7d6d; // concept §6.2 #2e8473 (4.16:1 on bg-app) darkened to ≥ 4.5:1
177
+ --theme-text-code: #1f2d40;
116
178
 
117
179
  // Neutral, not mint. A mint hairline measures 1.12:1 on a light ground —
118
180
  // it is not a subtle border, it is an invisible one.
119
- --theme-border-subtle: rgba(7, 23, 43, 0.12);
181
+ --theme-border-subtle: rgba(7, 23, 43, 0.08);
182
+ --theme-border-default: rgba(7, 23, 43, 0.12);
120
183
  --theme-border-strong: rgba(7, 23, 43, 0.22);
121
- --theme-border-divider: rgba(7, 23, 43, 0.1);
184
+ --theme-border-divider: var(--theme-border-subtle);
185
+ --theme-focus-ring: 0 0 0 2px #ffffff, 0 0 0 4px #2c7d6d;
186
+
187
+ --theme-accent: #2c7d6d;
188
+ --theme-accent-hover: #24685a; // concept #266f61, shifted with the accent
189
+ --theme-accent-subtle: rgba(44, 125, 109, 0.1);
190
+ --theme-accent-2: #0277a6;
191
+ --theme-ai: #6c4da8;
192
+ --theme-ai-subtle: rgba(108, 77, 168, 0.1);
122
193
 
123
- // A glow needs a dark room. On a light ground it only muddies the edge.
124
194
  --theme-glow-primary: none;
125
195
  --theme-glow-accent: none;
126
- --theme-shadow-panel: 0 2px 8px rgba(7, 23, 43, 0.08);
127
- --theme-shadow-popup: 0 8px 24px rgba(7, 23, 43, 0.15);
128
-
129
- --theme-gradient-page: linear-gradient(180deg, #f3f6fa, #ffffff);
130
- --theme-gradient-header: linear-gradient(180deg, #ffffff, #eef2f7);
131
- --theme-accent-line: linear-gradient(90deg, #2e8473, #0288b8, transparent);
132
- // accent-bar and panel-rule keep the brand gradient (defined in the dark
133
- // block, which always runs first, and deliberately not redefined here).
134
-
135
- --theme-status-success: #2e8b00;
136
- --theme-status-warning: #b56b3c;
137
- --theme-status-error: #c63d6a;
196
+ --theme-shadow-panel: 0 1px 2px rgba(7, 23, 43, 0.08);
197
+ --theme-shadow-popup: 0 8px 24px rgba(7, 23, 43, 0.14), 0 0 0 1px var(--theme-border-default);
198
+
199
+ // The flat gradient tokens (page/header/accent-bar/accent-line/panel-rule)
200
+ // are not redeclared: they reference bg-app/bg-elevated/accent, which are
201
+ // substituted on :root, so they already follow the light values.
202
+
203
+ --theme-status-success: #1f8a5b;
204
+ --theme-status-warning: #a9622f;
205
+ --theme-status-error: #c0385f;
138
206
  --theme-status-info: #0277a6;
207
+ --theme-status-neutral: #6b7a90;
208
+ // 10 % tints on a light ground
209
+ --theme-status-success-subtle: color-mix(in srgb, var(--theme-status-success) 10%, transparent);
210
+ --theme-status-warning-subtle: color-mix(in srgb, var(--theme-status-warning) 10%, transparent);
211
+ --theme-status-error-subtle: color-mix(in srgb, var(--theme-status-error) 10%, transparent);
212
+ --theme-status-info-subtle: color-mix(in srgb, var(--theme-status-info) 10%, transparent);
213
+ --theme-status-neutral-subtle: color-mix(in srgb, var(--theme-status-neutral) 10%, transparent);
139
214
 
140
215
  --theme-chart-1: #2e8473;
141
216
  --theme-chart-2: #0277a6;
@@ -146,56 +221,50 @@
146
221
  --theme-chart-7: #3f5394;
147
222
  --theme-chart-8: #6b7689;
148
223
 
149
- --kendo-color-on-base: #07172b;
150
- --kendo-color-on-primary: #ffffff;
151
- --kendo-color-base-on-surface: #07172b;
224
+ --kendo-color-on-base: #0f1c2e;
225
+ --kendo-color-base-on-surface: #0f1c2e;
152
226
 
153
- // Light-theme counterparts to the LCARS dark-theme overrides above.
154
- // Light buttons should sit on a pale neutral that reads against the
155
- // off-white app background without disappearing into it.
156
- --kendo-color-base: #eef2f7; // = theme-bg-elevated (light)
157
- --kendo-color-base-subtle: #f3f6fa; // = theme-bg-app (lightest)
227
+ // Light buttons sit on a pale neutral that reads against the canvas
228
+ // without disappearing into it.
229
+ --kendo-color-base: #eef2f7;
230
+ --kendo-color-base-subtle: #f4f6f9; // = theme-bg-app
158
231
  --kendo-color-base-emphasis: #d8e0ea; // one notch darker for hover/active
159
232
  --kendo-color-base-hover: #d8e0ea;
160
233
  --kendo-color-base-active: #c8d2dd;
161
234
  }
162
235
 
163
236
  // ----------------------------------------------------------------------------
164
- // Light-theme button contrast — the library's primary/base button gradients
165
- // (mint at 15–30% alpha) read fine on dark surfaces but wash out on white,
166
- // leaving confirmation-dialog buttons hard to tell apart and the secondary
167
- // button looking disabled. Apply solid theme-token fills in light mode.
237
+ // Light-theme buttons (AB#5526).
168
238
  //
169
- // Dialog/window action buttons are styled by the lcars-button mixin scoped to
170
- // `.k-dialog .k-dialog-actions` (and friends), which has specificity (0,4,0).
171
- // These selectors have to match or beat that — they land at (0,5,1).
239
+ // Buttons are flat and token-driven in both themes now (_button.scss and
240
+ // the `.k-button` block in _styles.scss: primary = --theme-accent with
241
+ // --theme-text-on-accent, base = --theme-bg-elevated with a neutral border),
242
+ // so the light theme needs no separate treatment. The mixins stay — they are
243
+ // forwarded by the styles entry point — and emit the same flat token rules,
244
+ // which also keeps dialog action rows (specificity (0,4,0) from the
245
+ // octo-button mixin) and page buttons identical.
172
246
  // ----------------------------------------------------------------------------
173
247
  @mixin light-theme-button-primary {
174
- background: var(--theme-text-accent);
175
- border-color: var(--theme-text-accent);
176
- color: #ffffff;
248
+ background: var(--theme-accent);
249
+ border-color: var(--theme-accent);
250
+ color: var(--theme-text-on-accent);
177
251
 
178
252
  .k-svg-icon,
179
253
  .k-icon,
180
254
  svg {
181
- fill: #ffffff;
182
- color: #ffffff;
255
+ fill: var(--theme-text-on-accent);
256
+ color: var(--theme-text-on-accent);
183
257
  }
184
258
 
185
259
  &:hover:not(:disabled) {
186
- background: color-mix(in srgb, var(--theme-text-accent) 88%, black);
187
- border-color: color-mix(in srgb, var(--theme-text-accent) 88%, black);
188
- box-shadow: var(--theme-glow-accent);
189
- }
190
-
191
- &:active:not(:disabled) {
192
- background: color-mix(in srgb, var(--theme-text-accent) 75%, black);
260
+ background: var(--theme-accent-hover);
261
+ border-color: var(--theme-accent-hover);
193
262
  }
194
263
  }
195
264
 
196
265
  @mixin light-theme-button-base {
197
266
  background: var(--theme-bg-elevated);
198
- border-color: var(--theme-border-strong);
267
+ border-color: var(--theme-border-default);
199
268
  color: var(--theme-text-primary);
200
269
 
201
270
  .k-svg-icon,
@@ -206,191 +275,47 @@
206
275
  }
207
276
 
208
277
  &:hover:not(:disabled) {
209
- background: color-mix(in srgb, var(--theme-text-accent) 12%, var(--theme-bg-elevated));
210
- border-color: var(--theme-text-accent);
211
- }
212
-
213
- &:active:not(:disabled) {
214
- background: color-mix(in srgb, var(--theme-text-accent) 20%, var(--theme-bg-elevated));
278
+ background: var(--theme-bg-hover);
279
+ border-color: var(--theme-border-strong);
215
280
  }
216
281
  }
217
282
 
218
283
  // ----------------------------------------------------------------------------
219
- // Light-theme structure.
220
- //
221
- // The LCARS palette is built to glow, and glowing means light. Ported to a
222
- // light ground by inverting lightness alone, every role it plays collapses:
223
- // octo-mint carries 9.49:1 as text on #07172b and 1.75:1 on #f3f6fa, and the
224
- // mint tints that draw every panel edge fall from 1.53:1 to 1.12:1.
284
+ // Light-theme structure (reworked in AB#5526, concept §6.5).
225
285
  //
226
- // So the ROLE changes with the theme, not just the value: in dark, colour is
227
- // the figure — thin bright lines and glowing text. In light, colour becomes
228
- // the ground — solid brand blocks carrying dark text, structure carried by
229
- // surface elevation and neutral rules. That is also closer to real LCARS,
230
- // which is built from coloured blocks rather than hairlines. The proof is in
231
- // the same number: #07172b on #64ceb9 is again 9.49:1 — one pair, two roles.
286
+ // Dark and light now follow the same rules — surfaces carry structure,
287
+ // borders are neutral hairlines, no glows — so the former light-only card,
288
+ // table band (#d7ebe5), drawer and app-bar treatments were deleted: the
289
+ // token values handle both themes. What is left is only what the tokens
290
+ // cannot express.
232
291
  // ----------------------------------------------------------------------------
233
292
  @mixin light-theme-surface-overrides {
234
- // Cards lift off the ground again.
235
- .k-grid,
236
- .lcars-content-panel,
237
- .lcars-panel {
238
- background: #ffffff;
239
- border: 1px solid rgba(7, 23, 43, 0.1);
240
- box-shadow: 0 1px 3px rgba(7, 23, 43, 0.07);
241
- }
242
-
243
- // A grid inside a content panel must not become a second card: the panel
244
- // already supplies the border, the radius and the surface. Two nested cards
245
- // is what left the toolbar's rounded top corners floating inside a white
246
- // sheet, with a transparent filter strip wedged between them.
247
- .lcars-content-panel .k-grid {
248
- border: none;
249
- border-radius: 0;
250
- box-shadow: none;
251
- }
252
-
253
- // MeshBoard tiles are cards too. The shared tile rules paint them with the
254
- // dark theme's translucent surfaces (tile --surface-elevated-60, body
255
- // --deep-sea-50), which on the light page resolve to two greys on grey: a
256
- // chart tile read as a tinted slab while a table tile looked white only
257
- // because the grid inside paints its own white. One treatment for every
258
- // widget, the same as the other cards above; the !important matches the
259
- // base rules', which would otherwise keep winning.
260
- kendo-tilelayout kendo-tilelayout-item {
261
- background: #ffffff !important;
262
- border: 1px solid rgba(7, 23, 43, 0.1) !important;
263
- box-shadow: 0 1px 3px rgba(7, 23, 43, 0.07);
264
-
265
- &:hover {
266
- border-color: rgba(7, 23, 43, 0.18) !important;
267
- box-shadow: 0 2px 8px rgba(7, 23, 43, 0.1);
268
- }
269
-
270
- kendo-tilelayout-item-body {
271
- background: #ffffff !important;
272
- }
273
- }
274
-
275
- // Table chrome. In dark the header/toolbar/pager band is LIGHTER than the
276
- // data; on a light ground it has to be darker, or the table loses its frame
277
- // and reads as a borderless white slab.
278
- .k-grid .k-grid-header,
279
- .k-grid-toolbar,
280
- .k-toolbar,
281
- .k-pager,
282
- .k-grid-pager {
283
- background: #d7ebe5;
284
- border-color: rgba(46, 132, 115, 0.3);
285
- }
286
-
287
- .k-grid .k-grid-header {
288
- border-bottom: 2px solid rgba(46, 132, 115, 0.45);
289
- }
290
-
291
- .k-pager,
292
- .k-grid-pager {
293
- border-top: 1px solid rgba(46, 132, 115, 0.3);
294
- }
295
-
296
- .k-grid .k-grid-content {
297
- background: #ffffff;
293
+ // MeshBoard tiles keep their !important surfaces from styles(); restate
294
+ // the hairline hover so the light canvas does not get the dark shadow.
295
+ kendo-tilelayout kendo-tilelayout-item:hover {
296
+ box-shadow: var(--theme-shadow-panel);
298
297
  }
298
+ }
299
299
 
300
- // Uppercase plus letter-spacing costs width and legibility in a dense table,
301
- // and mint column headers are unreadable on a light band anyway.
302
- .k-grid .k-grid-header th {
303
- color: var(--theme-text-primary);
304
- text-transform: none;
305
- letter-spacing: 0;
306
- font-weight: 600;
307
- }
300
+ // Kept as a no-op for hosts that include it directly: dark and light buttons
301
+ // are identical now (see above).
302
+ @mixin light-theme-button-overrides {
303
+ // intentionally empty
304
+ }
308
305
 
306
+ // ----------------------------------------------------------------------------
307
+ // Sentence case in both themes (concept §6.3: no uppercase + letter-spacing
308
+ // labels). The Refinery Studio sets no uppercase any more (AB#5526); the
309
+ // Meshmakers App's component stylesheets still do on labels and section
310
+ // titles, and `html:root` (0,2,1) outranks their (0,2,0) attribute-scoped
311
+ // selectors. Remove this block once the Meshmakers App is migrated.
312
+ // ----------------------------------------------------------------------------
313
+ @mixin sentence-case-overrides {
309
314
  .k-label,
310
315
  kendo-label,
311
316
  .section-title {
312
317
  text-transform: none;
313
- letter-spacing: 0;
314
- }
315
-
316
- // Brand as block, never as ink.
317
- .lcars-header-accent {
318
- background: var(--octo-mint);
319
- border-radius: 4px;
320
- box-shadow: none;
321
- }
322
-
323
- .page-title .title-prefix {
324
- background: var(--octo-mint);
325
- color: #07172b;
326
- padding: 3px 10px;
327
- border-radius: 3px;
328
- font-weight: 700;
329
- }
330
-
331
- .page-title .title-main {
332
- color: var(--theme-text-primary);
333
- }
334
-
335
- .lcars-header-line {
336
- background: rgba(7, 23, 43, 0.1);
337
- height: 1px;
338
- }
339
-
340
- .k-drawer .k-drawer-item.k-selected {
341
- background: var(--octo-mint);
342
- color: #07172b;
343
- border-left: none;
344
- font-weight: 600;
345
-
346
- // The count badge is mint-on-mint once the row itself is a mint block.
347
- .drawer-badge {
348
- color: #07172b;
349
- background: rgba(255, 255, 255, 0.8);
350
- border-color: rgba(7, 23, 43, 0.18);
351
- }
352
- }
353
-
354
- // The app bar carried four treatments on one edge: a 1px mint border
355
- // (1.18:1 — invisible), the 2px gradient ::after the host draws, a
356
- // dark-theme drop shadow, and the host's breadcrumb wash. Keep the gradient
357
- // as the single statement.
358
- .k-appbar {
359
- border-bottom: none;
360
- box-shadow: 0 1px 2px rgba(7, 23, 43, 0.08);
361
- }
362
-
363
- // Decoration that carries the theme in dark and carries nothing in light.
364
- .lcars-status-bar,
365
- .lcars-footer .footer-bar {
366
- display: none;
367
- }
368
- }
369
-
370
- @mixin light-theme-button-overrides {
371
- // Dialog action buttons (kendo-dialog-actions used by ConfirmationService etc.)
372
- .k-dialog .k-dialog-actions,
373
- .k-window > .k-actions {
374
- .k-button.k-button-solid-primary,
375
- .k-button.k-button-primary,
376
- .k-button[themecolor="primary"] {
377
- @include light-theme-button-primary;
378
- }
379
-
380
- // Base/default button (the "No"/"Cancel"/secondary action)
381
- .k-button:not(.k-button-primary):not(.k-button-solid-primary):not([themecolor="primary"]):not(.k-button-flat):not(.k-button-outline):not([themecolor="error"]) {
382
- @include light-theme-button-base;
383
- }
384
- }
385
-
386
- // General-purpose primary/base buttons outside dialogs (top-level scope so
387
- // host-app pages get the same emphasis without each page having to opt in)
388
- .k-button.k-button-solid-primary {
389
- @include light-theme-button-primary;
390
- }
391
-
392
- .k-button.k-button-solid-base {
393
- @include light-theme-button-base;
318
+ letter-spacing: normal;
394
319
  }
395
320
  }
396
321
 
@@ -409,15 +334,39 @@
409
334
  // :root custom properties covers both entry points and, unlike a compiled
410
335
  // $kendo-colors merge, follows the active theme at runtime.
411
336
  //
412
- // Values deliberately resolve to what the slim theme already produced, so this
413
- // changes nothing for hosts that were already on it.
337
+ // Surface/border/primary colours, the radius scale and the font family all
338
+ // point at the Deep Sea tokens (concept §6.6 step 2), so most of the Kendo
339
+ // re-skin happens here without selector overrides.
414
340
  // ----------------------------------------------------------------------------
415
341
  @mixin kendo-bridge {
416
342
  --kendo-color-surface: var(--theme-bg-surface);
417
343
  --kendo-color-surface-alt: var(--theme-bg-elevated);
418
344
  --kendo-color-on-surface: var(--kendo-color-base-on-surface);
419
345
  --kendo-color-subtle: var(--theme-text-secondary);
420
- --kendo-color-border: var(--theme-border-divider);
346
+ --kendo-color-border: var(--theme-border-default);
347
+
348
+ // Primary = the Deep Sea accent (mint in dark, darkened teal in light).
349
+ // Overrides the mint that `variables()` sets for hosts without theme().
350
+ --kendo-color-primary: var(--theme-accent);
351
+ --kendo-color-primary-hover: var(--theme-accent-hover);
352
+ --kendo-color-primary-active: var(--theme-accent-hover);
353
+ --kendo-color-on-primary: var(--theme-text-on-accent);
354
+
355
+ // Radius: symmetric edges from the --theme-radius-* scale. Kendo Material
356
+ // uses sm for chips, md for buttons/inputs and lg for cards/dialogs; the
357
+ // larger steps are capped at the dialog radius ("edges, not pills" —
358
+ // `full` stays for switches and badges, which are round by nature).
359
+ --kendo-border-radius-base: var(--theme-radius-sm);
360
+ --kendo-border-radius-xs: var(--theme-radius-xs);
361
+ --kendo-border-radius-sm: var(--theme-radius-xs);
362
+ --kendo-border-radius-md: var(--theme-radius-sm);
363
+ --kendo-border-radius-lg: var(--theme-radius-lg);
364
+ --kendo-border-radius-xl: var(--theme-radius-lg);
365
+ --kendo-border-radius-xxl: var(--theme-radius-lg);
366
+ --kendo-border-radius-xxxl: var(--theme-radius-lg);
367
+
368
+ // Typography: Kendo components use the UI face (Roboto).
369
+ --kendo-font-family: var(--theme-font-ui);
421
370
 
422
371
  // Kendo derives every `*-on-subtle` ink from its own `*-subtle` surface,
423
372
  // assuming that surface is light. We repoint --kendo-color-base-subtle at a
@@ -463,6 +412,7 @@
463
412
  @mixin theme {
464
413
  :root {
465
414
  @include vars.variables;
415
+ @include foundation-tokens;
466
416
  @include dark-theme-tokens;
467
417
  @include kendo-bridge;
468
418
  @include ink-overlays;
@@ -479,28 +429,38 @@
479
429
  :root[data-theme="light"] {
480
430
  @include light-theme-tokens;
481
431
  }
432
+
433
+ // ----- Reduced motion: every token-driven transition becomes instant -----
434
+ @media (prefers-reduced-motion: reduce) {
435
+ :root {
436
+ --theme-motion-fast: 0ms;
437
+ --theme-motion-base: 0ms;
438
+ }
439
+ }
482
440
  }
483
441
 
484
442
  // ----------------------------------------------------------------------------
485
443
  // Public entry point: emits the light-theme button overrides.
486
444
  //
487
- // Include this in a host's GLOBAL stylesheet, AFTER `styles()` — the lcars
488
- // button mixin styles dialog action buttons at specificity (0,4,0), so these
445
+ // Include this in a host's GLOBAL stylesheet, AFTER `styles()` — the octo-button
446
+ // mixin styles dialog action buttons at specificity (0,4,0), so these
489
447
  // rules must both outrank it and come later in source order. Deliberately not
490
448
  // part of `styles()`: that mixin is also included from component stylesheets,
491
449
  // where :root-scoped rules do not belong and would inflate every component's
492
450
  // style budget.
493
451
  // ----------------------------------------------------------------------------
494
452
  @mixin theme-overrides {
453
+ html:root {
454
+ @include sentence-case-overrides;
455
+ }
456
+
495
457
  @media (prefers-color-scheme: light) {
496
458
  :root:not([data-theme="dark"]) {
497
459
  @include light-theme-surface-overrides;
498
- @include light-theme-button-overrides;
499
460
  }
500
461
  }
501
462
 
502
463
  :root[data-theme="light"] {
503
464
  @include light-theme-surface-overrides;
504
- @include light-theme-button-overrides;
505
465
  }
506
466
  }