@attrus-ds/core 1.0.6 → 1.0.11

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.
Files changed (50) hide show
  1. package/dist/css/components/action-bar.css +93 -0
  2. package/dist/css/components/ambient.css +97 -0
  3. package/dist/css/components/animated-bg.css +261 -0
  4. package/dist/css/components/buttons.css +4 -4
  5. package/dist/css/components/callout.css +4 -3
  6. package/dist/css/components/cards.css +4 -3
  7. package/dist/css/components/combobox.css +16 -13
  8. package/dist/css/components/inputs.css +99 -15
  9. package/dist/css/components/number-input.css +6 -3
  10. package/dist/css/components/primitives.css +20 -15
  11. package/dist/css/components/sel-card.css +212 -0
  12. package/dist/css/components/sidebar.css +11 -9
  13. package/dist/css/components/smart-search.css +13 -11
  14. package/dist/css/components/status-pills.css +1 -1
  15. package/dist/css/components/stepper.css +81 -4
  16. package/dist/css/components/table.css +168 -7
  17. package/dist/css/components/tabs.css +4 -3
  18. package/dist/css/components/toast.css +2 -2
  19. package/dist/css/components/toggle-chip.css +137 -0
  20. package/dist/css/components/tooltip.css +8 -5
  21. package/dist/css/components/topbar.css +8 -7
  22. package/dist/css/index.css +5 -0
  23. package/dist/css/tokens.css +126 -22
  24. package/dist/react/ActionBar/ActionBar.d.ts +19 -0
  25. package/dist/react/ActionBar/ActionBar.tsx +54 -0
  26. package/dist/react/AmbientBackdrop/AmbientBackdrop.d.ts +17 -0
  27. package/dist/react/AmbientBackdrop/AmbientBackdrop.tsx +53 -0
  28. package/dist/react/AnimatedBackdrop/AnimatedBackdrop.d.ts +22 -0
  29. package/dist/react/AnimatedBackdrop/AnimatedBackdrop.tsx +317 -0
  30. package/dist/react/Callout/Callout.d.ts +1 -1
  31. package/dist/react/Callout/Callout.tsx +1 -1
  32. package/dist/react/Combobox/Combobox.d.ts +2 -0
  33. package/dist/react/Combobox/Combobox.tsx +85 -39
  34. package/dist/react/DataTable/DataTable.tsx +3 -3
  35. package/dist/react/Drawer/Drawer.tsx +7 -1
  36. package/dist/react/Input/Input.d.ts +1 -1
  37. package/dist/react/Input/Input.tsx +1 -1
  38. package/dist/react/Modal/Modal.tsx +8 -1
  39. package/dist/react/SelectableCard/SelectableCard.d.ts +39 -0
  40. package/dist/react/SelectableCard/SelectableCard.tsx +94 -0
  41. package/dist/react/SmartSearch/SmartSearch.tsx +2 -2
  42. package/dist/react/Stepper/Stepper.d.ts +3 -0
  43. package/dist/react/Stepper/Stepper.tsx +7 -0
  44. package/dist/react/Toast/Toast.d.ts +1 -1
  45. package/dist/react/Toast/Toast.tsx +1 -1
  46. package/dist/react/ToggleChip/ToggleChip.d.ts +29 -0
  47. package/dist/react/ToggleChip/ToggleChip.tsx +76 -0
  48. package/dist/react/Tooltip/Tooltip.d.ts +1 -1
  49. package/dist/react/Tooltip/Tooltip.tsx +1 -1
  50. package/package.json +1 -1
