@tangle-network/sandbox-ui 0.99.2 → 0.100.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.
package/dist/tokens.css CHANGED
@@ -1,10 +1,10 @@
1
1
  /* Tangle design tokens — the single palette every Tangle surface renders from.
2
2
  *
3
- * The surface ladder carries an indigo cast and steps in even ~4-5% lightness
4
- * increments, so a card, a nested panel and an overlay each read as a distinct
5
- * plane by their own fill. A flat, desaturated ladder forces separation onto
6
- * borders instead, which is what makes an app read as grey and washed out.
7
- * Light mirrors it: white paper on a tinted canvas, never white-on-white.
3
+ * The surface ladder is NEUTRAL GREY and the indigo is trim — accent, never
4
+ * field. It steps in even lightness increments so a card, a nested panel and an
5
+ * overlay each read as a distinct plane by their own fill, and carries a crisp
6
+ * hairline (1.6:1 against the card in dark) because shadow does no work on a dark
7
+ * canvas. Light mirrors it: white paper on a grey canvas, never white-on-white.
8
8
  *
9
9
  * Apps consume this spine as-is. If a product needs its own accent identity,
10
10
  * add a named theme in named-themes.css (`data-theme="<name>"`) — do NOT re-declare
@@ -14,25 +14,47 @@
14
14
  /* Dark is the default theme (`:root`/`[data-sandbox-ui]`). It also responds to
15
15
  * the standard `[data-theme="dark"]` / `.dark` switch so any consumer can pin or
16
16
  * toggle a theme without inventing an ad-hoc `data-sandbox-theme` name. */
17
+ /*
18
+ * Surfaces are NEUTRAL GREY and the indigo is trim.
19
+ *
20
+ * Colour in the surface competes with colour in the accent: on an indigo-cast
21
+ * ladder the indigo nav pill, link and focus ring are one more indigo thing on
22
+ * an indigo page, and the eye stops finding them. Neutral planes give the brand
23
+ * a field to sit on, so a single accent does all the signalling.
24
+ *
25
+ * The canvas sits OFF pure black (#151523). A near-black page under near-white
26
+ * ink is the glare end of the range, and it is the reading surface for long
27
+ * sessions.
28
+ *
29
+ * Elevation is carried by surface lightness plus a hairline that can actually be
30
+ * seen — a black shadow on a dark canvas renders as nothing, so shadow does no
31
+ * work here. The hairline is 1.6:1 against the resting card — quiet by design,
32
+ * enough to state an edge without drawing a box around every panel.
33
+ */
17
34
  :root,
18
35
  [data-sandbox-ui],
19
36
  [data-theme="dark"],
