@tangle-network/brand 1.29.0 → 1.31.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/CHANGELOG.md +18 -0
- package/README.md +4 -3
- package/bin/tangle-drift.mjs +4 -3
- package/package.json +1 -1
- package/src/accent-scopes.stories.tsx +54 -0
- package/src/styles/legacy-light.css +250 -24
- package/src/styles/named-themes.css +157 -11
- package/src/styles/named-themes.test.ts +1 -1
- package/src/styles/system.css +23 -15
- package/src/styles/theme-scopes.ts +8 -0
- package/src/styles/theme.css +3 -5
- package/src/styles/tokens.css +117 -27
- package/src/styles/tokens.test.ts +74 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.31.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 36a423d: Per-user accent scopes. `data-accent="violet" | "orange" | "teal" | "blue" | "pink" | "brown" | "cyan" | "lime"` on `<html>` or any theme boundary rebinds the four indigo roles (fill, mark, line, ink) and the ring to that category's hue, in both modes and inside every named theme; buttons, focus, selection, interaction states, wells, gradients and the chart accent follow, while planes, body ink and status stay the theme's. `gen-ladders.mjs` solves each hue's roles (`--tone-<hue>-{fill,ring,mark,line,ink}`): the fill carries white at 4.5:1, the ink clears 4.5:1 on the page, the card and every selected tint, the line and the focus border clear 3:1. No picker ships; a product sets the attribute.
|
|
8
|
+
|
|
9
|
+
`tangle-drift`'s `settings_layout` counts a settings page as composed when it binds the shared frame with `bindSettingsNav` from `@tangle-network/ui/presentation`, as a server-rendered host does, as well as when it imports `@tangle-network/agent-app/settings-react`.
|
|
10
|
+
|
|
11
|
+
## 1.30.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 29e2a49: One loop period, one shadow colour and one radius ramp.
|
|
16
|
+
|
|
17
|
+
- `--duration-ambient` (2000ms) and `--motion-ambient` (ease-in-out) are the only loop period on a page; the shared `tangle-breathe` keyframe breathes a running mark on them, opacity only. Reduced motion keeps that period, so the essential "still working" signal survives.
|
|
18
|
+
- `--shadow-color` is the one shadow colour, as HSL channels: black in dark, `220 40% 8%` in light. Every spine shadow, the agents and hospitality themes and system.css draw in it; hospitality's forest-tinted shadow becomes the light shadow colour. The dark `--shadow-overlay` no longer inverts into a white halo.
|
|
19
|
+
- Radius roles are one ramp: `--radius-tag` 4, `--radius-control` 8, `--radius-card` 12, `--radius-container` 16, `--radius-pill`. `--radius-chip`, `--radius-field` and `--radius-panel` keep their values as aliases of control, card and container.
|
|
20
|
+
|
|
3
21
|
## 1.29.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ For finer control, use the exported filenames:
|
|
|
60
60
|
| `.dark[data-theme="intelligence"]` | Existing dark-only product surface; `.light` keeps the identity and uses canonical light |
|
|
61
61
|
| `[data-theme="hospitality"]` + `.dark[data-theme="hospitality"]` | Hospitality's sage-and-forest product theme in both modes. Set it on the document root. Without `.dark` it is a complete light scope, including before hydration; `.dark` selects the dark baseline and its dark ladder. Status tones stay canonical |
|
|
62
62
|
| `.dark[data-theme="website"]` + `[data-theme="website"]` | The public site's indigo-night planes and indigo-paper light. Set `class="dark" data-theme="website"` on the document root; a section with `data-theme="website"` and no `.dark` is a complete light island. Accent, status, category and syntax tones stay canonical |
|
|
63
|
+
| `data-accent="violet"`, `"orange"`, `"teal"`, `"blue"`, `"pink"`, `"brown"`, `"cyan"` or `"lime"` | A per-user accent. Set it on `<html>` or any theme boundary, in either mode and inside any named theme. It rebinds the four indigo roles (`--hsl-primary` fill, `--brand-primary` mark, `--brand-cool` line, `--accent-text` ink) and `--hsl-ring` to that category's `--tone-<hue>-*` values, and everything the spine derives from them follows: buttons, focus, selection, interaction states, wells, gradients, chart accent. Planes, body ink and status stay the theme's. Generated by `scripts/gen-ladders.mjs`; no picker ships, the product stores and sets the attribute |
|
|
63
64
|
|
|
64
65
|
Set one coherent mode on each boundary; do not author contradictory `.light`/`.dark`/named-mode markers. Named light variants keep their existing palettes; `tangle-light` is not silently retuned to the neutral base light palette. Each known boundary receives a complete baseline before named overrides, so nested scopes do not inherit opposite-mode syntax, status or already-resolved HSL aliases.
|
|
65
66
|
|
|
@@ -82,7 +83,7 @@ The Agent App light-default contract is an explicit exception to Brand's default
|
|
|
82
83
|
/* Import globals.css separately only when the host already intends those globals. */
|
|
83
84
|
```
|
|
84
85
|
|
|
85
|
-
Choose this **instead of** `styles`, `tokens.css` and `named-themes.css`; do not stack the two default contracts. The projection contains canonical declarations with a zero-specificity light root default, keeps all explicit mode selectors, and omits `--radius`, which the legacy consumer deliberately leaves to its host. It includes no global component rules
|
|
86
|
+
Choose this **instead of** `styles`, `tokens.css` and `named-themes.css`; do not stack the two default contracts. The projection contains canonical declarations with a zero-specificity light root default, keeps all explicit mode selectors, and omits `--radius`, which the legacy consumer deliberately leaves to its host. It includes no global component rules or automatic ladder/system imports; its one keyframe, `tangle-breathe`, runs only where a component names it.
|
|
86
87
|
|
|
87
88
|
This export is the shared migration boundary, **not a claim that Agent App has already migrated** and not a replacement for that package's entire stylesheet. Its component-specific variables, neutral-ramp API, keyframes and structural rules remain consumer-owned until a separate, explicit migration reconciles them. Remove duplicate shared palette declarations in that migration rather than placing competing global token systems on the same page.
|
|
88
89
|
|
|
@@ -123,9 +124,9 @@ Renderers that cannot read CSS variables (PDF, canvas, WebGL, email, the `theme-
|
|
|
123
124
|
|
|
124
125
|
The base surface ladder is the one GTM proved out: dark is indigo-lifted `#0a0a14` / `#191826` / `#221f33` / `#2c2942`; light is a cool `#eceef3` canvas, white paper cards and overlays, and `#f1f2f7` nested wells. The cast stays below 0.1 chroma and ink stays achromatic, so indigo still reads as the interaction accent, not the base field. Existing named products may supply deliberate surface overrides.
|
|
125
126
|
|
|
126
|
-
Chrome radii are 6/8/10/12px in both base modes; the composer has its own 26px role. Inter (variable, then static) leads body and display stacks, with Geist fallbacks; Geist Mono leads code with JetBrains Mono and system fallbacks. Runtime font/radius registrations retain their public override names; tests keep Tailwind registration defaults aligned with the canonical declarations.
|
|
127
|
+
Chrome radii are 6/8/10/12px in both base modes; the composer has its own 26px role. Role radii are one ramp: `--radius-tag` 4, `--radius-control` 8, `--radius-card` 12, `--radius-container` 16, and `--radius-pill` for every chip; `--radius-chip`, `--radius-field` and `--radius-panel` are older names of control, card and container. Every shadow is drawn in `--shadow-color`, HSL channels that are black in dark and `220 40% 8%` in light; a named theme retints its shadows by setting that one token. Inter (variable, then static) leads body and display stacks, with Geist fallbacks; Geist Mono leads code with JetBrains Mono and system fallbacks. Runtime font/radius registrations retain their public override names; tests keep Tailwind registration defaults aligned with the canonical declarations.
|
|
127
128
|
|
|
128
|
-
Existing `--transition-*` values are preserved. The shared `--duration-*`, `--ease-*` and `--motion-*` vocabulary comes from the existing Agent App contract. It does not apply animations automatically. Reduced motion zeroes those durations and re-resolves composites at theme boundaries. The adjacent `--interaction-*` roles are elapsed acknowledgment/loading thresholds for existing product instrumentation, not animation durations; reduced motion does not change them.
|
|
129
|
+
Existing `--transition-*` values are preserved. The shared `--duration-*`, `--ease-*` and `--motion-*` vocabulary comes from the existing Agent App contract. It does not apply animations automatically. Reduced motion zeroes those durations and re-resolves composites at theme boundaries. `--duration-ambient` (2000 ms; `--motion-ambient` adds ease-in-out) is the only loop period on a page: a running mark breathes on it with the `tangle-breathe` keyframe, opacity only. Reduced motion keeps that one, because it is the essential "still working" signal and moves nothing. The adjacent `--interaction-*` roles are elapsed acknowledgment/loading thresholds for existing product instrumentation, not animation durations; reduced motion does not change them.
|
|
129
130
|
|
|
130
131
|
Focus uses `--focus-border`, `--focus-halo`, their danger variants, and `--border-strong`, derived from the local ring/border palette. UI's `focusField` / `focusRing` consume them. `globals.css` supplies the existing fallback for controls without their own focus treatment.
|
|
131
132
|
|
package/bin/tangle-drift.mjs
CHANGED
|
@@ -143,11 +143,12 @@ export const RULE_PATTERNS = {
|
|
|
143
143
|
*/
|
|
144
144
|
export const PAGE_PATTERNS = {
|
|
145
145
|
// A settings page drawn locally. agent-app's settings-react owns the settings composition: the
|
|
146
|
-
// section nav, frame, rows and save behaviour.
|
|
146
|
+
// section nav, frame, rows and save behaviour. A server-rendered host binds the same frame with
|
|
147
|
+
// ui's bindSettingsNav.
|
|
147
148
|
settings_layout: {
|
|
148
149
|
name: "settings",
|
|
149
|
-
composes: String.raw`(?:\bfrom|\bimport)\s*\(?\s*["']@tangle-network/agent-app/settings-react(?:/[^"']*)?["']`,
|
|
150
|
-
reason: "imports nothing from @tangle-network/agent-app/settings-react",
|
|
150
|
+
composes: String.raw`(?:\bfrom|\bimport)\s*\(?\s*["']@tangle-network/agent-app/settings-react(?:/[^"']*)?["']|\bimport\s*\{[^}]*\bbindSettingsNav\b[^}]*\}\s*from\s*["']@tangle-network/ui/presentation["']`,
|
|
151
|
+
reason: "imports nothing from @tangle-network/agent-app/settings-react, nor bindSettingsNav from @tangle-network/ui/presentation",
|
|
151
152
|
},
|
|
152
153
|
};
|
|
153
154
|
const PAGE_DIR = /(?:^|\/)(?:routes|pages|views)\//i;
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
|
|
3
|
+
// Each data-accent scope inside an explicit .dark and .light boundary and two
|
|
4
|
+
// named themes: fill, ink, focus border, selection, mark and line follow the
|
|
5
|
+
// hue while the card, body ink and status stay the theme's.
|
|
6
|
+
const HUES = ["violet", "orange", "teal", "blue", "pink", "brown", "cyan", "lime"] as const;
|
|
7
|
+
|
|
8
|
+
function Island({ accent, theme, label }: { accent?: string; theme?: string; label: string }) {
|
|
9
|
+
return (
|
|
10
|
+
<section data-accent={accent} data-theme={theme} className="rounded-[var(--radius-card)] border border-border bg-card p-3 text-foreground">
|
|
11
|
+
<p className="mb-2 text-xs text-muted-foreground">{label}</p>
|
|
12
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
13
|
+
<button type="button" className="h-8 rounded-[var(--radius-control)] bg-primary px-3 text-sm font-medium text-primary-foreground">
|
|
14
|
+
Run
|
|
15
|
+
</button>
|
|
16
|
+
<span className="text-sm font-medium text-[var(--accent-text)]">Accent ink</span>
|
|
17
|
+
<span className="h-8 w-14 rounded-[var(--radius-control)] border border-[var(--focus-border)] shadow-[0_0_0_3px_var(--focus-halo)]" />
|
|
18
|
+
<span className="rounded-[var(--radius-pill)] bg-[var(--bg-selection)] px-2 py-0.5 text-xs">Selected</span>
|
|
19
|
+
<span className="size-4 rounded-full bg-[var(--brand-primary)]" />
|
|
20
|
+
<span className="w-8 border-t-2 border-[var(--brand-cool)]" />
|
|
21
|
+
<span className="text-xs text-[var(--surface-success-text)]">Done</span>
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function Mode({ mode }: { mode: "dark" | "light" }) {
|
|
28
|
+
return (
|
|
29
|
+
<div className={`${mode} bg-background p-4`} data-mode={mode}>
|
|
30
|
+
<div className="grid gap-3 sm:grid-cols-3">
|
|
31
|
+
<Island label="indigo (no accent)" />
|
|
32
|
+
{HUES.map((hue) => (
|
|
33
|
+
<Island key={hue} accent={hue} label={hue} />
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const meta: Meta = { title: "Brand/Accent scopes", parameters: { layout: "fullscreen" } };
|
|
41
|
+
export default meta;
|
|
42
|
+
|
|
43
|
+
export const AllHues: StoryObj = {
|
|
44
|
+
render: () => (
|
|
45
|
+
<div>
|
|
46
|
+
<Mode mode="dark" />
|
|
47
|
+
<Mode mode="light" />
|
|
48
|
+
<div className="grid gap-3 p-4 sm:grid-cols-2">
|
|
49
|
+
<Island theme="aubergine" accent="pink" label="pink inside aubergine" />
|
|
50
|
+
<Island theme="super-light" accent="teal" label="teal inside super-light" />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* GENERATED by scripts/gen-compat.mjs. Do not edit.
|
|
2
2
|
* Source: tokens.css + named-themes.css
|
|
3
|
-
* SHA-256:
|
|
3
|
+
* SHA-256: 728da214076ca13bbac43529a3ba30c5349fdd796266ebe18b354598b58acc40
|
|
4
4
|
* Light-default compatibility; all explicit modes remain canonical.
|
|
5
5
|
*/
|
|
6
6
|
:where(:root),
|
|
@@ -239,6 +239,46 @@
|
|
|
239
239
|
--surface-teal-bg: #18333b;
|
|
240
240
|
--surface-teal-border: #207e73;
|
|
241
241
|
--surface-teal-text: #0bd8b6;
|
|
242
|
+
--tone-violet-fill: 272 44.9% 50.2%;
|
|
243
|
+
--tone-violet-ring: 272 100% 80.8%;
|
|
244
|
+
--tone-violet-mark: #8e4ec6;
|
|
245
|
+
--tone-violet-line: #8e4ec6;
|
|
246
|
+
--tone-violet-ink: #d19dff;
|
|
247
|
+
--tone-orange-fill: 22 93.6% 36.9%;
|
|
248
|
+
--tone-orange-ring: 26 100% 67.1%;
|
|
249
|
+
--tone-orange-mark: #f76b15;
|
|
250
|
+
--tone-orange-line: #f76b15;
|
|
251
|
+
--tone-orange-ink: #ffa057;
|
|
252
|
+
--tone-teal-fill: 172 95% 23.7%;
|
|
253
|
+
--tone-teal-ring: 170 90.3% 44.5%;
|
|
254
|
+
--tone-teal-mark: #12a594;
|
|
255
|
+
--tone-teal-line: #12a594;
|
|
256
|
+
--tone-teal-ink: #0bd8b6;
|
|
257
|
+
--tone-blue-fill: 209 86.3% 40%;
|
|
258
|
+
--tone-blue-ring: 210 100% 72%;
|
|
259
|
+
--tone-blue-mark: #0090ff;
|
|
260
|
+
--tone-blue-line: #0090ff;
|
|
261
|
+
--tone-blue-ink: #70b8ff;
|
|
262
|
+
--tone-pink-fill: 322 65.1% 46.1%;
|
|
263
|
+
--tone-pink-ring: 327 100% 77.6%;
|
|
264
|
+
--tone-pink-mark: #d6409f;
|
|
265
|
+
--tone-pink-line: #d6409f;
|
|
266
|
+
--tone-pink-ink: #ff8dcc;
|
|
267
|
+
--tone-brown-fill: 24 29.6% 39%;
|
|
268
|
+
--tone-brown-ring: 28 49.7% 72%;
|
|
269
|
+
--tone-brown-mark: #ad7f58;
|
|
270
|
+
--tone-brown-line: #ad7f58;
|
|
271
|
+
--tone-brown-ink: #dbb594;
|
|
272
|
+
--tone-cyan-fill: 192 80.4% 30%;
|
|
273
|
+
--tone-cyan-ring: 190 75.5% 60%;
|
|
274
|
+
--tone-cyan-mark: #00a2c7;
|
|
275
|
+
--tone-cyan-line: #00a2c7;
|
|
276
|
+
--tone-cyan-ink: #4ccce6;
|
|
277
|
+
--tone-lime-fill: 84 44.2% 30.2%;
|
|
278
|
+
--tone-lime-ring: 80 69.9% 66.1%;
|
|
279
|
+
--tone-lime-mark: #bdee63;
|
|
280
|
+
--tone-lime-line: #bdee63;
|
|
281
|
+
--tone-lime-ink: #bde56c;
|
|
242
282
|
--run-mix-succeeded: #76edb9;
|
|
243
283
|
--run-mix-waiting: #e9b642;
|
|
244
284
|
--run-mix-failed: #e1918e;
|
|
@@ -272,12 +312,12 @@
|
|
|
272
312
|
--code-success: var(--surface-success-text);
|
|
273
313
|
--code-comment: var(--syntax-comment);
|
|
274
314
|
--code-error: var(--syntax-error);
|
|
275
|
-
--shadow-
|
|
276
|
-
--shadow-
|
|
315
|
+
--shadow-color: 0 0% 0%;
|
|
316
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.3);
|
|
317
|
+
--shadow-dropdown: 0 8px 30px hsl(var(--shadow-color) / 0.45);
|
|
277
318
|
--shadow-glow: none;
|
|
278
|
-
--shadow-accent:
|
|
279
|
-
--shadow-overlay: 0 2px 6px hsl(var(--hsl-
|
|
280
|
-
0 16px 40px hsl(var(--hsl-foreground) / 0.16);
|
|
319
|
+
--shadow-accent: var(--shadow-card);
|
|
320
|
+
--shadow-overlay: 0 2px 6px hsl(var(--shadow-color) / 0.3), 0 16px 40px hsl(var(--shadow-color) / 0.5);
|
|
281
321
|
--scrim: rgb(0 0 0 / 0.4);
|
|
282
322
|
--scrim-strong: rgb(0 0 0 / 0.72);
|
|
283
323
|
--on-media: #ffffff;
|
|
@@ -292,9 +332,12 @@
|
|
|
292
332
|
--radius-full: 999px;
|
|
293
333
|
--radius-composer: 26px;
|
|
294
334
|
--radius-tag: 4px;
|
|
295
|
-
--radius-
|
|
296
|
-
--radius-
|
|
297
|
-
--radius-
|
|
335
|
+
--radius-control: var(--radius-md);
|
|
336
|
+
--radius-card: var(--radius-xl);
|
|
337
|
+
--radius-container: 16px;
|
|
338
|
+
--radius-chip: var(--radius-control);
|
|
339
|
+
--radius-field: var(--radius-card);
|
|
340
|
+
--radius-panel: var(--radius-container);
|
|
298
341
|
--radius-cover: 20px;
|
|
299
342
|
--radius-sheet: 24px;
|
|
300
343
|
--control-height: 2.25rem;
|
|
@@ -377,6 +420,8 @@
|
|
|
377
420
|
--motion-dismiss: var(--duration-instant) var(--ease-exit);
|
|
378
421
|
--motion-stream: var(--duration-stream) var(--ease-expo);
|
|
379
422
|
--motion-arrive: var(--duration-arrive) var(--ease-expo);
|
|
423
|
+
--duration-ambient: 2000ms;
|
|
424
|
+
--motion-ambient: var(--duration-ambient) ease-in-out;
|
|
380
425
|
--interaction-feedback-budget: 100ms;
|
|
381
426
|
--interaction-pending-delay: 200ms;
|
|
382
427
|
--interaction-long-work-delay: 1000ms;
|
|
@@ -526,6 +571,46 @@
|
|
|
526
571
|
--surface-teal-bg: #e0f8f3;
|
|
527
572
|
--surface-teal-border: #a1ded2;
|
|
528
573
|
--surface-teal-text: #017d6d;
|
|
574
|
+
--tone-violet-fill: 272 44.9% 50.2%;
|
|
575
|
+
--tone-violet-ring: 272 44.9% 50.2%;
|
|
576
|
+
--tone-violet-mark: #8e4ec6;
|
|
577
|
+
--tone-violet-line: #8e4ec6;
|
|
578
|
+
--tone-violet-ink: #753ea6;
|
|
579
|
+
--tone-orange-fill: 22 93.6% 36.9%;
|
|
580
|
+
--tone-orange-ring: 22 93.6% 36.9%;
|
|
581
|
+
--tone-orange-mark: #f76b15;
|
|
582
|
+
--tone-orange-line: #cc4e00;
|
|
583
|
+
--tone-orange-ink: #953d0e;
|
|
584
|
+
--tone-teal-fill: 172 95% 23.7%;
|
|
585
|
+
--tone-teal-ring: 172 95% 23.7%;
|
|
586
|
+
--tone-teal-mark: #12a594;
|
|
587
|
+
--tone-teal-line: #008573;
|
|
588
|
+
--tone-teal-ink: #066558;
|
|
589
|
+
--tone-blue-fill: 209 86.3% 40%;
|
|
590
|
+
--tone-blue-ring: 209 86.3% 40%;
|
|
591
|
+
--tone-blue-mark: #0090ff;
|
|
592
|
+
--tone-blue-line: #0588f0;
|
|
593
|
+
--tone-blue-ink: #0f59a3;
|
|
594
|
+
--tone-pink-fill: 322 65.1% 46.1%;
|
|
595
|
+
--tone-pink-ring: 322 65.1% 46.1%;
|
|
596
|
+
--tone-pink-mark: #d6409f;
|
|
597
|
+
--tone-pink-line: #d6409f;
|
|
598
|
+
--tone-pink-ink: #a02071;
|
|
599
|
+
--tone-brown-fill: 24 29.6% 39%;
|
|
600
|
+
--tone-brown-ring: 24 29.6% 39%;
|
|
601
|
+
--tone-brown-mark: #ad7f58;
|
|
602
|
+
--tone-brown-line: #ad7f58;
|
|
603
|
+
--tone-brown-ink: #725440;
|
|
604
|
+
--tone-cyan-fill: 192 80.4% 30%;
|
|
605
|
+
--tone-cyan-ring: 192 80.4% 30%;
|
|
606
|
+
--tone-cyan-mark: #00a2c7;
|
|
607
|
+
--tone-cyan-line: #107d98;
|
|
608
|
+
--tone-cyan-ink: #0f6277;
|
|
609
|
+
--tone-lime-fill: 84 44.2% 30.2%;
|
|
610
|
+
--tone-lime-ring: 84 44.2% 30.2%;
|
|
611
|
+
--tone-lime-mark: #bdee63;
|
|
612
|
+
--tone-lime-line: #5c7c2f;
|
|
613
|
+
--tone-lime-ink: #4b6126;
|
|
529
614
|
--run-mix-succeeded: #3c9b78;
|
|
530
615
|
--run-mix-waiting: #c16227;
|
|
531
616
|
--run-mix-failed: #cd342f;
|
|
@@ -533,10 +618,10 @@
|
|
|
533
618
|
--run-mix-cancelled: #515367;
|
|
534
619
|
--glass-bg-strong: var(--depth-1);
|
|
535
620
|
--code-header-bg: color-mix(in srgb, hsl(var(--hsl-muted)) 50%, transparent);
|
|
536
|
-
--shadow-
|
|
537
|
-
--shadow-
|
|
538
|
-
--shadow-
|
|
539
|
-
--shadow-
|
|
621
|
+
--shadow-color: 220 40% 8%;
|
|
622
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.06);
|
|
623
|
+
--shadow-dropdown: 0 8px 30px hsl(var(--shadow-color) / 0.1);
|
|
624
|
+
--shadow-overlay: 0 2px 6px hsl(var(--shadow-color) / 0.1), 0 16px 40px hsl(var(--shadow-color) / 0.16);
|
|
540
625
|
}
|
|
541
626
|
:where(:root),
|
|
542
627
|
.dark,
|
|
@@ -640,6 +725,11 @@
|
|
|
640
725
|
--chart-tier-a: #5b5bd6;
|
|
641
726
|
--chart-tier-s: #272962;
|
|
642
727
|
}
|
|
728
|
+
@keyframes tangle-breathe {
|
|
729
|
+
50% {
|
|
730
|
+
opacity: 0.45;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
643
733
|
@media (prefers-reduced-motion: reduce) {
|
|
644
734
|
:where(:root),
|
|
645
735
|
.dark,
|
|
@@ -1262,9 +1352,9 @@
|
|
|
1262
1352
|
--field-border: #798479;
|
|
1263
1353
|
--border-accent: rgba(40, 88, 66, 0.20);
|
|
1264
1354
|
--border-accent-hover: rgba(40, 88, 66, 0.36);
|
|
1265
|
-
--shadow-card: 0 1px 2px
|
|
1266
|
-
--shadow-raised: 0 1px 1px
|
|
1267
|
-
--shadow-well: inset 0 1px 2px
|
|
1355
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.07), 0 4px 14px -4px hsl(var(--shadow-color) / 0.1);
|
|
1356
|
+
--shadow-raised: 0 1px 1px hsl(var(--shadow-color) / 0.05), 0 2px 6px -2px hsl(var(--shadow-color) / 0.07);
|
|
1357
|
+
--shadow-well: inset 0 1px 2px hsl(var(--shadow-color) / 0.06);
|
|
1268
1358
|
--edge-highlight: 0 0 0 0 transparent;
|
|
1269
1359
|
--accent-text: #285842;
|
|
1270
1360
|
--accent-surface-soft: rgba(40, 88, 66, 0.05);
|
|
@@ -1364,9 +1454,9 @@
|
|
|
1364
1454
|
--field-border: #939c94;
|
|
1365
1455
|
--border-accent: rgba(118, 183, 145, 0.35);
|
|
1366
1456
|
--border-accent-hover: rgba(118, 183, 145, 0.50);
|
|
1367
|
-
--shadow-card: 0 1px 2px
|
|
1368
|
-
--shadow-raised: 0 1px 2px
|
|
1369
|
-
--shadow-well: inset 0 1px 2px
|
|
1457
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.45), 0 10px 24px -14px hsl(var(--shadow-color) / 0.6);
|
|
1458
|
+
--shadow-raised: 0 1px 2px hsl(var(--shadow-color) / 0.35);
|
|
1459
|
+
--shadow-well: inset 0 1px 2px hsl(var(--shadow-color) / 0.5);
|
|
1370
1460
|
--edge-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
|
1371
1461
|
--accent-text: #b2dbc2;
|
|
1372
1462
|
--accent-surface-soft: rgba(118, 183, 145, 0.07);
|
|
@@ -1448,8 +1538,8 @@
|
|
|
1448
1538
|
--border-default: #49474e;
|
|
1449
1539
|
--border-hover: #625f69;
|
|
1450
1540
|
--shadow-card: inset 0 0 0 1px rgb(255 255 255 / 0.06);
|
|
1451
|
-
--shadow-dropdown: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px
|
|
1452
|
-
--shadow-overlay: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px
|
|
1541
|
+
--shadow-dropdown: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px hsl(var(--shadow-color) / 0.5);
|
|
1542
|
+
--shadow-overlay: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px hsl(var(--shadow-color) / 0.6);
|
|
1453
1543
|
}
|
|
1454
1544
|
[data-theme="agents-light"] {
|
|
1455
1545
|
--bg-card: #ffffff;
|
|
@@ -1502,9 +1592,9 @@
|
|
|
1502
1592
|
--border-subtle: #dbd8e0;
|
|
1503
1593
|
--border-default: #d0cdd7;
|
|
1504
1594
|
--border-hover: #bcbac7;
|
|
1505
|
-
--shadow-card: 0 0 0 1px
|
|
1506
|
-
--shadow-dropdown: 0 0 0 1px
|
|
1507
|
-
--shadow-overlay: 0 8px 28px
|
|
1595
|
+
--shadow-card: 0 0 0 1px hsl(var(--shadow-color) / 0.03), 0 2px 6px hsl(var(--shadow-color) / 0.05), 0 4px 8px hsl(var(--shadow-color) / 0.08);
|
|
1596
|
+
--shadow-dropdown: 0 0 0 1px hsl(var(--shadow-color) / 0.04), 0 6px 16px hsl(var(--shadow-color) / 0.12);
|
|
1597
|
+
--shadow-overlay: 0 8px 28px hsl(var(--shadow-color) / 0.28);
|
|
1508
1598
|
}
|
|
1509
1599
|
[data-theme="website"]:where(:not(.dark)) {
|
|
1510
1600
|
--hsl-background: 240 42.9% 97.3%;
|
|
@@ -1598,3 +1688,139 @@
|
|
|
1598
1688
|
--text-muted: #9496bc;
|
|
1599
1689
|
--text-dim: #9496bc;
|
|
1600
1690
|
}
|
|
1691
|
+
[data-accent="violet"],
|
|
1692
|
+
[data-accent="violet"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1693
|
+
[data-accent="violet"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1694
|
+
--hsl-primary: var(--tone-violet-fill);
|
|
1695
|
+
--hsl-ring: var(--tone-violet-ring);
|
|
1696
|
+
--brand-primary: var(--tone-violet-mark);
|
|
1697
|
+
--brand-cool: var(--tone-violet-line);
|
|
1698
|
+
--accent-text: var(--tone-violet-ink);
|
|
1699
|
+
}
|
|
1700
|
+
[data-accent="orange"],
|
|
1701
|
+
[data-accent="orange"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1702
|
+
[data-accent="orange"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1703
|
+
--hsl-primary: var(--tone-orange-fill);
|
|
1704
|
+
--hsl-ring: var(--tone-orange-ring);
|
|
1705
|
+
--brand-primary: var(--tone-orange-mark);
|
|
1706
|
+
--brand-cool: var(--tone-orange-line);
|
|
1707
|
+
--accent-text: var(--tone-orange-ink);
|
|
1708
|
+
}
|
|
1709
|
+
[data-accent="teal"],
|
|
1710
|
+
[data-accent="teal"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1711
|
+
[data-accent="teal"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1712
|
+
--hsl-primary: var(--tone-teal-fill);
|
|
1713
|
+
--hsl-ring: var(--tone-teal-ring);
|
|
1714
|
+
--brand-primary: var(--tone-teal-mark);
|
|
1715
|
+
--brand-cool: var(--tone-teal-line);
|
|
1716
|
+
--accent-text: var(--tone-teal-ink);
|
|
1717
|
+
}
|
|
1718
|
+
[data-accent="blue"],
|
|
1719
|
+
[data-accent="blue"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1720
|
+
[data-accent="blue"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1721
|
+
--hsl-primary: var(--tone-blue-fill);
|
|
1722
|
+
--hsl-ring: var(--tone-blue-ring);
|
|
1723
|
+
--brand-primary: var(--tone-blue-mark);
|
|
1724
|
+
--brand-cool: var(--tone-blue-line);
|
|
1725
|
+
--accent-text: var(--tone-blue-ink);
|
|
1726
|
+
}
|
|
1727
|
+
[data-accent="pink"],
|
|
1728
|
+
[data-accent="pink"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1729
|
+
[data-accent="pink"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1730
|
+
--hsl-primary: var(--tone-pink-fill);
|
|
1731
|
+
--hsl-ring: var(--tone-pink-ring);
|
|
1732
|
+
--brand-primary: var(--tone-pink-mark);
|
|
1733
|
+
--brand-cool: var(--tone-pink-line);
|
|
1734
|
+
--accent-text: var(--tone-pink-ink);
|
|
1735
|
+
}
|
|
1736
|
+
[data-accent="brown"],
|
|
1737
|
+
[data-accent="brown"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1738
|
+
[data-accent="brown"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1739
|
+
--hsl-primary: var(--tone-brown-fill);
|
|
1740
|
+
--hsl-ring: var(--tone-brown-ring);
|
|
1741
|
+
--brand-primary: var(--tone-brown-mark);
|
|
1742
|
+
--brand-cool: var(--tone-brown-line);
|
|
1743
|
+
--accent-text: var(--tone-brown-ink);
|
|
1744
|
+
}
|
|
1745
|
+
[data-accent="cyan"],
|
|
1746
|
+
[data-accent="cyan"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1747
|
+
[data-accent="cyan"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1748
|
+
--hsl-primary: var(--tone-cyan-fill);
|
|
1749
|
+
--hsl-ring: var(--tone-cyan-ring);
|
|
1750
|
+
--brand-primary: var(--tone-cyan-mark);
|
|
1751
|
+
--brand-cool: var(--tone-cyan-line);
|
|
1752
|
+
--accent-text: var(--tone-cyan-ink);
|
|
1753
|
+
}
|
|
1754
|
+
[data-accent="lime"],
|
|
1755
|
+
[data-accent="lime"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
1756
|
+
[data-accent="lime"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1757
|
+
--hsl-primary: var(--tone-lime-fill);
|
|
1758
|
+
--hsl-ring: var(--tone-lime-ring);
|
|
1759
|
+
--brand-primary: var(--tone-lime-mark);
|
|
1760
|
+
--brand-cool: var(--tone-lime-line);
|
|
1761
|
+
--accent-text: var(--tone-lime-ink);
|
|
1762
|
+
}
|
|
1763
|
+
:is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]), :is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]):is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]), :is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]) :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
1764
|
+
--btn-primary-hover: color-mix(in srgb, hsl(var(--hsl-primary)), white 10%);
|
|
1765
|
+
--brand-purple: light-dark(var(--brand-primary), color-mix(in srgb, var(--accent-text), white 35%));
|
|
1766
|
+
--brand-strong: color-mix(in srgb, hsl(var(--hsl-primary)), black 50%);
|
|
1767
|
+
--md3-primary-fixed: light-dark(color-mix(in srgb, var(--accent-text) 12%, white), var(--accent-text));
|
|
1768
|
+
--md3-inverse-primary: light-dark(color-mix(in srgb, var(--brand-primary), white 40%), var(--btn-primary-bg));
|
|
1769
|
+
--hsl-primary-foreground: 0 0% 100%;
|
|
1770
|
+
--btn-primary-text: #ffffff;
|
|
1771
|
+
--md3-on-primary: #ffffff;
|
|
1772
|
+
--brand-glow: var(--accent-text);
|
|
1773
|
+
--brand-vibrant: var(--brand-primary);
|
|
1774
|
+
--btn-primary-bg: hsl(var(--hsl-primary));
|
|
1775
|
+
--btn-cta-bg: var(--btn-primary-bg);
|
|
1776
|
+
--btn-cta-text: var(--btn-primary-text);
|
|
1777
|
+
--sidebar-primary: var(--hsl-primary);
|
|
1778
|
+
--sidebar-primary-foreground: var(--hsl-primary-foreground);
|
|
1779
|
+
--sidebar-ring: var(--hsl-ring);
|
|
1780
|
+
--md3-primary: var(--accent-text);
|
|
1781
|
+
--md3-primary-dim: light-dark(var(--btn-primary-bg), var(--brand-primary));
|
|
1782
|
+
--md3-primary-container: var(--btn-primary-bg);
|
|
1783
|
+
--md3-on-primary-container: light-dark(#ffffff, var(--brand-purple));
|
|
1784
|
+
--md3-primary-fixed-dim: light-dark(color-mix(in srgb, var(--md3-primary-fixed) 85%, var(--accent-text)), var(--brand-cool));
|
|
1785
|
+
--md3-secondary: light-dark(var(--accent-text), var(--brand-purple));
|
|
1786
|
+
--md3-secondary-dim: light-dark(var(--btn-primary-bg), var(--accent-text));
|
|
1787
|
+
--md3-secondary-container: light-dark(var(--md3-primary-fixed), var(--brand-strong));
|
|
1788
|
+
--md3-on-secondary: light-dark(#ffffff, var(--brand-purple));
|
|
1789
|
+
--md3-on-secondary-container: light-dark(var(--accent-text), var(--brand-purple));
|
|
1790
|
+
--md3-surface-tint: light-dark(var(--brand-primary), var(--accent-text));
|
|
1791
|
+
--accent-surface-soft: light-dark(color-mix(in srgb, var(--btn-primary-bg) 5%, transparent), color-mix(in srgb, var(--accent-text) 7%, transparent));
|
|
1792
|
+
--accent-surface-strong: light-dark(color-mix(in srgb, var(--btn-primary-bg) 13%, transparent), color-mix(in srgb, var(--btn-primary-bg) 30%, transparent));
|
|
1793
|
+
--bg-hover: var(--accent-surface-soft);
|
|
1794
|
+
--bg-selection: light-dark(color-mix(in srgb, var(--btn-primary-bg) 16%, transparent), color-mix(in srgb, var(--brand-primary) 28%, transparent));
|
|
1795
|
+
--border-accent: light-dark(color-mix(in srgb, var(--btn-primary-hover) 18%, transparent), color-mix(in srgb, var(--brand-cool) 35%, transparent));
|
|
1796
|
+
--border-accent-hover: light-dark(color-mix(in srgb, var(--btn-primary-hover) 35%, transparent), color-mix(in srgb, var(--brand-cool) 50%, transparent));
|
|
1797
|
+
--tangle-gradient: linear-gradient(135deg, var(--btn-primary-bg), light-dark(var(--btn-primary-hover), var(--accent-text)));
|
|
1798
|
+
--tangle-gradient-text: linear-gradient(135deg, light-dark(var(--btn-primary-bg), var(--brand-purple)), light-dark(var(--btn-primary-hover), var(--accent-text)));
|
|
1799
|
+
--accent-gradient-strong: linear-gradient(135deg, var(--btn-primary-bg), light-dark(var(--btn-primary-hover), var(--brand-primary)));
|
|
1800
|
+
--status-creating: var(--accent-text);
|
|
1801
|
+
--primary: var(--hsl-primary);
|
|
1802
|
+
--primary-foreground: var(--hsl-primary-foreground);
|
|
1803
|
+
--ring: var(--hsl-ring);
|
|
1804
|
+
--syntax-meta: var(--accent-text);
|
|
1805
|
+
--display-control-shadow: inset 0 1px 0 rgb(255 255 255 / 0.18), 0 4px 16px hsl(var(--primary) / 0.2);
|
|
1806
|
+
--display-gradient: linear-gradient(135deg, hsl(var(--foreground)) 10%, var(--accent-text) 70%);
|
|
1807
|
+
--focus-border: hsl(var(--hsl-ring) / 0.8);
|
|
1808
|
+
--focus-halo: hsl(var(--hsl-ring) / 0.18);
|
|
1809
|
+
--ix-hover: 0 0 0 1px hsl(var(--hsl-ring) / 0.45), 0 0 0 4px hsl(var(--hsl-ring) / 0.16);
|
|
1810
|
+
--ix-hover-border: hsl(var(--hsl-ring) / 0.7);
|
|
1811
|
+
--ix-press: 0 0 0 1px hsl(var(--hsl-ring) / 0.95), 0 0 0 4px hsl(var(--hsl-ring) / 0.3);
|
|
1812
|
+
--ix-after: 0 0 0 1px hsl(var(--hsl-ring) / 0.6), 0 0 0 3px hsl(var(--hsl-ring) / 0.14);
|
|
1813
|
+
--ix-sel-ring: hsl(var(--hsl-ring) / 0.85);
|
|
1814
|
+
--ix-sel-bg: light-dark(hsl(var(--hsl-ring) / 0.13), hsl(var(--hsl-ring) / 0.18));
|
|
1815
|
+
--ix-hover-bg: light-dark(hsl(var(--hsl-ring) / 0.07), hsl(var(--hsl-ring) / 0.08));
|
|
1816
|
+
--chart-shade: var(--bg-hover);
|
|
1817
|
+
--chart-accent: light-dark(hsl(var(--hsl-primary)), var(--brand-cool));
|
|
1818
|
+
--color-brand: var(--brand-primary);
|
|
1819
|
+
--color-brand-dim: var(--btn-primary-bg);
|
|
1820
|
+
--color-brand-cool: var(--brand-cool);
|
|
1821
|
+
--color-brand-glow: var(--brand-glow);
|
|
1822
|
+
--color-status-creating: var(--status-creating);
|
|
1823
|
+
--color-primary: hsl(var(--primary));
|
|
1824
|
+
--color-primary-foreground: hsl(var(--primary-foreground));
|
|
1825
|
+
--color-ring: hsl(var(--ring));
|
|
1826
|
+
}
|
|
@@ -393,9 +393,9 @@
|
|
|
393
393
|
--border-hover: rgba(23, 38, 29, 0.28); --border-strong: #798479;
|
|
394
394
|
--field-border: #798479;
|
|
395
395
|
--border-accent: rgba(40, 88, 66, 0.20); --border-accent-hover: rgba(40, 88, 66, 0.36);
|
|
396
|
-
--shadow-card: 0 1px 2px
|
|
397
|
-
--shadow-raised: 0 1px 1px
|
|
398
|
-
--shadow-well: inset 0 1px 2px
|
|
396
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.07), 0 4px 14px -4px hsl(var(--shadow-color) / 0.1);
|
|
397
|
+
--shadow-raised: 0 1px 1px hsl(var(--shadow-color) / 0.05), 0 2px 6px -2px hsl(var(--shadow-color) / 0.07);
|
|
398
|
+
--shadow-well: inset 0 1px 2px hsl(var(--shadow-color) / 0.06);
|
|
399
399
|
--edge-highlight: 0 0 0 0 transparent;
|
|
400
400
|
--accent-text: #285842;
|
|
401
401
|
--accent-surface-soft: rgba(40, 88, 66, 0.05); --accent-surface-strong: rgba(40, 88, 66, 0.13);
|
|
@@ -443,9 +443,9 @@
|
|
|
443
443
|
--border-hover: rgba(227, 233, 229, 0.28); --border-strong: #939c94;
|
|
444
444
|
--field-border: #939c94;
|
|
445
445
|
--border-accent: rgba(118, 183, 145, 0.35); --border-accent-hover: rgba(118, 183, 145, 0.50);
|
|
446
|
-
--shadow-card: 0 1px 2px
|
|
447
|
-
--shadow-raised: 0 1px 2px
|
|
448
|
-
--shadow-well: inset 0 1px 2px
|
|
446
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.45), 0 10px 24px -14px hsl(var(--shadow-color) / 0.6);
|
|
447
|
+
--shadow-raised: 0 1px 2px hsl(var(--shadow-color) / 0.35);
|
|
448
|
+
--shadow-well: inset 0 1px 2px hsl(var(--shadow-color) / 0.5);
|
|
449
449
|
--edge-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
|
450
450
|
/* Lifted from #99caad so selected text on a primary tint still clears 4.5:1
|
|
451
451
|
* over the lighter raised plane (L 37.9). */
|
|
@@ -517,8 +517,8 @@
|
|
|
517
517
|
--border-default: #49474e;
|
|
518
518
|
--border-hover: #625f69;
|
|
519
519
|
--shadow-card: inset 0 0 0 1px rgb(255 255 255 / 0.06);
|
|
520
|
-
--shadow-dropdown: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px
|
|
521
|
-
--shadow-overlay: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px
|
|
520
|
+
--shadow-dropdown: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px hsl(var(--shadow-color) / 0.5);
|
|
521
|
+
--shadow-overlay: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px hsl(var(--shadow-color) / 0.6);
|
|
522
522
|
}
|
|
523
523
|
[data-theme="agents-light"] {
|
|
524
524
|
--bg-card: #ffffff;
|
|
@@ -571,9 +571,9 @@
|
|
|
571
571
|
--border-subtle: #dbd8e0;
|
|
572
572
|
--border-default: #d0cdd7;
|
|
573
573
|
--border-hover: #bcbac7;
|
|
574
|
-
--shadow-card: 0 0 0 1px
|
|
575
|
-
--shadow-dropdown: 0 0 0 1px
|
|
576
|
-
--shadow-overlay: 0 8px 28px
|
|
574
|
+
--shadow-card: 0 0 0 1px hsl(var(--shadow-color) / 0.03), 0 2px 6px hsl(var(--shadow-color) / 0.05), 0 4px 8px hsl(var(--shadow-color) / 0.08);
|
|
575
|
+
--shadow-dropdown: 0 0 0 1px hsl(var(--shadow-color) / 0.04), 0 6px 16px hsl(var(--shadow-color) / 0.12);
|
|
576
|
+
--shadow-overlay: 0 8px 28px hsl(var(--shadow-color) / 0.28);
|
|
577
577
|
}
|
|
578
578
|
/* agents:end */
|
|
579
579
|
|
|
@@ -629,3 +629,149 @@
|
|
|
629
629
|
--border-hover: rgba(255, 255, 255, 0.28);
|
|
630
630
|
--text-primary: #fdfdfd; --text-secondary: #aaa8b8; --text-muted: #9496bc; --text-dim: #9496bc;
|
|
631
631
|
}
|
|
632
|
+
|
|
633
|
+
/* accent-scopes:begin — generated by scripts/gen-ladders.mjs from its CATEGORY map and the spine; edit there, not these lines.
|
|
634
|
+
* data-accent="<category>" on <html> or any theme boundary retints the
|
|
635
|
+
* interaction accent: the five roles take the hue's --tone-<hue>-* values and
|
|
636
|
+
* the shared rule re-declares what derives from them. Planes, body ink and
|
|
637
|
+
* status stay the theme's. Last in the stylesheet, so a scope wins over the
|
|
638
|
+
* named theme it sits on or in. */
|
|
639
|
+
[data-accent="violet"],
|
|
640
|
+
[data-accent="violet"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
641
|
+
[data-accent="violet"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
642
|
+
--hsl-primary: var(--tone-violet-fill);
|
|
643
|
+
--hsl-ring: var(--tone-violet-ring);
|
|
644
|
+
--brand-primary: var(--tone-violet-mark);
|
|
645
|
+
--brand-cool: var(--tone-violet-line);
|
|
646
|
+
--accent-text: var(--tone-violet-ink);
|
|
647
|
+
}
|
|
648
|
+
[data-accent="orange"],
|
|
649
|
+
[data-accent="orange"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
650
|
+
[data-accent="orange"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
651
|
+
--hsl-primary: var(--tone-orange-fill);
|
|
652
|
+
--hsl-ring: var(--tone-orange-ring);
|
|
653
|
+
--brand-primary: var(--tone-orange-mark);
|
|
654
|
+
--brand-cool: var(--tone-orange-line);
|
|
655
|
+
--accent-text: var(--tone-orange-ink);
|
|
656
|
+
}
|
|
657
|
+
[data-accent="teal"],
|
|
658
|
+
[data-accent="teal"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
659
|
+
[data-accent="teal"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
660
|
+
--hsl-primary: var(--tone-teal-fill);
|
|
661
|
+
--hsl-ring: var(--tone-teal-ring);
|
|
662
|
+
--brand-primary: var(--tone-teal-mark);
|
|
663
|
+
--brand-cool: var(--tone-teal-line);
|
|
664
|
+
--accent-text: var(--tone-teal-ink);
|
|
665
|
+
}
|
|
666
|
+
[data-accent="blue"],
|
|
667
|
+
[data-accent="blue"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
668
|
+
[data-accent="blue"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
669
|
+
--hsl-primary: var(--tone-blue-fill);
|
|
670
|
+
--hsl-ring: var(--tone-blue-ring);
|
|
671
|
+
--brand-primary: var(--tone-blue-mark);
|
|
672
|
+
--brand-cool: var(--tone-blue-line);
|
|
673
|
+
--accent-text: var(--tone-blue-ink);
|
|
674
|
+
}
|
|
675
|
+
[data-accent="pink"],
|
|
676
|
+
[data-accent="pink"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
677
|
+
[data-accent="pink"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
678
|
+
--hsl-primary: var(--tone-pink-fill);
|
|
679
|
+
--hsl-ring: var(--tone-pink-ring);
|
|
680
|
+
--brand-primary: var(--tone-pink-mark);
|
|
681
|
+
--brand-cool: var(--tone-pink-line);
|
|
682
|
+
--accent-text: var(--tone-pink-ink);
|
|
683
|
+
}
|
|
684
|
+
[data-accent="brown"],
|
|
685
|
+
[data-accent="brown"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
686
|
+
[data-accent="brown"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
687
|
+
--hsl-primary: var(--tone-brown-fill);
|
|
688
|
+
--hsl-ring: var(--tone-brown-ring);
|
|
689
|
+
--brand-primary: var(--tone-brown-mark);
|
|
690
|
+
--brand-cool: var(--tone-brown-line);
|
|
691
|
+
--accent-text: var(--tone-brown-ink);
|
|
692
|
+
}
|
|
693
|
+
[data-accent="cyan"],
|
|
694
|
+
[data-accent="cyan"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
695
|
+
[data-accent="cyan"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
696
|
+
--hsl-primary: var(--tone-cyan-fill);
|
|
697
|
+
--hsl-ring: var(--tone-cyan-ring);
|
|
698
|
+
--brand-primary: var(--tone-cyan-mark);
|
|
699
|
+
--brand-cool: var(--tone-cyan-line);
|
|
700
|
+
--accent-text: var(--tone-cyan-ink);
|
|
701
|
+
}
|
|
702
|
+
[data-accent="lime"],
|
|
703
|
+
[data-accent="lime"]:is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
704
|
+
[data-accent="lime"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
705
|
+
--hsl-primary: var(--tone-lime-fill);
|
|
706
|
+
--hsl-ring: var(--tone-lime-ring);
|
|
707
|
+
--brand-primary: var(--tone-lime-mark);
|
|
708
|
+
--brand-cool: var(--tone-lime-line);
|
|
709
|
+
--accent-text: var(--tone-lime-ink);
|
|
710
|
+
}
|
|
711
|
+
:is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]),
|
|
712
|
+
:is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]):is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]),
|
|
713
|
+
:is([data-accent="violet"], [data-accent="orange"], [data-accent="teal"], [data-accent="blue"], [data-accent="pink"], [data-accent="brown"], [data-accent="cyan"], [data-accent="lime"]) :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme]) {
|
|
714
|
+
--btn-primary-hover: color-mix(in srgb, hsl(var(--hsl-primary)), white 10%);
|
|
715
|
+
--brand-purple: light-dark(var(--brand-primary), color-mix(in srgb, var(--accent-text), white 35%));
|
|
716
|
+
--brand-strong: color-mix(in srgb, hsl(var(--hsl-primary)), black 50%);
|
|
717
|
+
--md3-primary-fixed: light-dark(color-mix(in srgb, var(--accent-text) 12%, white), var(--accent-text));
|
|
718
|
+
--md3-inverse-primary: light-dark(color-mix(in srgb, var(--brand-primary), white 40%), var(--btn-primary-bg));
|
|
719
|
+
--hsl-primary-foreground: 0 0% 100%;
|
|
720
|
+
--btn-primary-text: #ffffff;
|
|
721
|
+
--md3-on-primary: #ffffff;
|
|
722
|
+
--brand-glow: var(--accent-text);
|
|
723
|
+
--brand-vibrant: var(--brand-primary);
|
|
724
|
+
--btn-primary-bg: hsl(var(--hsl-primary));
|
|
725
|
+
--btn-cta-bg: var(--btn-primary-bg);
|
|
726
|
+
--btn-cta-text: var(--btn-primary-text);
|
|
727
|
+
--sidebar-primary: var(--hsl-primary);
|
|
728
|
+
--sidebar-primary-foreground: var(--hsl-primary-foreground);
|
|
729
|
+
--sidebar-ring: var(--hsl-ring);
|
|
730
|
+
--md3-primary: var(--accent-text);
|
|
731
|
+
--md3-primary-dim: light-dark(var(--btn-primary-bg), var(--brand-primary));
|
|
732
|
+
--md3-primary-container: var(--btn-primary-bg);
|
|
733
|
+
--md3-on-primary-container: light-dark(#ffffff, var(--brand-purple));
|
|
734
|
+
--md3-primary-fixed-dim: light-dark(color-mix(in srgb, var(--md3-primary-fixed) 85%, var(--accent-text)), var(--brand-cool));
|
|
735
|
+
--md3-secondary: light-dark(var(--accent-text), var(--brand-purple));
|
|
736
|
+
--md3-secondary-dim: light-dark(var(--btn-primary-bg), var(--accent-text));
|
|
737
|
+
--md3-secondary-container: light-dark(var(--md3-primary-fixed), var(--brand-strong));
|
|
738
|
+
--md3-on-secondary: light-dark(#ffffff, var(--brand-purple));
|
|
739
|
+
--md3-on-secondary-container: light-dark(var(--accent-text), var(--brand-purple));
|
|
740
|
+
--md3-surface-tint: light-dark(var(--brand-primary), var(--accent-text));
|
|
741
|
+
--accent-surface-soft: light-dark(color-mix(in srgb, var(--btn-primary-bg) 5%, transparent), color-mix(in srgb, var(--accent-text) 7%, transparent));
|
|
742
|
+
--accent-surface-strong: light-dark(color-mix(in srgb, var(--btn-primary-bg) 13%, transparent), color-mix(in srgb, var(--btn-primary-bg) 30%, transparent));
|
|
743
|
+
--bg-hover: var(--accent-surface-soft);
|
|
744
|
+
--bg-selection: light-dark(color-mix(in srgb, var(--btn-primary-bg) 16%, transparent), color-mix(in srgb, var(--brand-primary) 28%, transparent));
|
|
745
|
+
--border-accent: light-dark(color-mix(in srgb, var(--btn-primary-hover) 18%, transparent), color-mix(in srgb, var(--brand-cool) 35%, transparent));
|
|
746
|
+
--border-accent-hover: light-dark(color-mix(in srgb, var(--btn-primary-hover) 35%, transparent), color-mix(in srgb, var(--brand-cool) 50%, transparent));
|
|
747
|
+
--tangle-gradient: linear-gradient(135deg, var(--btn-primary-bg), light-dark(var(--btn-primary-hover), var(--accent-text)));
|
|
748
|
+
--tangle-gradient-text: linear-gradient(135deg, light-dark(var(--btn-primary-bg), var(--brand-purple)), light-dark(var(--btn-primary-hover), var(--accent-text)));
|
|
749
|
+
--accent-gradient-strong: linear-gradient(135deg, var(--btn-primary-bg), light-dark(var(--btn-primary-hover), var(--brand-primary)));
|
|
750
|
+
--status-creating: var(--accent-text);
|
|
751
|
+
--primary: var(--hsl-primary);
|
|
752
|
+
--primary-foreground: var(--hsl-primary-foreground);
|
|
753
|
+
--ring: var(--hsl-ring);
|
|
754
|
+
--syntax-meta: var(--accent-text);
|
|
755
|
+
--display-control-shadow: inset 0 1px 0 rgb(255 255 255 / 0.18), 0 4px 16px hsl(var(--primary) / 0.2);
|
|
756
|
+
--display-gradient: linear-gradient(135deg, hsl(var(--foreground)) 10%, var(--accent-text) 70%);
|
|
757
|
+
--focus-border: hsl(var(--hsl-ring) / 0.8);
|
|
758
|
+
--focus-halo: hsl(var(--hsl-ring) / 0.18);
|
|
759
|
+
--ix-hover: 0 0 0 1px hsl(var(--hsl-ring) / 0.45), 0 0 0 4px hsl(var(--hsl-ring) / 0.16);
|
|
760
|
+
--ix-hover-border: hsl(var(--hsl-ring) / 0.7);
|
|
761
|
+
--ix-press: 0 0 0 1px hsl(var(--hsl-ring) / 0.95), 0 0 0 4px hsl(var(--hsl-ring) / 0.3);
|
|
762
|
+
--ix-after: 0 0 0 1px hsl(var(--hsl-ring) / 0.6), 0 0 0 3px hsl(var(--hsl-ring) / 0.14);
|
|
763
|
+
--ix-sel-ring: hsl(var(--hsl-ring) / 0.85);
|
|
764
|
+
--ix-sel-bg: light-dark(hsl(var(--hsl-ring) / 0.13), hsl(var(--hsl-ring) / 0.18));
|
|
765
|
+
--ix-hover-bg: light-dark(hsl(var(--hsl-ring) / 0.07), hsl(var(--hsl-ring) / 0.08));
|
|
766
|
+
--chart-shade: var(--bg-hover);
|
|
767
|
+
--chart-accent: light-dark(hsl(var(--hsl-primary)), var(--brand-cool));
|
|
768
|
+
--color-brand: var(--brand-primary);
|
|
769
|
+
--color-brand-dim: var(--btn-primary-bg);
|
|
770
|
+
--color-brand-cool: var(--brand-cool);
|
|
771
|
+
--color-brand-glow: var(--brand-glow);
|
|
772
|
+
--color-status-creating: var(--status-creating);
|
|
773
|
+
--color-primary: hsl(var(--primary));
|
|
774
|
+
--color-primary-foreground: hsl(var(--primary-foreground));
|
|
775
|
+
--color-ring: hsl(var(--ring));
|
|
776
|
+
}
|
|
777
|
+
/* accent-scopes:end */
|
|
@@ -301,7 +301,7 @@ for (const [name, mode] of [
|
|
|
301
301
|
it("is a white page whose cards separate by shadow", () => {
|
|
302
302
|
expect(declIn(css, "hsl-background")).toBe("0 0% 100%");
|
|
303
303
|
expect(declIn(css, "hsl-card")).toBe("0 0% 100%");
|
|
304
|
-
expect(declIn(css, "shadow-card")).toMatch(/
|
|
304
|
+
expect(declIn(css, "shadow-card")).toMatch(/hsl\(var\(--shadow-color\)/);
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
307
|
});
|
package/src/styles/system.css
CHANGED
|
@@ -140,12 +140,15 @@
|
|
|
140
140
|
* action. White text passes on every stop (5.37, 5.39 and 5.18:1). */
|
|
141
141
|
--cta: linear-gradient(90deg, #5b5bd6 0%, #6e56cf 55%, #8e4ec6 100%);
|
|
142
142
|
--cta-hover: linear-gradient(90deg, #5151cd 0%, #654dc4 55%, #8347b9 100%);
|
|
143
|
-
/* Radius by role.
|
|
144
|
-
*
|
|
143
|
+
/* Radius by role, the tokens.css ramp: tag 4, control 8, card 12,
|
|
144
|
+
* container 16, pill. Chip, field and panel are the older names. */
|
|
145
145
|
--radius-tag: 4px;
|
|
146
|
-
--radius-
|
|
147
|
-
--radius-
|
|
148
|
-
--radius-
|
|
146
|
+
--radius-control: 8px;
|
|
147
|
+
--radius-card: 12px;
|
|
148
|
+
--radius-container: 16px;
|
|
149
|
+
--radius-chip: var(--radius-control);
|
|
150
|
+
--radius-field: var(--radius-card);
|
|
151
|
+
--radius-panel: var(--radius-container);
|
|
149
152
|
--radius-cover: 20px;
|
|
150
153
|
--radius-sheet: 24px;
|
|
151
154
|
--radius-pill: 999px;
|
|
@@ -191,10 +194,11 @@
|
|
|
191
194
|
--text-figure--line-height: 2.75rem;
|
|
192
195
|
--text-figure--letter-spacing: -0.02em;
|
|
193
196
|
--text-figure--font-weight: 600;
|
|
197
|
+
--shadow-color: 0 0% 0%;
|
|
194
198
|
/* Elevation. In dark, a white hairline states the edge; only floating layers cast a shadow. */
|
|
195
199
|
--shadow-1: inset 0 0 0 1px rgb(255 255 255 / 0.06);
|
|
196
|
-
--shadow-2: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px
|
|
197
|
-
--shadow-3: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px
|
|
200
|
+
--shadow-2: inset 0 0 0 1px rgb(255 255 255 / 0.08), 0 8px 24px hsl(var(--shadow-color) / 0.5);
|
|
201
|
+
--shadow-3: inset 0 0 0 1px rgb(255 255 255 / 0.1), 0 16px 48px hsl(var(--shadow-color) / 0.6);
|
|
198
202
|
--shadow-card: var(--shadow-1);
|
|
199
203
|
--shadow-dropdown: var(--shadow-2);
|
|
200
204
|
--shadow-overlay: var(--shadow-3);
|
|
@@ -332,12 +336,15 @@
|
|
|
332
336
|
* action. White text passes on every stop (5.37, 5.39 and 5.18:1). */
|
|
333
337
|
--cta: linear-gradient(90deg, #5b5bd6 0%, #6e56cf 55%, #8e4ec6 100%);
|
|
334
338
|
--cta-hover: linear-gradient(90deg, #5151cd 0%, #654dc4 55%, #8347b9 100%);
|
|
335
|
-
/* Radius by role.
|
|
336
|
-
*
|
|
339
|
+
/* Radius by role, the tokens.css ramp: tag 4, control 8, card 12,
|
|
340
|
+
* container 16, pill. Chip, field and panel are the older names. */
|
|
337
341
|
--radius-tag: 4px;
|
|
338
|
-
--radius-
|
|
339
|
-
--radius-
|
|
340
|
-
--radius-
|
|
342
|
+
--radius-control: 8px;
|
|
343
|
+
--radius-card: 12px;
|
|
344
|
+
--radius-container: 16px;
|
|
345
|
+
--radius-chip: var(--radius-control);
|
|
346
|
+
--radius-field: var(--radius-card);
|
|
347
|
+
--radius-panel: var(--radius-container);
|
|
341
348
|
--radius-cover: 20px;
|
|
342
349
|
--radius-sheet: 24px;
|
|
343
350
|
--radius-pill: 999px;
|
|
@@ -383,10 +390,11 @@
|
|
|
383
390
|
--text-figure--line-height: 2.75rem;
|
|
384
391
|
--text-figure--letter-spacing: -0.02em;
|
|
385
392
|
--text-figure--font-weight: 600;
|
|
393
|
+
--shadow-color: 220 40% 8%;
|
|
386
394
|
/* Elevation. In light, shadow separates a card from the white page. */
|
|
387
|
-
--shadow-1: 0 0 0 1px
|
|
388
|
-
--shadow-2: 0 0 0 1px
|
|
389
|
-
--shadow-3: 0 8px 28px
|
|
395
|
+
--shadow-1: 0 0 0 1px hsl(var(--shadow-color) / 0.03), 0 2px 6px hsl(var(--shadow-color) / 0.05), 0 4px 8px hsl(var(--shadow-color) / 0.08);
|
|
396
|
+
--shadow-2: 0 0 0 1px hsl(var(--shadow-color) / 0.04), 0 6px 16px hsl(var(--shadow-color) / 0.12);
|
|
397
|
+
--shadow-3: 0 8px 28px hsl(var(--shadow-color) / 0.28);
|
|
390
398
|
--shadow-card: var(--shadow-1);
|
|
391
399
|
--shadow-dropdown: var(--shadow-2);
|
|
392
400
|
--shadow-overlay: var(--shadow-3);
|
|
@@ -80,6 +80,14 @@ export const scopes: Array<{ name: string; sources: string[] }> = [
|
|
|
80
80
|
sources: [named('[data-theme="website"]:where(:not(.dark))'), LIGHT, DARK],
|
|
81
81
|
},
|
|
82
82
|
{ name: "website dark", sources: [named('.dark[data-theme="website"]'), DARK] },
|
|
83
|
+
// Every data-accent scope on each canonical mode: its role rule, then the spine.
|
|
84
|
+
...[...themes.matchAll(/^\[data-accent="([a-z]+)"\],$/gm)].flatMap(([, hue]) => {
|
|
85
|
+
const rule = blockIn(themes, `[data-accent="${hue}"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme])`);
|
|
86
|
+
return [
|
|
87
|
+
{ name: `accent ${hue} on dark`, sources: [rule, DARK] },
|
|
88
|
+
{ name: `accent ${hue} on light`, sources: [rule, LIGHT, DARK] },
|
|
89
|
+
];
|
|
90
|
+
}),
|
|
83
91
|
];
|
|
84
92
|
|
|
85
93
|
export type Rgb = [number, number, number];
|
package/src/styles/theme.css
CHANGED
|
@@ -36,12 +36,10 @@
|
|
|
36
36
|
--radius-xl: 12px;
|
|
37
37
|
--radius-full: 999px;
|
|
38
38
|
|
|
39
|
-
--shadow-card: 0 1px 2px
|
|
40
|
-
--shadow-dropdown: 0 8px 30px
|
|
39
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.3);
|
|
40
|
+
--shadow-dropdown: 0 8px 30px hsl(var(--shadow-color) / 0.45);
|
|
41
41
|
--shadow-glow: none;
|
|
42
|
-
--shadow-overlay:
|
|
43
|
-
0 2px 6px hsl(var(--hsl-foreground) / 0.1),
|
|
44
|
-
0 16px 40px hsl(var(--hsl-foreground) / 0.16);
|
|
42
|
+
--shadow-overlay: 0 2px 6px hsl(var(--shadow-color) / 0.3), 0 16px 40px hsl(var(--shadow-color) / 0.5);
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
/* Inline variable-dependent utilities so their source is resolved on the
|
package/src/styles/tokens.css
CHANGED
|
@@ -310,6 +310,46 @@
|
|
|
310
310
|
--surface-teal-bg: #18333b;
|
|
311
311
|
--surface-teal-border: #207e73;
|
|
312
312
|
--surface-teal-text: #0bd8b6;
|
|
313
|
+
--tone-violet-fill: 272 44.9% 50.2%;
|
|
314
|
+
--tone-violet-ring: 272 100% 80.8%;
|
|
315
|
+
--tone-violet-mark: #8e4ec6;
|
|
316
|
+
--tone-violet-line: #8e4ec6;
|
|
317
|
+
--tone-violet-ink: #d19dff;
|
|
318
|
+
--tone-orange-fill: 22 93.6% 36.9%;
|
|
319
|
+
--tone-orange-ring: 26 100% 67.1%;
|
|
320
|
+
--tone-orange-mark: #f76b15;
|
|
321
|
+
--tone-orange-line: #f76b15;
|
|
322
|
+
--tone-orange-ink: #ffa057;
|
|
323
|
+
--tone-teal-fill: 172 95% 23.7%;
|
|
324
|
+
--tone-teal-ring: 170 90.3% 44.5%;
|
|
325
|
+
--tone-teal-mark: #12a594;
|
|
326
|
+
--tone-teal-line: #12a594;
|
|
327
|
+
--tone-teal-ink: #0bd8b6;
|
|
328
|
+
--tone-blue-fill: 209 86.3% 40%;
|
|
329
|
+
--tone-blue-ring: 210 100% 72%;
|
|
330
|
+
--tone-blue-mark: #0090ff;
|
|
331
|
+
--tone-blue-line: #0090ff;
|
|
332
|
+
--tone-blue-ink: #70b8ff;
|
|
333
|
+
--tone-pink-fill: 322 65.1% 46.1%;
|
|
334
|
+
--tone-pink-ring: 327 100% 77.6%;
|
|
335
|
+
--tone-pink-mark: #d6409f;
|
|
336
|
+
--tone-pink-line: #d6409f;
|
|
337
|
+
--tone-pink-ink: #ff8dcc;
|
|
338
|
+
--tone-brown-fill: 24 29.6% 39%;
|
|
339
|
+
--tone-brown-ring: 28 49.7% 72%;
|
|
340
|
+
--tone-brown-mark: #ad7f58;
|
|
341
|
+
--tone-brown-line: #ad7f58;
|
|
342
|
+
--tone-brown-ink: #dbb594;
|
|
343
|
+
--tone-cyan-fill: 192 80.4% 30%;
|
|
344
|
+
--tone-cyan-ring: 190 75.5% 60%;
|
|
345
|
+
--tone-cyan-mark: #00a2c7;
|
|
346
|
+
--tone-cyan-line: #00a2c7;
|
|
347
|
+
--tone-cyan-ink: #4ccce6;
|
|
348
|
+
--tone-lime-fill: 84 44.2% 30.2%;
|
|
349
|
+
--tone-lime-ring: 80 69.9% 66.1%;
|
|
350
|
+
--tone-lime-mark: #bdee63;
|
|
351
|
+
--tone-lime-line: #bdee63;
|
|
352
|
+
--tone-lime-ink: #bde56c;
|
|
313
353
|
/* tones:end */
|
|
314
354
|
/* Run-mix ramp — the fills of a proportional status bar.
|
|
315
355
|
*
|
|
@@ -374,23 +414,18 @@
|
|
|
374
414
|
--code-comment: var(--syntax-comment);
|
|
375
415
|
--code-error: var(--syntax-error);
|
|
376
416
|
|
|
377
|
-
/* Shadows — flat, no inner ring, no
|
|
378
|
-
|
|
379
|
-
|
|
417
|
+
/* Shadows — flat, no inner ring, no glow. Every shadow is one colour,
|
|
418
|
+
--shadow-color, as HSL channels so each sets its own alpha: black here, a
|
|
419
|
+
deep cool ink in light. A shadow never inverts into a light halo; dark
|
|
420
|
+
elevation is the plane step plus --edge-highlight. --shadow-overlay is the
|
|
421
|
+
lift above --shadow-dropdown, for a surface that covers the page (a
|
|
422
|
+
drawer, a modal, a floating dock): a tight contact layer plus a wide one. */
|
|
423
|
+
--shadow-color: 0 0% 0%;
|
|
424
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.3);
|
|
425
|
+
--shadow-dropdown: 0 8px 30px hsl(var(--shadow-color) / 0.45);
|
|
380
426
|
--shadow-glow: none;
|
|
381
|
-
--shadow-accent:
|
|
382
|
-
|
|
383
|
-
rather than sitting beside it: a drawer, a modal, a floating dock.
|
|
384
|
-
Two layers — a tight contact shadow plus a wide ambient one.
|
|
385
|
-
Alone among the shadows this is derived from --hsl-foreground instead of
|
|
386
|
-
a fixed rgba, so it INVERTS with the theme. That is what makes it work in
|
|
387
|
-
dark: a black shadow on a dark canvas renders as nothing (see the ladder
|
|
388
|
-
note at the top of this file), while the foreground is near-white there,
|
|
389
|
-
so the same declaration reads as a soft halo. One definition therefore
|
|
390
|
-
serves both themes and the light block does not restate it. */
|
|
391
|
-
--shadow-overlay:
|
|
392
|
-
0 2px 6px hsl(var(--hsl-foreground) / 0.1),
|
|
393
|
-
0 16px 40px hsl(var(--hsl-foreground) / 0.16);
|
|
427
|
+
--shadow-accent: var(--shadow-card);
|
|
428
|
+
--shadow-overlay: 0 2px 6px hsl(var(--shadow-color) / 0.3), 0 16px 40px hsl(var(--shadow-color) / 0.5);
|
|
394
429
|
/* Scrim and media ink. A scrim dims the page behind a modal or sheet, or
|
|
395
430
|
darkens a photo under its caption. It is black in every theme: a dimming
|
|
396
431
|
layer that inverted in dark mode would brighten the page it covers.
|
|
@@ -410,12 +445,18 @@
|
|
|
410
445
|
--radius-pill: 999px;
|
|
411
446
|
--radius-full: 999px;
|
|
412
447
|
--radius-composer: 26px; /* centered composer pill */
|
|
413
|
-
/* Radius by role
|
|
414
|
-
|
|
448
|
+
/* Radius by role, one ramp: tag 4, control 8, card 12, container 16, and
|
|
449
|
+
--radius-pill for every chip and status pill. Chip, field and panel are
|
|
450
|
+
the older names of control, card and container. Cover and sheet are the
|
|
451
|
+
media and bottom-sheet radii. system.css repeats these for pages that load
|
|
452
|
+
it on its own ladders. */
|
|
415
453
|
--radius-tag: 4px;
|
|
416
|
-
--radius-
|
|
417
|
-
--radius-
|
|
418
|
-
--radius-
|
|
454
|
+
--radius-control: var(--radius-md);
|
|
455
|
+
--radius-card: var(--radius-xl);
|
|
456
|
+
--radius-container: 16px;
|
|
457
|
+
--radius-chip: var(--radius-control);
|
|
458
|
+
--radius-field: var(--radius-card);
|
|
459
|
+
--radius-panel: var(--radius-container);
|
|
419
460
|
--radius-cover: 20px;
|
|
420
461
|
--radius-sheet: 24px;
|
|
421
462
|
--radius: 0.5rem; /* 8px chrome default (was 10px) */
|
|
@@ -527,6 +568,11 @@
|
|
|
527
568
|
--motion-dismiss: var(--duration-instant) var(--ease-exit);
|
|
528
569
|
--motion-stream: var(--duration-stream) var(--ease-expo);
|
|
529
570
|
--motion-arrive: var(--duration-arrive) var(--ease-expo);
|
|
571
|
+
/* The only loop period on a page. A running mark breathes on it with
|
|
572
|
+
`tangle-breathe` (opacity, never position or scale), so every live thing
|
|
573
|
+
shares one rhythm. Reduced motion keeps it: see the rule below. */
|
|
574
|
+
--duration-ambient: 2000ms;
|
|
575
|
+
--motion-ambient: var(--duration-ambient) ease-in-out;
|
|
530
576
|
|
|
531
577
|
/* Elapsed interaction thresholds, not animation durations. See
|
|
532
578
|
* docs/design-bible.md#interaction. Reduced motion leaves these unchanged. */
|
|
@@ -709,6 +755,46 @@
|
|
|
709
755
|
--surface-teal-bg: #e0f8f3;
|
|
710
756
|
--surface-teal-border: #a1ded2;
|
|
711
757
|
--surface-teal-text: #017d6d;
|
|
758
|
+
--tone-violet-fill: 272 44.9% 50.2%;
|
|
759
|
+
--tone-violet-ring: 272 44.9% 50.2%;
|
|
760
|
+
--tone-violet-mark: #8e4ec6;
|
|
761
|
+
--tone-violet-line: #8e4ec6;
|
|
762
|
+
--tone-violet-ink: #753ea6;
|
|
763
|
+
--tone-orange-fill: 22 93.6% 36.9%;
|
|
764
|
+
--tone-orange-ring: 22 93.6% 36.9%;
|
|
765
|
+
--tone-orange-mark: #f76b15;
|
|
766
|
+
--tone-orange-line: #cc4e00;
|
|
767
|
+
--tone-orange-ink: #953d0e;
|
|
768
|
+
--tone-teal-fill: 172 95% 23.7%;
|
|
769
|
+
--tone-teal-ring: 172 95% 23.7%;
|
|
770
|
+
--tone-teal-mark: #12a594;
|
|
771
|
+
--tone-teal-line: #008573;
|
|
772
|
+
--tone-teal-ink: #066558;
|
|
773
|
+
--tone-blue-fill: 209 86.3% 40%;
|
|
774
|
+
--tone-blue-ring: 209 86.3% 40%;
|
|
775
|
+
--tone-blue-mark: #0090ff;
|
|
776
|
+
--tone-blue-line: #0588f0;
|
|
777
|
+
--tone-blue-ink: #0f59a3;
|
|
778
|
+
--tone-pink-fill: 322 65.1% 46.1%;
|
|
779
|
+
--tone-pink-ring: 322 65.1% 46.1%;
|
|
780
|
+
--tone-pink-mark: #d6409f;
|
|
781
|
+
--tone-pink-line: #d6409f;
|
|
782
|
+
--tone-pink-ink: #a02071;
|
|
783
|
+
--tone-brown-fill: 24 29.6% 39%;
|
|
784
|
+
--tone-brown-ring: 24 29.6% 39%;
|
|
785
|
+
--tone-brown-mark: #ad7f58;
|
|
786
|
+
--tone-brown-line: #ad7f58;
|
|
787
|
+
--tone-brown-ink: #725440;
|
|
788
|
+
--tone-cyan-fill: 192 80.4% 30%;
|
|
789
|
+
--tone-cyan-ring: 192 80.4% 30%;
|
|
790
|
+
--tone-cyan-mark: #00a2c7;
|
|
791
|
+
--tone-cyan-line: #107d98;
|
|
792
|
+
--tone-cyan-ink: #0f6277;
|
|
793
|
+
--tone-lime-fill: 84 44.2% 30.2%;
|
|
794
|
+
--tone-lime-ring: 84 44.2% 30.2%;
|
|
795
|
+
--tone-lime-mark: #bdee63;
|
|
796
|
+
--tone-lime-line: #5c7c2f;
|
|
797
|
+
--tone-lime-ink: #4b6126;
|
|
712
798
|
/* tones:end */
|
|
713
799
|
/* Run-mix ramp — the fills of a proportional status bar.
|
|
714
800
|
*
|
|
@@ -737,11 +823,11 @@
|
|
|
737
823
|
--glass-bg-strong: var(--depth-1);
|
|
738
824
|
--code-header-bg: color-mix(in srgb, hsl(var(--hsl-muted)) 50%, transparent);
|
|
739
825
|
|
|
740
|
-
/* Shadows —
|
|
741
|
-
--shadow-
|
|
742
|
-
--shadow-
|
|
743
|
-
--shadow-
|
|
744
|
-
--shadow-
|
|
826
|
+
/* Shadows — the dark roles in the light shadow colour, at light alphas. */
|
|
827
|
+
--shadow-color: 220 40% 8%;
|
|
828
|
+
--shadow-card: 0 1px 2px hsl(var(--shadow-color) / 0.06);
|
|
829
|
+
--shadow-dropdown: 0 8px 30px hsl(var(--shadow-color) / 0.1);
|
|
830
|
+
--shadow-overlay: 0 2px 6px hsl(var(--shadow-color) / 0.1), 0 16px 40px hsl(var(--shadow-color) / 0.16);
|
|
745
831
|
}
|
|
746
832
|
|
|
747
833
|
|
|
@@ -940,7 +1026,11 @@
|
|
|
940
1026
|
|
|
941
1027
|
|
|
942
1028
|
/* Motion is opt-in at the component, but its reduced-motion preference is
|
|
943
|
-
* shared. Re-declare composites at the same boundary as their durations.
|
|
1029
|
+
* shared. Re-declare composites at the same boundary as their durations.
|
|
1030
|
+
* --duration-ambient is not zeroed: the essential running mark
|
|
1031
|
+
* (data-motion="essential") keeps its opacity-only breathe, because it is the
|
|
1032
|
+
* only "still working" signal and it never moves anything. */
|
|
1033
|
+
@keyframes tangle-breathe { 50% { opacity: 0.45; } }
|
|
944
1034
|
@media (prefers-reduced-motion: reduce) {
|
|
945
1035
|
:root,
|
|
946
1036
|
.dark,
|
|
@@ -5,10 +5,12 @@ import {
|
|
|
5
5
|
blockIn,
|
|
6
6
|
compositeOver,
|
|
7
7
|
contrastRatio,
|
|
8
|
+
declIn,
|
|
8
9
|
hexIn,
|
|
9
10
|
hexRelativeLuminanceIn,
|
|
10
11
|
hslIn,
|
|
11
12
|
hslToRgb,
|
|
13
|
+
resolveIn,
|
|
12
14
|
} from "./css-test-utils";
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -608,3 +610,75 @@ describe("each colour is declared once: a role, then aliases", () => {
|
|
|
608
610
|
expect(literalsOutsideTones(spine).size).toBeLessThanOrEqual(ceiling);
|
|
609
611
|
});
|
|
610
612
|
});
|
|
613
|
+
|
|
614
|
+
describe("shadow, radius and loop period are one role each", () => {
|
|
615
|
+
// The run-UI critique of 2026-10-27 (fixes 9, 10 and 22) found a white shadow
|
|
616
|
+
// haze in dark mode, six radii and seven loop periods. Each is now one role
|
|
617
|
+
// that every other name aliases, so a surface cannot drift to a second value.
|
|
618
|
+
it.each([
|
|
619
|
+
["dark", DARK, "0 0% 0%"],
|
|
620
|
+
["light", LIGHT, "220 40% 8%"],
|
|
621
|
+
] as const)("%s: every shadow is drawn in --shadow-color and never inverts", (_, spine, colour) => {
|
|
622
|
+
expect(declIn(spine, "shadow-color")).toBe(colour);
|
|
623
|
+
for (const token of ["shadow-card", "shadow-dropdown", "shadow-overlay"]) {
|
|
624
|
+
const value = declIn(spine, token);
|
|
625
|
+
expect(value, `--${token}`).toMatch(/^[^,]*hsl\(var\(--shadow-color\) \/ [\d.]+\)/);
|
|
626
|
+
expect(value, `--${token} names a colour of its own`).not.toMatch(/rgba?\(|#[0-9a-f]{3,8}\b|--hsl-foreground/i);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("draws radii from one ramp, and the older role names alias it", () => {
|
|
631
|
+
const ramp = { tag: "4px", control: "8px", card: "12px", container: "16px", pill: "999px" };
|
|
632
|
+
for (const [role, px] of Object.entries(ramp)) expect(resolveIn(DARK, `radius-${role}`), role).toBe(px);
|
|
633
|
+
for (const [older, role] of [["chip", "control"], ["field", "card"], ["panel", "container"]]) {
|
|
634
|
+
expect(declIn(DARK, `radius-${older}`)).toBe(`var(--radius-${role})`);
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it("has one loop period, which reduced motion keeps for an opacity-only breathe", () => {
|
|
639
|
+
expect(declIn(DARK, "duration-ambient")).toBe("2000ms");
|
|
640
|
+
expect(declIn(DARK, "motion-ambient")).toBe("var(--duration-ambient) ease-in-out");
|
|
641
|
+
const reduced = tokens.match(/@media \(prefers-reduced-motion: reduce\) \{([\s\S]*?)\n\}/)![1];
|
|
642
|
+
expect(reduced).toContain("--duration-fast: 0ms");
|
|
643
|
+
expect(reduced, "the essential breathe keeps its period").not.toContain("--duration-ambient");
|
|
644
|
+
const breathe = tokens.match(/@keyframes tangle-breathe \{([\s\S]*?)\}\s*\}/)?.[1];
|
|
645
|
+
expect(breathe, "the shared breathe keyframe").toMatch(/opacity/);
|
|
646
|
+
expect(breathe, "a loop never moves or scales").not.toMatch(/transform|translate|scale|rotate/);
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
describe("data-accent scopes retint the accent and nothing else", () => {
|
|
651
|
+
// Palette plan item 6: a product sets data-accent="<category>" on <html> or a
|
|
652
|
+
// theme boundary. Each hue's roles are generated (gen-ladders.mjs) into the
|
|
653
|
+
// tone regions, which the ratchet above already counts as generated tones.
|
|
654
|
+
const themes = readFileSync(path.resolve(import.meta.dirname, "named-themes.css"), "utf8");
|
|
655
|
+
const region = themes.match(/\/\* accent-scopes:begin[\s\S]*?accent-scopes:end \*\//)?.[0] ?? "";
|
|
656
|
+
const hues = [...region.matchAll(/^\[data-accent="([a-z]+)"\],$/gm)].map((m) => m[1]);
|
|
657
|
+
|
|
658
|
+
it("scopes every categorical hue", () => {
|
|
659
|
+
expect(hues).toEqual(["violet", "orange", "teal", "blue", "pink", "brown", "cyan", "lime"]);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
for (const [theme, spine] of [
|
|
663
|
+
["dark", DARK],
|
|
664
|
+
["light", LIGHT],
|
|
665
|
+
] as const) {
|
|
666
|
+
it.each(hues)(`${theme} %s: the accent ink clears 4.5:1 on the page and the card`, (hue) => {
|
|
667
|
+
for (const plane of ["md3-surface", "md3-surface-container"]) {
|
|
668
|
+
expect(contrastRatio(hexIn(spine, `tone-${hue}-ink`), hexIn(spine, plane)), plane).toBeGreaterThanOrEqual(4.5);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
it("maps the five roles to the hue and re-declares no plane, body ink or status", () => {
|
|
674
|
+
for (const hue of hues) {
|
|
675
|
+
const rule = blockIn(region, `[data-accent="${hue}"] :is(.light, .dark, [data-theme], [data-sandbox-ui], [data-sandbox-theme])`);
|
|
676
|
+
expect([...rule.matchAll(/--([a-z-]+): var\(--tone-([a-z]+)-([a-z]+)\);/g)].map((m) => `${m[1]}=${m[2]}-${m[3]}`)).toEqual([
|
|
677
|
+
`hsl-primary=${hue}-fill`, `hsl-ring=${hue}-ring`, `brand-primary=${hue}-mark`, `brand-cool=${hue}-line`, `accent-text=${hue}-ink`,
|
|
678
|
+
]);
|
|
679
|
+
}
|
|
680
|
+
const declared = [...region.matchAll(/--([a-z0-9-]+):/g)].map((m) => m[1]);
|
|
681
|
+
const untouchable = /^(hsl-(background|card|popover|secondary|muted|accent|border|input|foreground|muted-foreground|success|warning|destructive|info)|text-|md3-(surface($|-(container|dim|bright|variant))|on-surface|outline)|depth-|bg-(root|card|elevated|dark|section|input)|surface-|status-(running|stopped|warm|cold|error|deleted))/;
|
|
682
|
+
expect(declared.filter((t) => untouchable.test(t))).toEqual([]);
|
|
683
|
+
});
|
|
684
|
+
});
|