@@ -0,0 +1,137 @@
1
+ /* ============================================================
2
+ TOGGLE CHIP — components/toggle-chip.css
3
+ A chip that is a FORM CONTROL: multi-select filters, tag pickers,
4
+ preference pills. Uses aria-pressed (button) or a hidden checkbox.
5
+
6
+ Why it exists: `.chip` is a removable token (it ships `.chip-x`) and
7
+ `.pill.interactive` is a status pill — neither is a selection control.
8
+
9
+ Structure: .toggle-chip > [icon] label [.toggle-chip-count]
10
+ States: :hover · [aria-pressed="true"] / .is-on · :disabled · :focus-visible
11
+ Variants: .toggle-chip-sm · .on-inverse
12
+ ============================================================ */
13
+
14
+ .toggle-chip {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: var(--space-2);
18
+ height: 32px;
19
+ padding: 0 var(--space-4);
20
+ background: var(--color-background-surface);
21
+ border: 1px solid var(--color-border-default);
22
+ border-radius: var(--radius-pill);
23
+ font-family: var(--font-sans);
24
+ font-size: var(--fs-sm);
25
+ font-weight: var(--w-regular);
26
+ color: var(--color-foreground-secondary);
27
+ cursor: pointer;
28
+ white-space: nowrap;
29
+ box-sizing: border-box;
30
+ transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
31
+ }
32
+ .toggle-chip > input {
33
+ position: absolute;
34
+ opacity: 0;
35
+ pointer-events: none;
36
+ width: 0;
37
+ height: 0;
38
+ }
39
+ .toggle-chip svg,
40
+ .toggle-chip i {
41
+ width: var(--icon-xs);
42
+ height: var(--icon-xs);
43
+ stroke-width: var(--icon-stroke);
44
+ flex-shrink: 0;
45
+ }
46
+ .toggle-chip:hover {
47
+ border-color: var(--color-border-strong);
48
+ background: var(--color-background-hover);
49
+ color: var(--color-foreground-default);
50
+ }
51
+
52
+ /* Trailing count */
53
+ .toggle-chip-count {
54
+ font-family: var(--font-mono);
55
+ font-size-adjust: var(--font-mono-adjust);
56
+ font-size: var(--fs-xxs);
57
+ font-weight: var(--w-regular);
58
+ color: var(--color-foreground-muted);
59
+ font-variant-numeric: tabular-nums;
60
+ }
61
+
62
+ /* ---------- On ---------- */
63
+ .toggle-chip[aria-pressed="true"],
64
+ .toggle-chip.is-on,
65
+ .toggle-chip:has(input:checked) {
66
+ background: var(--color-background-selected);
67
+ border-color: var(--color-brand-solid);
68
+ color: var(--color-foreground-brand);
69
+ font-weight: var(--w-regular);
70
+ }
71
+ .toggle-chip[aria-pressed="true"] .toggle-chip-count,
72
+ .toggle-chip.is-on .toggle-chip-count,
73
+ .toggle-chip:has(input:checked) .toggle-chip-count { color: var(--color-foreground-brand); }
74
+
75
+ /* ---------- Focus ---------- */
76
+ .toggle-chip:focus-visible,
77
+ .toggle-chip:has(input:focus-visible) {
78
+ outline: none;
79
+ box-shadow: var(--shadow-focus);
80
+ }
81
+
82
+ /* ---------- Disabled ---------- */
83
+ .toggle-chip:disabled,
84
+ .toggle-chip[aria-disabled="true"],
85
+ .toggle-chip:has(input:disabled) {
86
+ cursor: not-allowed;
87
+ background: var(--color-background-sunken);
88
+ border-color: var(--color-border-default);
89
+ color: var(--color-foreground-disabled);
90
+ }
91
+ .toggle-chip:disabled .toggle-chip-count,
92
+ .toggle-chip[aria-disabled="true"] .toggle-chip-count,
93
+ .toggle-chip:has(input:disabled) .toggle-chip-count { color: var(--color-foreground-disabled); }
94
+
95
+ /* ---------- Dense ---------- */
96
+ .toggle-chip-sm {
97
+ height: 26px;
98
+ padding: 0 var(--space-3);
99
+ font-size: var(--fs-xs);
100
+ }
101
+ .toggle-chip-sm svg,
102
+ .toggle-chip-sm i { width: 12px; height: 12px; }
103
+
104
+ /* ---------- On inverse ---------- */
105
+ .toggle-chip.on-inverse {
106
+ background: color-mix(in srgb, var(--color-pure-white) 8%, transparent);
107
+ border-color: var(--color-border-inverse);
108
+ color: var(--color-foreground-inverse-secondary);
109
+ }
110
+ .toggle-chip.on-inverse:hover {
111
+ background: color-mix(in srgb, var(--color-pure-white) 14%, transparent);
112
+ border-color: color-mix(in srgb, var(--color-pure-white) 30%, transparent);
113
+ color: var(--color-foreground-inverse-default);
114
+ }
115
+ .toggle-chip.on-inverse[aria-pressed="true"],
116
+ .toggle-chip.on-inverse.is-on,
117
+ .toggle-chip.on-inverse:has(input:checked) {
118
+ background: color-mix(in srgb, var(--color-accent-solid) 22%, transparent);
119
+ border-color: var(--color-accent-solid);
120
+ color: var(--color-foreground-inverse-default);
121
+ }
122
+ .toggle-chip.on-inverse .toggle-chip-count { color: var(--color-foreground-inverse-muted); }
123
+ .toggle-chip.on-inverse:focus-visible,
124
+ .toggle-chip.on-inverse:has(input:focus-visible) { box-shadow: var(--shadow-focus-inverse); }
125
+ .toggle-chip.on-inverse:disabled,
126
+ .toggle-chip.on-inverse[aria-disabled="true"] {
127
+ background: color-mix(in srgb, var(--color-pure-white) 3%, transparent);
128
+ color: var(--color-foreground-inverse-muted);
129
+ border-color: var(--color-border-inverse);
130
+ }
131
+
132
+ /* ---------- Group ---------- */
133
+ .toggle-chip-group {
134
+ display: flex;
135
+ flex-wrap: wrap;
136
+ gap: var(--space-2);
137
+ }
@@ -19,7 +19,7 @@
19
19
  color: var(--color-foreground-inverse-default);
20
20
  font-family: var(--font-sans);
21
21
  font-size: var(--fs-xs);
22
- font-weight: var(--w-medium);
22
+ font-weight: var(--w-regular);
23
23
  line-height: var(--lh-tight);
24
24
  letter-spacing: var(--ls-body);
25
25
  white-space: normal;
@@ -113,7 +113,7 @@
113
113
  .tooltip-rich.tooltip-left::before { top: 0; bottom: 0; right: calc(-1 * var(--space-2)); width: var(--space-2); }
114
114
  .tooltip-rich.tooltip-right::before { top: 0; bottom: 0; left: calc(-1 * var(--space-2)); width: var(--space-2); }