20
37
  .dark {
21
- --md3-surface: #0a0a14; /* L0 page canvas */
22
- --md3-surface-dim: #060610; /* recessed page */
23
- --md3-surface-bright: #232238;
24
- --md3-surface-container-lowest: #050509; /* L-1 terminal/code well */
25
- --md3-surface-container-low: #12111f; /* L1 chrome, field wells */
26
- --md3-surface-container: #191826; /* L2 resting card */
27
- --md3-surface-container-high: #221f33; /* L3 nested / hover */
28
- --md3-surface-container-highest: #2c2942; /* L4 overlay */
29
- --md3-surface-variant: #2c2942;
30
- --md3-on-surface: #fdfdfd;
31
- --md3-on-surface-variant: #aaa8b8;
32
-
33
- --md3-primary: #818CF8;
34
- --md3-primary-dim: #6366F1;
35
- --md3-primary-container: #4F46E5;
38
+ --md3-surface: #151523; /* L0 page canvas */
39
+ --md3-surface-dim: #0f0f1a; /* recessed page */
40
+ --md3-surface-bright: #393957;
41
+ /* Same value as --md3-surface-dim by design, not by drift: both name the floor
42
+ * of the ladder, and dark has no plane below it to give them. They stay two
43
+ * tokens because the roles diverge in light, where the well is paper (#ffffff)
44
+ * and the recessed page is grey (#d9d9e6) — a consumer binding to the wrong one
45
+ * there gets the wrong surface. */
46
+ --md3-surface-container-lowest: #0f0f1a; /* L-1 terminal/code well */
47
+ --md3-surface-container-low: #222235; /* L1 chrome, field wells */
48
+ --md3-surface-container: #2d2e43; /* L2 resting card */
49
+ --md3-surface-container-high: #32324e; /* L3 nested / hover */
50
+ --md3-surface-container-highest: #393957; /* L4 overlay */
51
+ --md3-surface-variant: #32324e;
52
+ --md3-on-surface: #e3e6ed;
53
+ --md3-on-surface-variant: #b2b4c7;
54
+
55
+ --md3-primary: #A5B4FC;
56
+ --md3-primary-dim: #7C6CF6;
57
+ --md3-primary-container: #4C3FC5;
36
58
  --md3-on-primary: #ffffff;
37
59
  --md3-on-primary-container: #E0E7FF;
38
60
  --md3-primary-fixed: #A5AAFC;
@@ -45,8 +67,8 @@
45
67
  --md3-on-secondary-container: #E8E5FF;
46
68
 
47
69
  --md3-tertiary: #10B981;
48
- --md3-outline: #4a4965;
49
- --md3-outline-variant: #2a293d;
70
+ --md3-outline: #898da9;
71
+ --md3-outline-variant: #474a6b;
50
72
 
51
73
  --md3-error: #ff5277;
52
74
  --md3-error-dim: #eb2855;
@@ -55,32 +77,46 @@
55
77
  --md3-on-error-container: #ffbdf2;
56
78
 
57
79
  --md3-inverse-surface: #fdfdfd;
58
- --md3-inverse-primary: #4F46E5;
80
+ --md3-inverse-primary: #4C3FC5;
59
81
  --md3-inverse-on-surface: #000000;
60
- --md3-surface-tint: #818CF8;
61
-
62
- /* Semantic HSL bridge — the indigo-cast surface ladder */
63
- --hsl-background: 240 33% 6%; /* L0 canvas #0a0a14 */
64
- --hsl-foreground: 240 6% 93%; /* #ececee */
65
- --hsl-card: 244 23% 12%; /* L2 card #191826 */
66
- --hsl-card-foreground: 240 6% 93%;
67
- --hsl-popover: 247 23% 21%; /* L4 overlay #2c2942 */
68
- --hsl-popover-foreground: 240 6% 93%;
69
- --hsl-primary: 239 84% 62%;
82
+ --md3-surface-tint: #A5B4FC;
83
+
84
+ /* Semantic HSL bridge — the neutral-grey surface ladder */
85
+ --hsl-background: 240 24% 11%; /* L0 canvas #151523 */
86
+ /* 92% L, not pure white: near-white ink on the canvas is the glare end of the
87
+ * range, and the tiers below it need room to separate. 91% holds 10.6:1 on the
88
+ * card — AAA with headroom — and leaves four ink steps the eye can tell apart. */
89
+ --hsl-foreground: 225 22% 91%; /* #e3e6ed */
90
+ --hsl-card: 238 20% 22%; /* L2 card #2d2e43 */
91
+ --hsl-card-foreground: 225 22% 91%;
92
+ --hsl-popover: 239 22% 25%; /* L3 #32324e — the NESTED plane despite
93
+ the name; the overlay plane is --md3-surface-container-highest. A popover
94
+ carries body text, and the ladder above L3 is reserved for planes that lift
95
+ by shadow, so it sits on L3 and the ink ramp reaches it. */
96
+ --hsl-popover-foreground: 225 22% 91%;
97
+ /* The fill indigo, NOT the bright one: `.btn-primary` puts white on this, and
98
+ * #6366F1 carries white at only 4.47:1. This clears the floor at 7.4:1. The
99
+ * bright indigo stays available for accent TEXT via --accent-text. */
100
+ --hsl-primary: 246 54% 51%; /* #4c3fc6 */
70
101
  --hsl-primary-foreground: 0 0% 100%;
71
- --hsl-secondary: 249 24% 16%; /* L3 #221f33 */
72
- --hsl-secondary-foreground: 240 6% 93%;
73
- --hsl-muted: 249 24% 16%; /* L3 hover well */
74
- --hsl-muted-foreground: 240 5% 68%; /* #a9a9b1 — high enough that the
102
+ --hsl-secondary: 239 22% 25%; /* L3 #32324e */
103
+ --hsl-secondary-foreground: 225 22% 91%;
104
+ --hsl-muted: 239 22% 25%; /* L3 hover well */
105
+ --hsl-muted-foreground: 234 16% 74%; /* #b2b4c7 — high enough that the
75
106
  reduced-opacity caption derivatives (/70, /60) still clear the large-text
76
107
  contrast floor against the L2 card. */
77
- --hsl-accent: 249 24% 16%; /* neutral hover (indigo lives in accent-surface-strong) */
78
- --hsl-accent-foreground: 240 6% 93%;
108
+ --hsl-accent: 239 22% 25%; /* neutral hover (indigo lives in accent-surface-strong) */
109
+ --hsl-accent-foreground: 225 22% 91%;
79
110
  --hsl-destructive: 348 74% 48%;
80
111
  --hsl-destructive-foreground: 0 0% 100%;
81
- --hsl-border: 243 19.6% 20%; /* #2a293d = --md3-outline-variant */
82
- --hsl-input: 244 29% 9%; /* recessed field well #12111f */
83
- --hsl-ring: 239 84% 67%;
112
+ /* Quiet on purpose: 1.6:1 against the resting card. Shadow does no work on a
113
+ * dark canvas, so the hairline is what states where a plane ends. */
114
+ --hsl-border: 236 20% 35%; /* #474a6b = --md3-outline-variant */
115
+ --hsl-input: 240 24% 15%; /* recessed field well */
116
+ /* Brighter than --primary on purpose: the ring is NON-TEXT and has to clear
117
+ * 3:1 against BOTH canvas and card. The fill indigo is tuned to carry white
118
+ * text instead, so the two cannot be the same value. */
119
+ --hsl-ring: 233 100% 87%;
84
120
  --hsl-success: 160 84% 39%;
85
121
  --hsl-warning: 40 94% 56%;
86
122
  --hsl-info: 200 88% 56%;
@@ -115,26 +151,26 @@
115
151
  --error: var(--hsl-destructive);
116
152
  --info: var(--hsl-info);
117
153
 
118
- /* Sidebar tokens — L1 chrome: one step ABOVE the canvas in lightness, so the
119
- * panel reads as its own plane rather than a hole in the page. */
120
- --sidebar-background: 244 29% 9%; /* L1 chrome #12111f */
121
- --sidebar-foreground: 240 6% 93%;
122
- --sidebar-primary: 239 84% 67%;
154
+ /* Sidebar tokens — L1 chrome: one step BELOW the canvas here, so the nav reads
155
+ * as chrome the content sits in front of rather than as another card. */
156
+ --sidebar-background: 242 26% 8%; /* L1 chrome #0f0f1a */
157
+ --sidebar-foreground: 225 22% 91%;
158
+ --sidebar-primary: 246 54% 51%;
123
159
  --sidebar-primary-foreground: 0 0% 100%;
124
- --sidebar-accent: 249 24% 16%; /* L3 hover well */
125
- --sidebar-accent-foreground: 240 6% 93%;
126
- --sidebar-border: 243 19.6% 20%; /* = --md3-outline-variant */
127
- --sidebar-ring: 239 84% 67%;
160
+ --sidebar-accent: 239 22% 25%; /* L3 hover well */
161
+ --sidebar-accent-foreground: 225 22% 91%;
162
+ --sidebar-border: 236 20% 35%;
163
+ --sidebar-ring: 233 100% 87%;
128
164
 
129
165
  /* Depth scale — tracks the MD3 ladder (canvas / card / nested / overlay) */
130
- --depth-1: #0a0a14;
131
- --depth-2: #191826;
132
- --depth-3: #221f33;
133
- --depth-4: #2c2942;
166
+ --depth-1: #151523;
167
+ --depth-2: #2d2e43;
168
+ --depth-3: #32324e;
169
+ --depth-4: #393957;
134
170
 
135
171
  /* Status colors */
136
172
  --status-running: #10B981;
137
- --status-creating: #9B93F0;
173
+ --status-creating: #A5B4FC;
138
174
  --status-stopped: #FFB800;
139
175
  --status-warm: #FF8A4C;
140
176
  --status-cold: #4AABFF;
@@ -142,18 +178,18 @@
142
178
  --status-deleted: #6B7094;
143
179
 
144
180
  /* Status surface tokens — bg / border / text for each semantic color */
145
- --surface-success-bg: #022c22;
146
- --surface-success-border: #14532d;
147
- --surface-success-text: #34D399;
148
- --surface-warning-bg: #451a03;
149
- --surface-warning-border: #78350f;
150
- --surface-warning-text: #fbbf24;
151
- --surface-danger-bg: #450a0a;
152
- --surface-danger-border: #7f1d1d;
153
- --surface-danger-text: #f87171;
154
- --surface-info-bg: #082f49;
155
- --surface-info-border: #0c4a6e;
156
- --surface-info-text: #38bdf8;
181
+ --surface-success-bg: #2f5146;
182
+ --surface-success-border: #3c7760;
183
+ --surface-success-text: #5ed7a4;
184
+ --surface-warning-bg: #6a402c;
185
+ --surface-warning-border: #916436;
186
+ --surface-warning-text: #f4c14f;
187
+ --surface-danger-bg: #733b36;
188
+ --surface-danger-border: #9a5b56;
189
+ --surface-danger-text: #ffada9;
190
+ --surface-info-bg: #2d4e67;
191
+ --surface-info-border: #3d7292;
192
+ --surface-info-text: #67cdff;
157
193
  --surface-teal-bg: #042f2e;
158
194
  --surface-teal-border: #134e4a;
159
195
  --surface-teal-text: #2dd4bf;
@@ -163,6 +199,31 @@
163
199
  --surface-orange-bg: #431407;
164
200
  --surface-orange-border: #7c2d12;
165
201
  --surface-orange-text: #fb923c;
202
+
203
+ /* Run-mix ramp — the fills of a proportional status bar.
204
+ *
205
+ * Separate from the `--surface-*-text` tones because a 6px bar is read
206
+ * differently from a chip: it is told by hue alone, at a size too small to
207
+ * carry a glyph or a label, so its slices must ALSO separate from each other
208
+ * in greyscale. These five are spaced at least 1.18:1 apart in relative
209
+ * luminance — 1.30 in dark, 1.20 in light, where the ramp has less room
210
+ * before it runs into the paper — and each clears 3:1 against the track, so
211
+ * the mix stays countable with any form of colour blindness and in a
212
+ * monochrome screenshot. 1.18 is the floor the gate holds them to.
213
+ *
214
+ * Ranked by how much the reader needs to tell them apart: succeeded is
215
+ * brightest and cancelled quietest, so the two that matter — succeeded and
216
+ * failed — sit at opposite ends and can never be confused.
217
+ *
218
+ * Spacing them by OKLCH lightness does NOT work: that is perceptual, and the
219
+ * contrast ratio is computed from relative luminance, so an even perceptual
220
+ * ramp left every pair at 1.1:1. Solved in luminance space instead. */
221
+ --run-mix-succeeded: #76edb9;
222
+ --run-mix-waiting: #e9b642;
223
+ --run-mix-failed: #e1918e;
224
+ --run-mix-in-flight: #2d9bca;
225
+ --run-mix-cancelled: #767d90;
226
+
166
227
  --surface-neutral-bg: var(--depth-3);
167
228
  --surface-neutral-border: var(--border-subtle);
168
229
  --surface-neutral-text: var(--text-muted);
@@ -180,32 +241,46 @@
180
241
  --mesh-blue: transparent;
181
242
 
182
243
  /* Brand gradients — the indigo spark, kept ONLY for the logo mark + send button */
183
- --tangle-gradient: linear-gradient(135deg, #6366F1, #818CF8);
184
- --tangle-gradient-text: linear-gradient(135deg, #A5AAFC, #818CF8);
185
- --accent-gradient-strong: linear-gradient(135deg, #6366F1, #818CF8);
244
+ --tangle-gradient: linear-gradient(135deg, #4C3FC5, #A5B4FC);
245
+ --tangle-gradient-text: linear-gradient(135deg, #C7D2FE, #A5B4FC);
246
+ --accent-gradient-strong: linear-gradient(135deg, #4C3FC5, #7C6CF6);
186
247
  /* Accent wells — soft = hover neutral, strong = active/selected row (indigo) */
187
- --accent-surface-soft: rgba(255, 255, 255, 0.04);
188
- --accent-surface-strong: rgba(129, 140, 248, 0.14);
189
- --accent-text: #b9bffb;
248
+ --accent-surface-soft: rgba(165, 180, 252, 0.07);
249
+ --accent-surface-strong: rgba(76, 63, 197, 0.30);
250
+ /* The BRIGHT indigo, and only ever as text: --primary is the fill (it carries
251
+ * white), and at 6.7:1 on the card this is the one that stays legible as ink. */
252
+ --accent-text: #A5B4FC;
190
253
 
191
254
  /* Surfaces — solid fills off the ladder (no radial mesh) */
192
- --bg-root: #0a0a14;
255
+ --bg-root: #151523;
193
256
  --bg-dark: var(--depth-1);
194
257
  --bg-card: var(--depth-2);
195
258
  --bg-elevated: var(--depth-4);
196
259
  --bg-section: var(--depth-1);
197
260
  --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
198
- --bg-hover: rgba(255, 255, 255, 0.04);
199
- --bg-selection: rgba(129, 140, 248, 0.14);
261
+ --bg-hover: rgba(165, 180, 252, 0.07);
262
+ --bg-selection: rgba(124, 108, 246, 0.28);
200
263
 
201
264
  /* Text — neutral */
202
- --text-primary: #ececee;
203
- --text-secondary: #c8c8cd;
204
- --text-muted: #9a9aa2;
205
- --text-dim: #6b6b73;
265
+ /* Four steps the eye can actually separate: 10.6 / 7.8 / 6.5 / 5.4 against the
266
+ * L2 card. --text-dim is the faintest tier but it is still TEXT, so it clears
267
+ * the 4.5:1 body floor rather than only the 3:1 large-text one.
268
+ *
269
+ * Solved against the WHOLE ladder, not against one plane. A faint tier tuned
270
+ * on the card alone still has to clear the floor on the nested and overlay
271
+ * planes above it, which carry the modals, the drawers and every dropdown
272
+ * menu — so both ends meet in the middle: the faint tiers sit high enough for
273
+ * the overlay, and the planes above the card sit low enough for them. The
274
+ * card, the chrome and the canvas are fixed points. tokens.test.ts asserts
275
+ * every tier on every plane, so a retune cannot open a gap on one plane while
276
+ * looking correct on another. */
277
+ --text-primary: #e3e6ed;
278
+ --text-secondary: #c2c7d4;
279
+ --text-muted: #b2b4c7;
280
+ --text-dim: #9ea5b9;
206
281
 
207
282
  /* Brand accents — Tangle */
208
- --brand-primary: #6366F1;
283
+ --brand-primary: #7C6CF6;
209
284
  --brand-strong: #2E2A5E;
210
285
  --brand-strong-text: #ffffff;
211
286
  --brand-strong-text-muted: rgba(255, 255, 255, 0.7);
@@ -217,17 +292,17 @@
217
292
  --brand-emerald: #10B981;
218
293
 
219
294
  /* Borders — neutral hairlines */
220
- --border-subtle: rgba(255, 255, 255, 0.05);
221
- --border-default: rgba(255, 255, 255, 0.07);
222
- --border-hover: rgba(255, 255, 255, 0.12);
223
- --border-accent: rgba(129, 140, 248, 0.20);
224
- --border-accent-hover: rgba(129, 140, 248, 0.35);
295
+ --border-subtle: rgba(165, 180, 252, 0.10);
296
+ --border-default: rgba(165, 180, 252, 0.16);
297
+ --border-hover: rgba(165, 180, 252, 0.28);
298
+ --border-accent: rgba(129, 140, 248, 0.35);
299
+ --border-accent-hover: rgba(129, 140, 248, 0.50);
225
300
 
226
301
  /* Buttons */
227
- --btn-primary-bg: #5B4ED4;
228
- --btn-primary-hover: #4F46E5;
302
+ --btn-primary-bg: #4C3FC5;
303
+ --btn-primary-hover: #5A50D6;
229
304
  --btn-primary-text: #ffffff;
230
- --btn-cta-bg: #4F46E5;
305
+ --btn-cta-bg: #4C3FC5;
231
306
  --btn-cta-text: #FFFFFF;
232
307
 
233
308
  /* Code / runtime syntax */
@@ -342,17 +417,17 @@
342
417
  * by shadow and ring, not by fill — which is how light-mode elevation actually
343
418
  * reads. Forcing a strictly-darkening ladder here is what produced the
344
419
  * white-on-white flatness this palette replaced. Pinned in tokens.test.ts. */
345
- --md3-surface: #eceef3; /* L0 page canvas — tinted */
346
- --md3-surface-dim: #dcdee6;
420
+ --md3-surface: #e9e9f1; /* L0 page canvas — neutral grey, BELOW the wells */
421
+ --md3-surface-dim: #d9d9e6;
347
422
  --md3-surface-bright: #ffffff;
348
423
  --md3-surface-container-lowest: #ffffff; /* insets read as paper */
349
- --md3-surface-container-low: #f6f7fa; /* L1 chrome, field wells — well */
424
+ --md3-surface-container-low: #f7f7fa; /* L1 chrome, field wells — well */
350
425
  --md3-surface-container: #ffffff; /* L2 resting card — paper */
351
- --md3-surface-container-high: #f1f2f7; /* L3 nested / hover — well */
426
+ --md3-surface-container-high: #f2f2f7; /* L3 nested / hover — well */
352
427
  --md3-surface-container-highest: #ffffff; /* L4 overlay — paper + shadow */
353
- --md3-surface-variant: #f1f2f7;
354
- --md3-on-surface: #191c24;
355
- --md3-on-surface-variant: #464858;
428
+ --md3-surface-variant: #f2f2f7;
429
+ --md3-on-surface: #2b2c3e;
430
+ --md3-on-surface-variant: #494b5d;
356
431
 
357
432
  --md3-primary: #3E349E;
358
433
  --md3-primary-dim: #352B88;
@@ -370,8 +445,8 @@
370
445
 
371
446
  --md3-tertiary: #065F46;
372
447
  --md3-tertiary-container: #D1FAE5;
373
- --md3-outline: #6B6E8A;
374
- --md3-outline-variant: #C7C6D6;
448
+ --md3-outline: #737696;
449
+ --md3-outline-variant: #cecfde;
375
450
 
376
451
  --md3-error: #ba1a1a;
377
452
  --md3-error-dim: #93000a;
@@ -384,26 +459,36 @@
384
459
  --md3-inverse-on-surface: #f0f1f4;
385
460
  --md3-surface-tint: #6366F1;
386
461
 
387
- /* Semantic HSL bridge — white paper on a tinted canvas */
388
- --hsl-background: 223 23% 94%; /* L0 canvas #eceef3 */
389
- --hsl-foreground: 0 0% 5%; /* #0d0d0d */
462
+ /* Semantic HSL bridge — white paper on a neutral-grey canvas */
463
+ --hsl-background: 240 24% 93%; /* L0 canvas #e9e9f1 */
464
+ /* Ink, not black. Pure #0d0d0d on white paper measures 19.5:1 — a hair off the
465
+ * 21:1 ceiling — and that maximum is what reads as glare rather than as
466
+ * legibility. 20.5% L lands 13.7:1 on the card and 11.4:1 on the canvas: AAA with
467
+ * headroom in both places, and a step the eye reads as text instead of as a
468
+ * hole. */
469
+ --hsl-foreground: 235 18% 20.5%; /* #2b2c3e */
390
470
  --hsl-card: 0 0% 100%; /* L2 card = white paper */
391
- --hsl-card-foreground: 0 0% 5%;
471
+ --hsl-card-foreground: 235 18% 20.5%;
392
472
  --hsl-popover: 0 0% 100%;
393
- --hsl-popover-foreground: 0 0% 5%;
394
- --hsl-primary: 245 62% 57%; /* #5B4ED4 light accent */
473
+ --hsl-popover-foreground: 235 18% 20.5%;
474
+ --hsl-primary: 246 54% 51%; /* #4c3fc6 — carries white at 7.4:1 */
395
475
  --hsl-primary-foreground: 0 0% 100%;
396
- --hsl-secondary: 230 27% 96%; /* L3 #f1f2f7 */
397
- --hsl-secondary-foreground: 0 0% 10%;
398
- --hsl-muted: 230 27% 96%; /* L3 hover well */
399
- --hsl-muted-foreground: 240 5% 38%; /* #5d5d66 text-muted */
400
- --hsl-accent: 230 27% 96%; /* neutral hover */
401
- --hsl-accent-foreground: 0 0% 10%;
476
+ --hsl-secondary: 240 26% 96%; /* L3 #f2f2f7 */
477
+ --hsl-secondary-foreground: 235 18% 20.5%;
478
+ --hsl-muted: 240 26% 96%; /* L3 hover well */
479
+ /* 34% L: the shared components fade captions with /70 and /60, and a
480
+ * translucent foreground renders as the token COMPOSITED over the plane behind
481
+ * it. Against white paper that compositing is brutal — a lighter value puts the
482
+ * /60 caption under the 3:1 large-text floor. This holds 3.0:1 there and 8.6:1
483
+ * at full strength, still a clear step below the 13.7:1 body ink. */
484
+ --hsl-muted-foreground: 235 12% 32.5%; /* #494b5d text-muted */
485
+ --hsl-accent: 240 26% 96%; /* neutral hover */
486
+ --hsl-accent-foreground: 235 18% 20.5%;
402
487
  --hsl-destructive: 0 72% 41%;
403
488
  --hsl-destructive-foreground: 0 0% 100%;
404
- --hsl-border: 244 16.3% 80.8%; /* #c7c6d6 = --md3-outline-variant */
405
- --hsl-input: 225 29% 97%; /* field well #f6f7fa */
406
- --hsl-ring: 245 62% 57%;
489
+ --hsl-border: 238 20% 84%; /* #cecfde = --md3-outline-variant */
490
+ --hsl-input: 240 30% 97%; /* field well */
491
+ --hsl-ring: 246 54% 51%;
407
492
  --hsl-success: 160 84% 30%;
408
493
  --hsl-warning: 41 96% 50%;
409
494
  --hsl-info: 212 80% 50%;
@@ -411,9 +496,9 @@
411
496
  /* depth-2 (card) and depth-4 (elevated) are BOTH paper, on purpose: in light,
412
497
  * a popover does not out-lighten a white card — it lifts by shadow. See the
413
498
  * ladder note above. */
414
- --depth-1: #eceef3; /* canvas */
499
+ --depth-1: #e9e9f1; /* canvas */
415
500
  --depth-2: #ffffff; /* card — paper */
416
- --depth-3: #f1f2f7; /* nested well */
501
+ --depth-3: #f2f2f7; /* nested well */
417
502
  --depth-4: #ffffff; /* elevated — paper, separated by shadow */
418
503
 
419
504
  --status-running: #059669;
@@ -435,7 +520,7 @@
435
520
  --surface-success-text: #047857;
436
521
  --surface-warning-bg: #fffbeb;
437
522
  --surface-warning-border: #fde68a;
438
- --surface-warning-text: #b45309;
523
+ --surface-warning-text: #ab4f09;
439
524
  --surface-danger-bg: #fff1f2;
440
525
  --surface-danger-border: #fecdd3;
441
526
  --surface-danger-text: #b91c1c;
@@ -451,26 +536,51 @@
451
536
  --surface-orange-bg: #fff7ed;
452
537
  --surface-orange-border: #fed7aa;
453
538
  --surface-orange-text: #c2410c;
539
+
540
+ /* Run-mix ramp — the fills of a proportional status bar.
541
+ *
542
+ * Separate from the `--surface-*-text` tones because a 6px bar is read
543
+ * differently from a chip: it is told by hue alone, at a size too small to
544
+ * carry a glyph or a label, so its slices must ALSO separate from each other
545
+ * in greyscale. These five are spaced at least 1.18:1 apart in relative
546
+ * luminance — 1.30 in dark, 1.20 in light, where the ramp has less room
547
+ * before it runs into the paper — and each clears 3:1 against the track, so
548
+ * the mix stays countable with any form of colour blindness and in a
549
+ * monochrome screenshot. 1.18 is the floor the gate holds them to.
550
+ *
551
+ * Ranked by how much the reader needs to tell them apart: succeeded is
552
+ * brightest and cancelled quietest, so the two that matter — succeeded and
553
+ * failed — sit at opposite ends and can never be confused.
554
+ *
555
+ * Spacing them by OKLCH lightness does NOT work: that is perceptual, and the
556
+ * contrast ratio is computed from relative luminance, so an even perceptual
557
+ * ramp left every pair at 1.1:1. Solved in luminance space instead. */
558
+ --run-mix-succeeded: #3c9b78;
559
+ --run-mix-waiting: #c16227;
560
+ --run-mix-failed: #cd342f;
561
+ --run-mix-in-flight: #00679e;
562
+ --run-mix-cancelled: #515367;
563
+
454
564
  --surface-neutral-bg: var(--depth-3);
455
565
  --surface-neutral-border: var(--border-subtle);
456
566
  --surface-neutral-text: var(--text-muted);
457
567
 
458
568
  /* Sidebar — light, flat surface + hairline */
459
569
  --sidebar-background: 0 0% 100%; /* white paper */
460
- --sidebar-foreground: 0 0% 5%;
461
- --sidebar-primary: 245 62% 57%; /* #5B4ED4 */
570
+ --sidebar-foreground: 235 18% 20.5%;
571
+ --sidebar-primary: 246 54% 51%; /* #4c3fc6 */
462
572
  --sidebar-primary-foreground: 0 0% 100%;
463
- --sidebar-accent: 230 27% 96%; /* L3 hover well */
464
- --sidebar-accent-foreground: 0 0% 10%;
465
- --sidebar-border: 244 16.3% 80.8%; /* = --md3-outline-variant */
466
- --sidebar-ring: 245 62% 57%;
573
+ --sidebar-accent: 240 26% 96%; /* L3 hover well */
574
+ --sidebar-accent-foreground: 235 18% 20.5%;
575
+ --sidebar-border: 238 20% 84%; /* = --md3-outline-variant */
576
+ --sidebar-ring: 246 54% 51%;
467
577
 
468
578
  --mesh-teal: transparent;
469
579
  --mesh-violet: transparent;
470
580
  --mesh-blue: transparent;
471
581
 
472
582
  /* Surfaces — flat single-neutral light */
473
- --bg-root: #eceef3;
583
+ --bg-root: #e9e9f1;
474
584
  --bg-dark: var(--depth-1);
475
585
  --bg-card: var(--depth-2);
476
586
  /* Paper, exactly like --bg-card: in light you cannot raise a plane by making it
@@ -481,13 +591,22 @@
481
591
  --bg-elevated: var(--depth-4);
482
592
  --bg-section: var(--depth-1);
483
593
  --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
484
- --bg-hover: rgba(0, 0, 0, 0.04);
485
- --bg-selection: rgba(91, 78, 212, 0.10);
486
-
487
- --text-primary: #0d0d0d;
488
- --text-secondary: #2a2a30;
489
- --text-muted: #5d5d66;
490
- --text-dim: #8e8e98;
594
+ --bg-hover: rgba(76, 63, 198, 0.06);
595
+ --bg-selection: rgba(76, 63, 198, 0.16);
596
+
597
+ /* Mirrors the dark ramp's spacing: 13.7 / 10.5 / 8.6 / 5.6 against white paper.
598
+ * Every step carries the 240 cast so the ink belongs to the palette.
599
+ *
600
+ * --text-dim is pinned from the CANVAS (#e9e9f1), not the white card: it is
601
+ * the faintest tier, so the tinted L0 is the surface that binds it, and a
602
+ * value chosen against paper alone sits under the 4.5:1 body floor there.
603
+ * 4.6 on the canvas, 5.6 on the card — both floors are asserted in
604
+ * tokens.test.ts, because a ratio quoted in a comment drifts the moment a
605
+ * surface moves and nothing catches it. */
606
+ --text-primary: #2b2c3e;
607
+ --text-secondary: #3d3e52;
608
+ --text-muted: #494b5d;
609
+ --text-dim: #65677b;
491
610
 
492
611
  --brand-primary: #6366F1;
493
612
  --brand-strong: #2E2A5E;
@@ -500,16 +619,16 @@
500
619
  --brand-vibrant: #6366F1;
501
620
  --brand-emerald: #059669;
502
621
 
503
- --border-subtle: rgba(0, 0, 0, 0.06);
504
- --border-default: rgba(0, 0, 0, 0.08);
505
- --border-hover: rgba(0, 0, 0, 0.13);
622
+ --border-subtle: rgba(60, 52, 140, 0.10);
623
+ --border-default: rgba(60, 52, 140, 0.14);
624
+ --border-hover: rgba(60, 52, 140, 0.24);
506
625
  --border-accent: rgba(91, 78, 212, 0.18);
507
626
  --border-accent-hover: rgba(91, 78, 212, 0.35);
508
627
 
509
- --btn-primary-bg: #5B4ED4;
510
- --btn-primary-hover: #4F46E5;
628
+ --btn-primary-bg: #4C3FC5;
629
+ --btn-primary-hover: #5A50D6;
511
630
  --btn-primary-text: #ffffff;
512
- --btn-cta-bg: #5B4ED4;
631
+ --btn-cta-bg: #4C3FC5;
513
632
  --btn-cta-text: #ffffff;
514
633
 
515
634
  --code-keyword: #3E349E;
@@ -530,9 +649,9 @@
530
649
  --tangle-gradient: linear-gradient(135deg, #4F46E5, #5B4ED4);
531
650
  --tangle-gradient-text: linear-gradient(135deg, #4F46E5, #5B4ED4);
532
651
  --accent-gradient-strong: linear-gradient(135deg, #4F46E5, #5B4ED4);
533
- --accent-surface-soft: rgba(0, 0, 0, 0.04);
534
- --accent-surface-strong: rgba(91, 78, 212, 0.10);
535
- --accent-text: #4F46E5;
652
+ --accent-surface-soft: rgba(76, 63, 198, 0.05);
653
+ --accent-surface-strong: rgba(76, 63, 198, 0.13);
654
+ --accent-text: #5047eb;
536
655
 
537
656
  /* Typography — unified on Inter */
538
657
  --font-sans: "Inter", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
package/dist/workbench.js CHANGED
@@ -12,14 +12,15 @@ import {
12
12
  buildUnifiedPatch,
13
13
  computeDiffStats,
14
14
  resolveLanguage
15
- } from "./chunk-JIZCCGD2.js";
15
+ } from "./chunk-WCUIUBG5.js";
16
16
  import "./chunk-3J6FG3FJ.js";
17
- import "./chunk-Y4OTBMZS.js";
17
+ import "./chunk-3W343PY5.js";
18
18
  import "./chunk-OMUIPJQZ.js";
19
19
  import "./chunk-WYMAC3T3.js";
20
20
  import "./chunk-KEYPUGS3.js";
21
21
  import "./chunk-4ZZUMXPX.js";
22
22
  import "./chunk-RQZRXKMR.js";
23
+ import "./chunk-BGVEHENC.js";
23
24
  import "./chunk-EI44GEQ5.js";
24
25
  import "./chunk-CZ7CSFO4.js";
25
26
  export {
package/dist/workflows.js CHANGED
@@ -36,7 +36,7 @@ function retryImport(factory, retries = 2, delayMs = 350) {
36
36
  function lazyWorkflowGraph() {
37
37
  return lazy(
38
38
  () => retryImport(
39
- () => import("./WorkflowGraph-3TQZHSUI.js").then((m) => ({ default: m.WorkflowGraph }))
39
+ () => import("./WorkflowGraph-AQLN34R3.js").then((m) => ({ default: m.WorkflowGraph }))
40
40
  )
41
41
  );
42
42
  }
package/dist/workspace.js CHANGED
@@ -20,7 +20,8 @@ import {
20
20
  dispatchSessionCreated,
21
21
  shellShouldRevalidate,
22
22
  useOptimisticSessionItems
23
- } from "./chunk-XKKV3B24.js";
23
+ } from "./chunk-OI7HEWLZ.js";
24
+ import "./chunk-BGVEHENC.js";
24
25
  import "./chunk-EI44GEQ5.js";
25
26
  import "./chunk-CZ7CSFO4.js";
26
27
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.99.2",
3
+ "version": "0.100.0",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -119,7 +119,7 @@
119
119
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
120
120
  "@nanostores/react": ">=0.8.4 <2.0.0",
121
121
  "@tangle-network/agent-interface": ">=0.36.0 <0.47.0",
122
- "@tangle-network/brand": "^1.1.0",
122
+ "@tangle-network/brand": "^1.4.0",
123
123
  "@tangle-network/ui": "^11.0.0",
124
124
  "@tanstack/react-query": "^5.0.0",
125
125
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
@@ -191,7 +191,7 @@
191
191
  "@storybook/react-vite": "^10.5.4",
192
192
  "@tailwindcss/postcss": "^4.3.3",
193
193
  "@tangle-network/agent-interface": "0.46.1",
194
- "@tangle-network/brand": "^1.1.0",
194
+ "@tangle-network/brand": "^1.4.0",
195
195
  "@tangle-network/ui": "^11.2.0",
196
196
  "@tanstack/react-query": "^5.101.4",
197
197
  "@testing-library/dom": "^10.4.1",