115
115
  .tooltip-rich .tooltip-title {
116
- font-weight: var(--w-semibold);
116
+ font-weight: var(--w-regular);
117
117
  font-size: var(--fs-sm);
118
118
  margin: 0 0 var(--space-1);
119
119
  color: var(--color-foreground-inverse-default);
@@ -128,8 +128,9 @@
128
128
  .tooltip-rich .tooltip-meta {
129
129
  display: block;
130
130
  font-family: var(--font-mono);
131
+ font-size-adjust: var(--font-mono-adjust);
131
132
  font-size: var(--fs-xxs);
132
- font-weight: var(--w-semibold);
133
+ font-weight: var(--w-regular);
133
134
  letter-spacing: var(--ls-caps);
134
135
  text-transform: uppercase;
135
136
  color: var(--color-foreground-inverse-muted);
@@ -190,8 +191,9 @@
190
191
  .tooltip-rich .tooltip-row .v {
191
192
  color: var(--color-foreground-inverse-default);
192
193
  font-family: var(--font-mono);
194
+ font-size-adjust: var(--font-mono-adjust);
193
195
  font-variant-numeric: tabular-nums;
194
- font-weight: var(--w-medium);
196
+ font-weight: var(--w-regular);
195
197
  }
196
198
  .tooltip-rich .tooltip-footer {
197
199
  margin-top: var(--space-3);
@@ -215,8 +217,9 @@
215
217
  border: 1px solid var(--color-border-inverse);
216
218
  border-radius: var(--radius-xs);
217
219
  font-family: var(--font-mono);
220
+ font-size-adjust: var(--font-mono-adjust);
218
221
  font-size: var(--fs-xxs);
219
- font-weight: var(--w-medium);
222
+ font-weight: var(--w-regular);
220
223
  line-height: 1;
221
224
  }
222
225
 
@@ -40,7 +40,7 @@
40
40
  border: 1px solid var(--color-border-default);
41
41
  background: var(--color-background-surface);
42
42
  color: var(--color-foreground-secondary);
43
- font-family: inherit; font-size: var(--fs-md); font-weight: var(--w-medium);
43
+ font-family: inherit; font-size: var(--fs-md); font-weight: var(--w-regular);
44
44
  white-space: nowrap; cursor: pointer;
45
45
  transition: var(--transition-fast);
46
46
  }
@@ -54,7 +54,7 @@
54
54
  (buttons.css), NOT a topbar-local style. Kept out of here on purpose for single source. */
55
55
 
56
56
  /* Lang label inside a control */
57
- .tb-control .tb-flag-label { font-size: var(--fs-sm); font-weight: var(--w-semibold); color: var(--color-foreground-secondary); }
57
+ .tb-control .tb-flag-label { font-size: var(--fs-sm); font-weight: var(--w-regular); color: var(--color-foreground-secondary); }
58
58
  .tb-control .chev { opacity: .6; }
59
59
  .tb-control .chev svg, .tb-control .chev i { width: 11px; height: 11px; }
60
60
 
@@ -66,8 +66,9 @@
66
66
  background: var(--color-success-500);
67
67
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-success-500) 15%, transparent);
68
68
  }
69
- .tb-live .lbl { font-size: var(--fs-sm); font-weight: var(--w-semibold); }
70
- .tb-clock { font-family: var(--font-mono); font-size: var(--fs-xxs); color: var(--color-foreground-muted); margin-left: var(--space-1); }
69
+ .tb-live .lbl { font-size: var(--fs-sm); font-weight: var(--w-regular); }
70
+ .tb-clock { font-family: var(--font-mono);
71
+ font-size-adjust: var(--font-mono-adjust); font-size: var(--fs-xxs); color: var(--color-foreground-muted); margin-left: var(--space-1); }
71
72
 
72
73
  /* Notification button + unread badge */
73
74
  .tb-bell .badge-anchor { position: relative; display: inline-flex; }
@@ -113,7 +114,7 @@
113
114
  .tb-account.is-open { border-color: var(--color-border-strong); background: var(--color-background-hover); }
114
115
  .tb-account .tb-avatar { width: 24px; height: 24px; font-size: 9px; margin-left: 0; }
115
116
  .tb-account-id { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.1; min-width: 0; }
116
- .tb-account-id .nm { font-size: var(--fs-xs); font-weight: var(--w-semibold); color: var(--color-foreground-default); white-space: nowrap; }
117
+ .tb-account-id .nm { font-size: var(--fs-xs); font-weight: var(--w-regular); color: var(--color-foreground-default); white-space: nowrap; }
117
118
  .tb-account-id .co { font-size: var(--fs-xxs); color: var(--color-foreground-muted); white-space: nowrap; }
118
119
  .tb-account .chev { display: inline-flex; color: var(--color-foreground-muted); margin-left: var(--space-1); }
119
120
  .tb-account .chev svg, .tb-account .chev i { width: 12px; height: 12px; }
@@ -129,14 +130,14 @@
129
130
  .tb-account-menu-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-2) var(--space-3); }
130
131
  .tb-account-menu .tb-avatar.lg { width: 36px; height: 36px; font-size: var(--fs-sm); margin-left: 0; }
131
132
  .tb-account-menu-id { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
132
- .tb-account-menu-id .nm { font-size: var(--fs-md); font-weight: var(--w-semibold); color: var(--color-foreground-default); white-space: nowrap; }
133
+ .tb-account-menu-id .nm { font-size: var(--fs-md); font-weight: var(--w-regular); color: var(--color-foreground-default); white-space: nowrap; }
133
134
  .tb-account-menu-id .co { font-size: var(--fs-xs); color: var(--color-foreground-muted); white-space: nowrap; }
134
135
  .tb-account-menu-sep { height: 1px; background: var(--color-border-subtle); margin: var(--space-1) calc(-1 * var(--space-2)); }
135
136
  .tb-account-menu-item {
136
137
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
137
138
  padding: var(--space-2-5) var(--space-2); margin: 1px 0;
138
139
  border: 0; border-radius: var(--radius-sm); background: transparent; cursor: pointer;
139
- font-family: inherit; font-size: var(--fs-sm); font-weight: var(--w-medium);
140
+ font-family: inherit; font-size: var(--fs-sm); font-weight: var(--w-regular);
140
141
  color: var(--color-foreground-secondary); text-align: left; white-space: nowrap;
141
142
  transition: background var(--transition-fast), color var(--transition-fast);
142
143
  }
@@ -4,6 +4,9 @@
4
4
  @import "./fonts.css";
5
5
  @import "./tokens.css";
6
6
 
7
+ @import "./components/action-bar.css";
8
+ @import "./components/ambient.css";
9
+ @import "./components/animated-bg.css";
7
10
  @import "./components/buttons.css";
8
11
  @import "./components/callout.css";
9
12
  @import "./components/cards.css";
@@ -13,6 +16,7 @@
13
16
  @import "./components/number-input.css";
14
17
  @import "./components/overlays.css";
15
18
  @import "./components/primitives.css";
19
+ @import "./components/sel-card.css";
16
20
  @import "./components/sidebar.css";
17
21
  @import "./components/smart-search.css";
18
22
  @import "./components/status-pills.css";
@@ -20,5 +24,6 @@
20
24
  @import "./components/table.css";
21
25
  @import "./components/tabs.css";
22
26
  @import "./components/toast.css";
27
+ @import "./components/toggle-chip.css";
23
28
  @import "./components/tooltip.css";
24
29
  @import "./components/topbar.css";
@@ -182,7 +182,12 @@
182
182
  --color-brand-solid: var(--color-brand-primary-500); /* Enterprise primary — ATTRUS Navy #002142 (buttons, avatars, toggles, badges, hero) */
183
183
  --color-brand-solid-hover: color-mix(in srgb, var(--color-pure-white) 12%, var(--color-brand-primary-500)); /* navy is near-ink → states LIGHTEN */
184
184
  --color-brand-solid-active: color-mix(in srgb, var(--color-pure-white) 6%, var(--color-brand-primary-500));
185
- --color-brand-text: var(--color-brand-primary-500); /* navy as text — 15:1 on white */
185
+ /* Brand text on a FIXED LIGHT background (white / --color-brand-subtle-solid).
186
+ Stays navy in both themes on purpose — 15:1 there. NEVER use on the ambient
187
+ surface: it resolves 1.05:1 in dark. For ambient brand text use the
188
+ theme-aware --color-foreground-brand. */
189
+ --color-brand-text-on-light: var(--color-brand-primary-500);
190
+ --color-brand-text: var(--color-brand-text-on-light); /* DEPRECATED alias — use -on-light (fixed bg) or --color-foreground-brand (ambient) */
186
191
  --color-brand-text-strong: var(--color-brand-primary-700); /* brand text pressed/emphasis */
187
192
  --color-brand-subtle: color-mix(in srgb, var(--color-brand-primary-500) 12%, transparent); /* tints: brand chips, selected, tags */
188
193
  --color-brand-subtle-solid: var(--color-brand-primary-50); /* opaque light brand tint: brand avatars, selected-row base */
@@ -251,9 +256,13 @@
251
256
  --color-foreground-inverse-disabled: var(--color-neutral-600);
252
257
 
253
258
  /* Borders */
254
- --color-border-subtle: var(--color-neutral-100);
255
- --color-border-default: var(--color-neutral-150);
256
- --color-border-strong: var(--color-neutral-200);
259
+ /* Borders — ink-alpha hairlines. Transparency instead of a fixed neutral so the
260
+ line takes the hue of whatever sits under it: it reads as a seam, not a rule.
261
+ Semantic borders below (focus, accent, feedback) stay solid — they carry
262
+ meaning and must remain legible. */
263
+ --color-border-subtle: color-mix(in srgb, var(--color-ink-base) 4%, transparent);
264
+ --color-border-default: color-mix(in srgb, var(--color-ink-base) 7%, transparent);
265
+ --color-border-strong: color-mix(in srgb, var(--color-ink-base) 12%, transparent);
257
266
  --color-border-focus: var(--color-accent-solid); /* per-theme accent (Sky default) — follows the product's accent */
258
267
  --color-border-accent: var(--color-accent-solid);
259
268
  --color-border-success: var(--color-success-500);
@@ -414,6 +423,7 @@
414
423
  4. LIQUID GLASS — transparent / frosted layers
415
424
  Use with backdrop-filter: blur().
416
425
  ============================================================ */
426
+ /* ── RAW primitives (not theme-aware — do NOT use directly on themed chrome) ── */
417
427
  --glass-white: color-mix(in srgb, var(--color-pure-white) 58%, transparent);
418
428
  --glass-white-hi: color-mix(in srgb, var(--color-pure-white) 78%, transparent);
419
429
  --glass-white-lo: color-mix(in srgb, var(--color-pure-white) 38%, transparent);
@@ -421,9 +431,22 @@
421
431
  --glass-ink: color-mix(in srgb, var(--color-background-inverse) 55%, transparent); /* dark navy */
422
432
  --glass-ink-hi: color-mix(in srgb, var(--color-background-inverse) 78%, transparent);
423
433
  --glass-ink-lo: color-mix(in srgb, var(--color-background-inverse) 35%, transparent);
424
- /* Theme-aware frosted chrome (drawer/modal header+footer): white glass in light,
425
- ink glass in dark. Use this — NOT raw --glass-white — on themed chrome. */
426
- --glass-chrome: var(--glass-white);
434
+
435
+ /* ── SEMANTIC glass (theme-aware) — ALWAYS prefer these ──────────────────
436
+ Four roles, each with a paired -fg so text contrast comes with the surface:
437
+ surface frosted card / panel over content
438
+ chrome drawer + modal header/footer, action bars
439
+ overlay floating menus, popovers, command palettes
440
+ hero marketing/hero glass — stays DARK in both themes by design
441
+ Remapped in [data-theme="dark"] below. */
442
+ --glass-surface: var(--glass-white);
443
+ --glass-surface-fg: var(--color-foreground-default);
444
+ --glass-chrome: var(--glass-white);
445
+ --glass-chrome-fg: var(--color-foreground-default);
446
+ --glass-overlay: var(--glass-white-hi);
447
+ --glass-overlay-fg: var(--color-foreground-default);
448
+ --glass-hero: var(--glass-ink);
449
+ --glass-hero-fg: var(--color-foreground-inverse-default);
427
450
  --glass-blur: saturate(140%) blur(18px); /* @kind other */
428
451
  --glass-blur-sm: saturate(130%) blur(10px); /* @kind other */
429
452
  --glass-blur-lg: saturate(160%) blur(28px); /* @kind other */
@@ -487,6 +510,17 @@
487
510
  --shadow-focus-warning: 0 0 0 4px color-mix(in srgb, var(--color-warning-500) 22%, transparent);
488
511
  --shadow-focus-danger: 0 0 0 4px color-mix(in srgb, var(--color-danger-500) 22%, transparent);
489
512
  --shadow-focus-info: 0 0 0 4px color-mix(in srgb, var(--color-info-500) 22%, transparent);
513
+ /* Focus ring for dark/inverse surfaces — the 22% accent ring vanishes over ink */
514
+ --shadow-focus-inverse: 0 0 0 4px color-mix(in srgb, var(--color-pure-white) 34%, transparent);
515
+
516
+ /* Glow — colored ambient halos that survive a dark surface (all --shadow-* above
517
+ are neutral bluish and disappear there). For CTAs, active cards, hero accents. */
518
+ --shadow-glow-accent: 0 0 0 1px color-mix(in srgb, var(--color-accent-solid) 26%, transparent),
519
+ 0 8px 32px color-mix(in srgb, var(--color-accent-solid) 34%, transparent);
520
+ --shadow-glow-brand: 0 0 0 1px color-mix(in srgb, var(--color-brand-solid) 26%, transparent),
521
+ 0 8px 32px color-mix(in srgb, var(--color-brand-solid) 34%, transparent);
522
+ --shadow-glow-success: 0 0 0 1px color-mix(in srgb, var(--color-success-500) 26%, transparent),
523
+ 0 8px 32px color-mix(in srgb, var(--color-success-500) 34%, transparent);
490
524
 
491
525
  /* Semantic elevation — for status cards, toasts, banners, action buttons */
492
526
  --shadow-success-sm: 0 1px 2px color-mix(in srgb, var(--shadow-tint-success-color) 8%, transparent),
@@ -532,6 +566,8 @@
532
566
  --space-6: 20px;
533
567
  --space-7: 24px;
534
568
  --space-8: 32px;
569
+ /* Indent step for a table subrow, multiplied by its depth (see table.css §12) */
570
+ --subrow-indent: 24px; /* @kind spacing */
535
571
  --space-9: 40px;
536
572
  --space-10: 48px;
537
573
  --space-12: 64px;
@@ -569,6 +605,9 @@
569
605
  --ease-standard: cubic-bezier(.2, .8, .2, 1); /* @kind other */
570
606
  --ease-emphasis: cubic-bezier(.2, .9, .15, 1); /* @kind other */ /* slightly more overshoot — for entries */
571
607
  --ease-linear: linear; /* @kind other */ /* loading bars, scrubbers */
608
+ /* Entrance choreography — the eases above cover microinteraction; this one is for
609
+ elements arriving on screen (hero, wizard steps, staggered lists). */
610
+ --ease-entrance: cubic-bezier(.16, 1, .3, 1); /* @kind other */
572
611
 
573
612
  /* Durations — paired with --ease-standard unless noted */
574
613
  --duration-instant: 80ms; /* @kind other */ /* micro feedback (tap echo) */
@@ -577,6 +616,20 @@
577
616
  --duration-slow: 320ms; /* @kind other */ /* expansions, reveals */
578
617
  --duration-slower: 480ms; /* @kind other */ /* page-level transitions */
579
618
  --duration-deliberate: 640ms; /* @kind other */ /* overlays — modal/drawer entry */
619
+ --duration-entrance: 720ms; /* @kind other */ /* on-screen arrival, paired with --ease-entrance */
620
+
621
+ /* Stagger — delay step between siblings in a choreographed entrance */
622
+ --stagger-tight: 40ms; /* @kind other */
623
+ --stagger-base: 80ms; /* @kind other */
624
+ --stagger-loose: 140ms; /* @kind other */
625
+
626
+ /* Breakpoints — single source for JS, docs and the token export. NOTE: custom
627
+ properties do NOT work inside @media queries; author media with the literal
628
+ px and keep these as the canonical reference. */
629
+ --bp-sm: 640px; /* @kind other */
630
+ --bp-md: 900px; /* @kind other */
631
+ --bp-lg: 1200px; /* @kind other */
632
+ --bp-xl: 1440px; /* @kind other */
580
633
 
581
634
  /* Pre-composed transition shorthand (most common cases) */
582
635
  --transition-fast: 140ms var(--ease-standard); /* @kind other */ /* default for buttons/inputs */
@@ -593,16 +646,30 @@
593
646
  ============================================================ */
594
647
  --font-sans: 'Lato', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
595
648
  --font-mono: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
649
+ /* Optical size for the mono family. JetBrains Mono is drawn for code editors:
650
+ its x-height is 0.550 against Lato's 0.5065, so at the same px it reads ~8.6%
651
+ larger in mixed UI text. Every rule that sets --font-mono also sets
652
+ font-size-adjust to this value, which scales the glyph to Lato's x-height.
653
+ Swapping the mono family = change the two lines above/below (and a product
654
+ theme may override the pair). Set to `none` to disable the whole mechanism. */
655
+ --font-mono-adjust: 0.5065; /* @kind font */
596
656
  --font-display: 'Lato', -apple-system, BlinkMacSystemFont, sans-serif;
597
657
 
598
658
  /* Weights (per manual) */
599
659
  --w-light: 300; /* @kind font */
600
660
  --w-regular: 400; /* @kind font */
601
- --w-medium: 500; /* @kind font */ /* Lato doesn't ship 500 natively; maps to 400 */
602
- --w-semibold: 600; /* @kind font */
603
661
  --w-bold: 700; /* @kind font */
604
- --w-extrabold: 800; /* @kind font */
605
662
  --w-black: 900; /* @kind font */
663
+ /* RETIRED — Lato has no Medium, SemiBold or ExtraBold. Requesting them from
664
+ Google Fonts returns nothing and the browser rounds silently (500→400,
665
+ 600→700, 800→900), so the scale advertised 7 steps while rendering 4.
666
+ --w-semibold was the most used token in the system (99 rules) and rendered
667
+ BOLD everywhere, which is why the UI read heavier than the scale implied.
668
+ Kept as deprecated aliases so a consuming project does not break; new code
669
+ must use one of the four real weights above. */
670
+ --w-medium: var(--w-regular); /* DEPRECATED → 400 */
671
+ --w-semibold: var(--w-regular); /* DEPRECATED → 400 */
672
+ --w-extrabold: var(--w-black); /* DEPRECATED → 900 */
606
673
 
607
674
  /* Type scale (px) */
608
675
  --fs-xxs: 10px;
@@ -619,6 +686,7 @@
619
686
  --fs-6xl: 40px;
620
687
  --fs-7xl: 56px;
621
688
  --fs-8xl: 72px;
689
+ --fs-9xl: 96px; /* marketing/editorial hero — the scale used to stop at 72 */
622
690
 
623
691
  /* Line heights */
624
692
  --lh-tight: 1.15; /* @kind font */
@@ -628,6 +696,7 @@
628
696
 
629
697
  /* Letter spacing — per Lato manual rules */
630
698
  --ls-display: -0.03em; /* @kind font */ /* 32–80px headlines */
699
+ --ls-display-xl: -0.04em; /* @kind font */ /* >80px — -3% reads loose at hero sizes */
631
700
  --ls-tight: -0.01em; /* @kind font */ /* large numerics / stat values — between display and body */
632
701
  --ls-title: -0.015em; /* @kind font */ /* 16–32px titles (manual says -5%, but -5% looks tight on screens; using -1.5%) */
633
702
  --ls-body: 0em; /* @kind font */ /* <16px body */
@@ -675,9 +744,9 @@
675
744
  /* --color-foreground-on-solid stays neutral-0 (white text on Royal/accent fills) */
676
745
 
677
746
  /* Borders — white-alpha hairlines */
678
- --color-border-subtle: color-mix(in srgb, var(--color-pure-white) 8%, transparent);
679
- --color-border-default: color-mix(in srgb, var(--color-pure-white) 12%, transparent);
680
- --color-border-strong: color-mix(in srgb, var(--color-pure-white) 20%, transparent);
747
+ --color-border-subtle: color-mix(in srgb, var(--color-pure-white) 4%, transparent);
748
+ --color-border-default: color-mix(in srgb, var(--color-pure-white) 6%, transparent);
749
+ --color-border-strong: color-mix(in srgb, var(--color-pure-white) 10%, transparent);
681
750
  /* focus / accent borders stay Sky-500 — reads well on dark */
682
751
 
683
752
  /* Feedback pairs flip: light foreground + dark-tinted background */
@@ -698,6 +767,12 @@
698
767
  --grad-surface-top: linear-gradient(180deg, color-mix(in srgb, var(--color-pure-white) 6%, transparent) 0%, transparent 100%); /* @kind color */
699
768
  /* Frosted chrome flips to ink glass on dark (drawer/modal header+footer) */
700
769
  --glass-chrome: var(--glass-ink);
770
+ --glass-chrome-fg: var(--color-foreground-inverse-default);
771
+ --glass-surface: var(--glass-ink-lo);
772
+ --glass-surface-fg: var(--color-foreground-default);
773
+ --glass-overlay: var(--glass-ink-hi);
774
+ --glass-overlay-fg: var(--color-foreground-default);
775
+ /* --glass-hero stays ink in both themes (by design) */
701
776
 
702
777
  /* Data-viz categorical — lift the dark hues so every series reads on dark ink.
703
778
  Ramps (seq/div) + structure follow the semantic tokens already remapped above. */
@@ -728,29 +803,58 @@ html, body {
728
803
  -moz-osx-font-smoothing: grayscale;
729
804
  }
730
805
 
731
- .display-xl { font-family: var(--font-display); font-weight: var(--w-black); font-size: var(--fs-8xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
732
- .display-lg { font-family: var(--font-display); font-weight: var(--w-extrabold);font-size: var(--fs-7xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
806
+ .display-hero-xl { font-family: var(--font-display); font-weight: var(--w-black); font-size: var(--fs-9xl); line-height: 0.98; letter-spacing: var(--ls-display-xl); }
807
+ .display-hero { font-family: var(--font-display); font-weight: var(--w-black); font-size: var(--fs-8xl); line-height: 1.02; letter-spacing: var(--ls-display-xl); }
808
+ .display-xl { font-family: var(--font-display); font-weight: var(--w-black); font-size: var(--fs-8xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }.display-lg { font-family: var(--font-display); font-weight: var(--w-black);font-size: var(--fs-7xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
733
809
  .display { font-family: var(--font-display); font-weight: var(--w-bold); font-size: var(--fs-6xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
734
810
 
735
811
  h1, .h1 { font-weight: var(--w-bold); font-size: var(--fs-5xl); line-height: var(--lh-tight); letter-spacing: var(--ls-display); }
736
812
  h2, .h2 { font-weight: var(--w-bold); font-size: var(--fs-4xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
737
813
  h3, .h3 { font-weight: var(--w-bold); font-size: var(--fs-3xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
738
- h4, .h4 { font-weight: var(--w-semibold); font-size: var(--fs-2xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
739
- h5, .h5 { font-weight: var(--w-semibold); font-size: var(--fs-xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
740
- h6, .h6 { font-weight: var(--w-semibold); font-size: var(--fs-lg); line-height: var(--lh-snug); letter-spacing: var(--ls-body); }
814
+ h4, .h4 { font-weight: var(--w-regular); font-size: var(--fs-2xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
815
+ h5, .h5 { font-weight: var(--w-regular); font-size: var(--fs-xl); line-height: var(--lh-snug); letter-spacing: var(--ls-title); }
816
+ h6, .h6 { font-weight: var(--w-regular); font-size: var(--fs-lg); line-height: var(--lh-snug); letter-spacing: var(--ls-body); }
741
817
 
742
818
  .body-lg { font-size: var(--fs-lg); line-height: var(--lh-relaxed); }
743
819
  .body { font-size: var(--fs-base);line-height: var(--lh-normal); }
744
820
  .body-sm { font-size: var(--fs-md); line-height: var(--lh-normal); }
745
821
  .caption { font-size: var(--fs-sm); line-height: var(--lh-normal); color: var(--color-foreground-muted); }
746
- .overline { font-size: var(--fs-xs); line-height: var(--lh-normal); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--color-foreground-muted); font-weight: var(--w-semibold); }
747
- .label { font-size: var(--fs-sm); font-weight: var(--w-semibold); letter-spacing: var(--ls-label); }
822
+ .overline { font-size: var(--fs-xs); line-height: var(--lh-normal); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--color-foreground-muted); font-weight: var(--w-regular); }
823
+ .overline-mono { font-family: var(--font-mono);
824
+ font-size-adjust: var(--font-mono-adjust); font-size: var(--fs-xxs); line-height: var(--lh-normal); letter-spacing: var(--ls-caps); text-transform: uppercase; color: var(--color-foreground-muted); font-weight: var(--w-regular); }
825
+ .label { font-size: var(--fs-sm); font-weight: var(--w-regular); letter-spacing: var(--ls-label); }
748
826
 
749
- .mono, code, pre, kbd { font-family: var(--font-mono); font-size: .92em; }
827
+ .mono, code, pre, kbd { font-family: var(--font-mono);
828
+ font-size-adjust: var(--font-mono-adjust); font-size: .92em; }
750
829
 
751
830
  /* Numbers that must line up in tables */
752
831
  .tabular { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' on, 'lnum' on; }
753
832
 
833
+ /* ============================================================
834
+ ENTRANCE CHOREOGRAPHY — .enter + .enter-1..5
835
+ Fade/rise for elements arriving on screen. Use .enter alone, or the
836
+ numbered variants on siblings for a staggered reveal.
837
+ ============================================================ */
838
+ @keyframes ds-enter {
839
+ from { opacity: 0; transform: translate3d(0, 14px, 0); }
840
+ to { opacity: 1; transform: none; }
841
+ }
842
+ .enter {
843
+ animation: ds-enter var(--duration-entrance) var(--ease-entrance) both;
844
+ }
845
+ .enter-1 { animation: ds-enter var(--duration-entrance) var(--ease-entrance) both; animation-delay: calc(var(--stagger-base) * 0); }
846
+ .enter-2 { animation: ds-enter var(--duration-entrance) var(--ease-entrance) both; animation-delay: calc(var(--stagger-base) * 1); }
847
+ .enter-3 { animation: ds-enter var(--duration-entrance) var(--ease-entrance) both; animation-delay: calc(var(--stagger-base) * 2); }
848
+ .enter-4 { animation: ds-enter var(--duration-entrance) var(--ease-entrance) both; animation-delay: calc(var(--stagger-base) * 3); }
849
+ .enter-5 { animation: ds-enter var(--duration-entrance) var(--ease-entrance) both; animation-delay: calc(var(--stagger-base) * 4); }
850
+ @media (prefers-reduced-motion: reduce) {
851
+ .enter, .enter-1, .enter-2, .enter-3, .enter-4, .enter-5 {
852
+ animation: none;
853
+ opacity: 1;
854
+ transform: none;
855
+ }
856
+ }
857
+
754
858
  /* Utility text colors */
755
859
  .fg-default { color: var(--color-foreground-default); }
756
860
  .fg-secondary { color: var(--color-foreground-secondary); }
@@ -780,7 +884,7 @@ h6, .h6 { font-weight: var(--w-semibold); font-size: var(--fs-lg); line-height:
780
884
  gap: var(--space-2);
781
885
  font-family: var(--font-sans);
782
886
  font-size: var(--fs-sm);
783
- font-weight: var(--w-semibold);
887
+ font-weight: var(--w-regular);
784
888
  letter-spacing: 0;
785
889
  text-transform: none;
786
890
  color: var(--color-foreground-default);
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ export interface ActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ /** Left cluster — back / secondary actions. */
5
+ lead?: React.ReactNode;
6
+ /** Middle context — progress text, count, hint. */
7
+ context?: React.ReactNode;
8
+ /** Right cluster — primary action(s). Right-aligned via margin-left:auto. */
9
+ actions?: React.ReactNode;
10
+ /** Pin to the viewport bottom. */
11
+ fixed?: boolean;
12
+ size?: 'md' | 'sm';
13
+ /** Dark-surface treatment. */
14
+ onInverse?: boolean;
15
+ }
16
+
17
+ export declare const ActionBar: React.FC<ActionBarProps>;
18
+
19
+ export default ActionBar;
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * ATTRUS ActionBar — typed wrapper over the canonical `.action-bar`
5
+ * (preview/components/action-bar.css): persistent action strip for wizards,
6
+ * drawers and editors.
7
+ *
8
+ * The right cluster uses `margin-left:auto` (never a flex:1 spacer — a spacer
9
+ * competing with a Stepper in the same row collapses it).
10
+ */
11
+ export interface ActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
12
+ /** Left cluster — back / secondary actions. */
13
+ lead?: React.ReactNode;
14
+ /** Middle context — progress text, count, hint. */
15
+ context?: React.ReactNode;
16
+ /** Right cluster — primary action(s). */
17
+ actions?: React.ReactNode;
18
+ /** Pin to the viewport bottom. */
19
+ fixed?: boolean;
20
+ size?: 'md' | 'sm';
21
+ /** Dark-surface treatment. */
22
+ onInverse?: boolean;
23
+ }
24
+
25
+ export const ActionBar: React.FC<ActionBarProps> = ({
26
+ lead,
27
+ context,
28
+ actions,
29
+ fixed = false,
30
+ size = 'md',
31
+ onInverse = false,
32
+ className,
33
+ children,
34
+ ...rest
35
+ }) => {
36
+ const cls = [
37
+ 'action-bar',
38
+ size === 'sm' ? 'action-bar-sm' : '',
39
+ fixed ? 'is-fixed' : '',
40
+ onInverse ? 'on-inverse' : '',
41
+ className || '',
42
+ ].filter(Boolean).join(' ');
43
+
44
+ return (
45
+ <div className={cls} {...rest}>
46
+ {lead != null ? <div className="action-bar-lead">{lead}</div> : null}
47
+ {context != null ? <div className="action-bar-context">{context}</div> : null}
48
+ {children}
49
+ {actions != null ? <div className="action-bar-actions">{actions}</div> : null}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default ActionBar;
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+
3
+ export interface AmbientBackdropProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children?: React.ReactNode;
5
+ /** How many blobs to render (1–3). */
6
+ blobs?: 1 | 2 | 3;
7
+ /** Blob opacity preset. */
8
+ intensity?: 'subtle' | 'default' | 'strong';
9
+ /** Dark hero: ink background + ink scrim. */
10
+ onInverse?: boolean;
11
+ /** Drop the readability veil. Only for decorative strips with no text. */
12
+ noScrim?: boolean;
13
+ }
14
+
15
+ export declare const AmbientBackdrop: React.FC<AmbientBackdropProps>;
16
+
17
+ export default AmbientBackdrop;