@crisp-ui-kit/crisp 0.55.6 → 0.57.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.
Files changed (112) hide show
  1. package/dist/callout.css +6 -0
  2. package/dist/callout.d.ts +2 -2
  3. package/dist/callout.layered.css +6 -0
  4. package/dist/cjs/components/callout/callout.js +2 -2
  5. package/dist/cjs/components/callout/callout.recipe.js +15 -2
  6. package/dist/cjs/components/input/input.recipe.js +6 -0
  7. package/dist/cjs/components/menu/menu.js +2 -2
  8. package/dist/cjs/components/menu/menu.recipe.js +8 -0
  9. package/dist/cjs/components/multiselect/multiselect.js +23 -10
  10. package/dist/cjs/components/multiselect/multiselect.recipe.js +95 -65
  11. package/dist/cjs/components/select/listbox-keyboard.js +7 -1
  12. package/dist/cjs/components/select/select.js +24 -11
  13. package/dist/cjs/components/select/select.recipe.js +150 -78
  14. package/dist/cjs/components/statusdot/index.js +7 -0
  15. package/dist/cjs/components/statusdot/statusdot.js +15 -0
  16. package/dist/cjs/components/statusdot/statusdot.recipe.js +34 -0
  17. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +3 -3
  18. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  19. package/dist/cjs/components/tabs/tabs.js +63 -4
  20. package/dist/cjs/components/tabs/tabs.recipe.js +13 -0
  21. package/dist/cjs/components/text/text.recipe.js +10 -0
  22. package/dist/cjs/components/textlink/textlink.js +2 -2
  23. package/dist/cjs/components/textlink/textlink.recipe.js +18 -0
  24. package/dist/cjs/index.js +1 -0
  25. package/dist/cjs/product/recordpage/recordpage.js +17 -4
  26. package/dist/cjs/tokens/elevation.js +7 -0
  27. package/dist/cjs/tokens/palette.js +41 -0
  28. package/dist/cjs/tokens/scale.js +16 -1
  29. package/dist/cjs/tokens/semantic.js +18 -0
  30. package/dist/esm/components/callout/callout.js +2 -2
  31. package/dist/esm/components/callout/callout.recipe.js +15 -2
  32. package/dist/esm/components/input/input.recipe.js +6 -0
  33. package/dist/esm/components/menu/menu.js +2 -2
  34. package/dist/esm/components/menu/menu.recipe.js +8 -0
  35. package/dist/esm/components/multiselect/multiselect.js +23 -10
  36. package/dist/esm/components/multiselect/multiselect.recipe.js +95 -65
  37. package/dist/esm/components/select/listbox-keyboard.js +7 -1
  38. package/dist/esm/components/select/select.js +24 -11
  39. package/dist/esm/components/select/select.recipe.js +150 -78
  40. package/dist/esm/components/statusdot/index.js +2 -0
  41. package/dist/esm/components/statusdot/statusdot.js +12 -0
  42. package/dist/esm/components/statusdot/statusdot.recipe.js +31 -0
  43. package/dist/esm/components/tabletoolbar/tabletoolbar.js +3 -3
  44. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  45. package/dist/esm/components/tabs/tabs.js +63 -4
  46. package/dist/esm/components/tabs/tabs.recipe.js +13 -0
  47. package/dist/esm/components/text/text.recipe.js +10 -0
  48. package/dist/esm/components/textlink/textlink.js +2 -2
  49. package/dist/esm/components/textlink/textlink.recipe.js +18 -0
  50. package/dist/esm/index.js +1 -0
  51. package/dist/esm/product/recordpage/recordpage.js +18 -5
  52. package/dist/esm/tokens/elevation.js +7 -0
  53. package/dist/esm/tokens/palette.js +41 -0
  54. package/dist/esm/tokens/scale.js +16 -1
  55. package/dist/esm/tokens/semantic.js +18 -0
  56. package/dist/input.css +4 -0
  57. package/dist/input.d.ts +2 -2
  58. package/dist/input.layered.css +4 -0
  59. package/dist/menu.css +8 -0
  60. package/dist/menu.layered.css +8 -0
  61. package/dist/multiselect.css +53 -29
  62. package/dist/multiselect.d.ts +3 -1
  63. package/dist/multiselect.layered.css +53 -29
  64. package/dist/native/tokens.d.ts +1 -1
  65. package/dist/native/tokens.js +50 -7
  66. package/dist/select.css +80 -42
  67. package/dist/select.d.ts +3 -2
  68. package/dist/select.layered.css +80 -42
  69. package/dist/statusdot.css +3 -0
  70. package/dist/statusdot.d.ts +7 -0
  71. package/dist/statusdot.layered.css +5 -0
  72. package/dist/styles.css +238 -73
  73. package/dist/styles.layered.css +238 -73
  74. package/dist/tabletoolbar.css +8 -1
  75. package/dist/tabletoolbar.layered.css +8 -1
  76. package/dist/tabs.css +6 -0
  77. package/dist/tabs.layered.css +6 -0
  78. package/dist/text.css +6 -0
  79. package/dist/text.d.ts +2 -2
  80. package/dist/text.layered.css +6 -0
  81. package/dist/textlink.css +9 -0
  82. package/dist/textlink.d.ts +2 -1
  83. package/dist/textlink.layered.css +9 -0
  84. package/dist/tokens.css +54 -1
  85. package/dist/tokens.json +50 -7
  86. package/dist/types/components/callout/callout.d.ts +9 -1
  87. package/dist/types/components/callout/callout.recipe.d.ts +12 -2
  88. package/dist/types/components/input/input.d.ts +2 -0
  89. package/dist/types/components/input/input.recipe.d.ts +3 -0
  90. package/dist/types/components/menu/menu.d.ts +7 -1
  91. package/dist/types/components/multiselect/multiselect.d.ts +24 -6
  92. package/dist/types/components/multiselect/multiselect.recipe.d.ts +24 -0
  93. package/dist/types/components/select/listbox-keyboard.d.ts +10 -1
  94. package/dist/types/components/select/select.d.ts +28 -6
  95. package/dist/types/components/select/select.recipe.d.ts +22 -10
  96. package/dist/types/components/statusdot/index.d.ts +2 -0
  97. package/dist/types/components/statusdot/statusdot.d.ts +21 -0
  98. package/dist/types/components/statusdot/statusdot.recipe.d.ts +15 -0
  99. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +14 -0
  100. package/dist/types/components/tabs/tabs.d.ts +9 -1
  101. package/dist/types/components/text/text.d.ts +4 -0
  102. package/dist/types/components/text/text.recipe.d.ts +12 -0
  103. package/dist/types/components/textlink/textlink.d.ts +8 -1
  104. package/dist/types/components/textlink/textlink.recipe.d.ts +10 -0
  105. package/dist/types/index.d.ts +1 -0
  106. package/dist/types/product/recordpage/recordpage.d.ts +25 -1
  107. package/dist/types/tokens/contract.d.ts +13 -3
  108. package/dist/types/tokens/elevation.d.ts +7 -0
  109. package/dist/types/tokens/palette.d.ts +36 -0
  110. package/dist/types/tokens/scale.d.ts +6 -1
  111. package/dist/types/tokens/semantic.d.ts +18 -0
  112. package/package.json +6 -1
@@ -1,3 +1,31 @@
1
+ @font-face {
2
+ font-family: "VK Sans Display";
3
+ src: local("VK Sans Display Regular");
4
+ font-weight: 400;
5
+ font-style: normal;
6
+ font-display: swap;
7
+ }
8
+ @font-face {
9
+ font-family: "VK Sans Display";
10
+ src: local("VK Sans Display Medium");
11
+ font-weight: 500;
12
+ font-style: normal;
13
+ font-display: swap;
14
+ }
15
+ @font-face {
16
+ font-family: "VK Sans Display";
17
+ src: local("VK Sans Display DemiBold");
18
+ font-weight: 600;
19
+ font-style: normal;
20
+ font-display: swap;
21
+ }
22
+ @font-face {
23
+ font-family: "VK Sans Display";
24
+ src: local("VK Sans Display Bold");
25
+ font-weight: 700;
26
+ font-style: normal;
27
+ font-display: swap;
28
+ }
1
29
  :root {
2
30
  --crisp-bg-brand-solid: #266df0;
3
31
  --crisp-bg-brand-solid-pressed: #1f5fd6;
@@ -54,6 +82,16 @@
54
82
  --crisp-bg-purple-soft: #f5f0ff;
55
83
  --crisp-fg-purple-soft: #6238b5;
56
84
  --crisp-ring-purple-soft: #e8ddfe;
85
+ --crisp-ring-brand-soft: #d6e5ff;
86
+ --crisp-fg-select-neutral: #1a1c20;
87
+ --crisp-bg-select-listbox: #ffffff;
88
+ --crisp-stroke-select-trigger: #dcdee3;
89
+ --crisp-bg-select-pressed: #00000007;
90
+ --crisp-bg-select-disabled: #f3f4f5;
91
+ --crisp-stroke-select-invalid: #fa342c;
92
+ --crisp-stroke-select-divider: #00000010;
93
+ --crisp-fg-select-placeholder: #b0b3ba;
94
+ --crisp-shadow-select: #0000001f;
57
95
  }
58
96
 
59
97
  [data-theme="dark"] {
@@ -112,6 +150,16 @@
112
150
  --crisp-bg-purple-soft: #2f1e5a;
113
151
  --crisp-fg-purple-soft: #d8c4ff;
114
152
  --crisp-ring-purple-soft: #45297d;
153
+ --crisp-ring-brand-soft: #212224;
154
+ --crisp-fg-select-neutral: #f3f4f5;
155
+ --crisp-bg-select-listbox: #1d2025;
156
+ --crisp-stroke-select-trigger: #393d46;
157
+ --crisp-bg-select-pressed: #ffffff0d;
158
+ --crisp-bg-select-disabled: #2b2e35;
159
+ --crisp-stroke-select-invalid: #ff6e60;
160
+ --crisp-stroke-select-divider: #ffffff17;
161
+ --crisp-fg-select-placeholder: #868b94;
162
+ --crisp-shadow-select: #000000cc;
115
163
  }
116
164
 
117
165
  :root {
@@ -126,12 +174,14 @@
126
174
  --crisp-space-0-5: 2px;
127
175
  --crisp-space-1-5: 6px;
128
176
  --crisp-space-2-5: 10px;
177
+ --crisp-space-3-5: 14px;
129
178
  --crisp-radius-chip: 7px;
130
179
  --crisp-radius-sm: 6px;
131
180
  --crisp-radius-md: 8px;
132
181
  --crisp-radius-lg: 9px;
133
182
  --crisp-radius-xl: 12px;
134
183
  --crisp-radius-2xl: 16px;
184
+ --crisp-radius-3xl: 20px;
135
185
  --crisp-radius-full: 999px;
136
186
  --crisp-size-icon: 16px;
137
187
  --crisp-size-chip: 22px;
@@ -142,6 +192,9 @@
142
192
  --crisp-size-header: 48px;
143
193
  --crisp-size-calendarCell: 34px;
144
194
  --crisp-size-calendarPill: 32px;
195
+ --crisp-size-selectTriggerMd: 40px;
196
+ --crisp-size-selectTriggerLg: 52px;
197
+ --crisp-size-selectIconLg: 20px;
145
198
  --crisp-text-xs: 11px;
146
199
  --crisp-text-sm: 12px;
147
200
  --crisp-text-md: 13px;
@@ -151,7 +204,7 @@
151
204
  --crisp-weight-medium: 500;
152
205
  --crisp-weight-semibold: 600;
153
206
  --crisp-weight-bold: 700;
154
- --crisp-font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
207
+ --crisp-font-sans: "VK Sans Display", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
155
208
  }
156
209
 
157
210
  @layer crisp-components {
@@ -409,6 +462,12 @@
409
462
  &[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
410
463
  &[data-tone="danger"] .crisp-callout-icon { color: var(--crisp-bg-danger); }
411
464
  }
465
+ .crisp-callout--bordered_true {
466
+ border: 1px solid var(--crisp-ring-brand-soft); border-radius: var(--crisp-radius-xl); padding: var(--crisp-space-2) var(--crisp-space-3); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent);
467
+ }
468
+ .crisp-callout--bordered_false {
469
+
470
+ }
412
471
 
413
472
  .crisp-card {
414
473
  display: flex; flex-direction: column; box-sizing: border-box; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary);
@@ -1328,6 +1387,10 @@
1328
1387
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
1329
1388
  &:has(.crisp-input-value:disabled) { opacity: 1; }
1330
1389
  }
1390
+ .crisp-input--size_sm {
1391
+ height: var(--crisp-size-row); border-radius: var(--crisp-radius-md);
1392
+ & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
1393
+ }
1331
1394
  .crisp-input--size_medium {
1332
1395
  height: 32px; border-radius: var(--crisp-radius-md);
1333
1396
  & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
@@ -1408,6 +1471,14 @@
1408
1471
  .crisp-menu-item--danger svg { color: var(--crisp-bg-danger); }
1409
1472
  .crisp-menu-item--danger:hover, .crisp-menu-item--danger:focus-visible, .crisp-menu-item--danger[data-highlighted] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
1410
1473
  .crisp-menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1474
+ /* crisp#875: same measured neutral chip TableToolbar's own ToolbarTool
1475
+ count prop uses (re-confirmed on two live Attio surfaces this round:
1476
+ the Sort tool's own "+N" overflow, a Kanban board's stage-column
1477
+ count) -- a small badge, not bare text. */
1478
+ .crisp-menu-item-count { flex-shrink: 0; margin-inline-start: var(--crisp-space-1-5); display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
1479
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
1480
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft);
1481
+ font-variant-numeric: tabular-nums; }
1411
1482
  .crisp-menu-item-shortcut { flex-shrink: 0; margin-inline-start: var(--crisp-space-3); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); letter-spacing: 0.02em; }
1412
1483
  .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
1413
1484
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -1428,38 +1499,42 @@
1428
1499
  }
1429
1500
 
1430
1501
 
1431
- /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1432
- on document.body while open (Zag re-enables it only on layers IT
1433
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1434
- inherited the lock, so every option was unclickable, hit-testing
1435
- right through to whatever real element sat behind it. */
1502
+ /* pointer-events: auto (#375) -- see Select's matching comment. */
1436
1503
  .crisp-multiselect-listbox {
1437
1504
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1438
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1439
- max-height: 300px; overflow-y: auto; padding: var(--crisp-space-1);
1440
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1441
- box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3);
1442
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
1505
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
1506
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
1507
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
1508
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
1509
+ transform-origin: top left; opacity: 1; transform: none;
1510
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
1443
1511
  }
1444
1512
  .crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
1445
- .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1446
- .crisp-multiselect-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 36px; flex-shrink: 0; margin: calc(-1 * var(--crisp-space-1)) calc(-1 * var(--crisp-space-1)) var(--crisp-space-1); padding: var(--crisp-space-2) var(--crisp-space-3); color: var(--crisp-fg-muted); }
1513
+ .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
1514
+ /* LOCKED geometry (#481, guarded by build/spec-check.mjs): height 36px,
1515
+ gap space["1.5"] (6px), no border-bottom -- an Attio measurement, not
1516
+ part of this seed-parity pass. Only colours below follow Select. */
1517
+ .crisp-multiselect-search { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 36px; flex-shrink: 0; margin: calc(-1 * var(--crisp-space-2)) calc(-1 * var(--crisp-space-2)) var(--crisp-space-2); padding: var(--crisp-space-2) var(--crisp-space-3); color: var(--crisp-fg-neutral-muted); }
1447
1518
  .crisp-multiselect-search svg { flex-shrink: 0; }
1448
- .crisp-multiselect-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
1449
- .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-muted); }
1450
- /* #343: same measured values as Menu's own group heading
1451
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1452
- same "small muted heading above a run of rows" treatment. */
1453
- .crisp-multiselect-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1519
+ .crisp-multiselect-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-select-neutral); }
1520
+ .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
1521
+ /* Same groupLabel treatment as Select's (13px/regular/neutral-subtle). */
1522
+ .crisp-multiselect-label { padding: var(--crisp-space-2) var(--crisp-space-4) 0; font-size: var(--crisp-text-md); line-height: 18px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-neutral-subtle); }
1454
1523
  .crisp-multiselect-option {
1455
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1456
- padding: 0 var(--crisp-space-2); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; cursor: pointer; text-align: start;
1524
+ box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0;
1525
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
1526
+ font-family: inherit; text-align: start; cursor: pointer;
1527
+ transition: background-color .15s cubic-bezier(.35,0,.35,1);
1457
1528
  }
1529
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill, same
1530
+ reasoning as Select's matching rule (seed's own pressed tint is too
1531
+ faint for a keyboard-navigation indicator). */
1458
1532
  .crisp-multiselect-option[data-highlighted] { background: var(--crisp-bg-raised); }
1533
+ .crisp-multiselect-option:active { background: var(--crisp-bg-select-pressed); }
1459
1534
  .crisp-multiselect-option-chip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
1460
1535
  .crisp-multiselect-check { flex-shrink: 0; margin-inline-start: auto; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: var(--crisp-radius-sm); box-shadow: inset 0 0 0 1.5px var(--crisp-border-default); color: transparent; }
1461
1536
  .crisp-multiselect-option[aria-selected="true"] .crisp-multiselect-check { background: var(--crisp-bg-brand-solid); box-shadow: none; color: #fff; }
1462
- .crisp-multiselect-empty { padding: var(--crisp-space-3) var(--crisp-space-2); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
1537
+ .crisp-multiselect-empty { padding: var(--crisp-space-3) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-neutral-subtle); }
1463
1538
 
1464
1539
  .crisp-recordstable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); }
1465
1540
  .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: color-mix(in srgb, var(--crisp-bg-brand-solid) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent); }
@@ -1472,21 +1547,41 @@
1472
1547
 
1473
1548
  .crisp-multiselect {
1474
1549
  position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1475
- & .crisp-multiselect-trigger { box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; min-width: 0; min-height: var(--crisp-size-menuRow); margin: 0; padding: var(--crisp-space-0-5) var(--crisp-space-1-5) var(--crisp-space-0-5) var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); cursor: pointer; text-align: start; transition: background-color .12s ease, box-shadow .12s ease; }
1476
- & .crisp-multiselect-trigger:hover { background: var(--crisp-bg-raised); }
1477
- & .crisp-multiselect-trigger[data-open], & .crisp-multiselect-trigger:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); background: var(--crisp-bg-surface); }
1478
- & .crisp-multiselect-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
1550
+ & .crisp-multiselect-trigger { box-sizing: border-box; display: flex; align-items: center; width: 100%; min-width: 0; margin: 0; border: 1px solid var(--crisp-stroke-select-trigger); background: transparent; cursor: pointer; text-align: start; font-family: inherit; color: var(--crisp-fg-select-neutral); transition: background-color .15s cubic-bezier(.35,0,.35,1), border-color .1s cubic-bezier(.35,0,.35,1), border-width .1s cubic-bezier(.35,0,.35,1); }
1551
+ /* Same pressed/disabled/readonly/focus model as Select's trigger -- see
1552
+ that recipe's comment for why hover and an open/focus ring are gone. */
1553
+ & .crisp-multiselect-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
1554
+ & .crisp-multiselect-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
1555
+ & .crisp-multiselect-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
1556
+ & .crisp-multiselect-trigger:disabled .crisp-multiselect-placeholder, & .crisp-multiselect-trigger:disabled .crisp-multiselect-chevron { color: var(--crisp-fg-disabled); }
1557
+ & .crisp-multiselect-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
1479
1558
  & .crisp-multiselect-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); }
1480
- & .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
1481
- & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1482
- & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); }
1559
+ & .crisp-multiselect-placeholder { color: var(--crisp-fg-select-placeholder); }
1560
+ /* closeRotateDuration/openRotateDuration, same asymmetric timing as Select's own chevron (see that recipe's comment). */
1561
+ & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
1562
+ & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); transition-duration: .15s; }
1563
+ }
1564
+ .crisp-multiselect--size_medium {
1565
+ & .crisp-multiselect-trigger { min-height: var(--crisp-size-selectTriggerMd); border-radius: var(--crisp-radius-md); gap: var(--crisp-space-2); padding: var(--crisp-space-1) var(--crisp-space-3-5); }
1566
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
1567
+ }
1568
+ .crisp-multiselect--size_large {
1569
+ & .crisp-multiselect-trigger { min-height: var(--crisp-size-selectTriggerLg); border-radius: var(--crisp-radius-xl); gap: var(--crisp-space-2-5); padding: var(--crisp-space-1-5) var(--crisp-space-4); }
1570
+ & .crisp-multiselect-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
1483
1571
  }
1484
1572
  .crisp-multiselect--invalid_true {
1485
- & .crisp-multiselect-trigger, & .crisp-multiselect-trigger:hover, & .crisp-multiselect-trigger[data-open], & .crisp-multiselect-trigger:focus-visible { box-shadow: inset 0 0 0 1px var(--crisp-bg-danger); }
1573
+ & .crisp-multiselect-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
1486
1574
  }
1487
1575
  .crisp-multiselect--invalid_false {
1488
1576
 
1489
1577
  }
1578
+ .crisp-multiselect--variant_default {
1579
+
1580
+ }
1581
+ .crisp-multiselect--variant_ghost {
1582
+ & .crisp-multiselect-trigger { min-height: 0; height: 100%; border-color: transparent; }
1583
+ & .crisp-multiselect-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
1584
+ }
1490
1585
 
1491
1586
  @media (prefers-reduced-motion: reduce) {
1492
1587
  .crisp-multiselect, .crisp-multiselect * {
@@ -1669,58 +1764,69 @@
1669
1764
 
1670
1765
 
1671
1766
  /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1672
- on document.body while open (Zag re-enables it only on layers IT
1673
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1674
- inherited the lock, so every option was unclickable, hit-testing
1675
- right through to whatever real element sat behind it. */
1767
+ on document.body while open, and the portaled listbox is outside its
1768
+ knowledge -- it inherited the lock without this. */
1676
1769
  .crisp-select-listbox {
1677
1770
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1678
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1679
- max-height: 280px; overflow-y: auto; padding: var(--crisp-space-1);
1680
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1681
- box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3);
1682
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
1771
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
1772
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
1773
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
1774
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
1775
+ transform-origin: top left; opacity: 1; transform: none;
1776
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
1683
1777
  }
1684
- .crisp-select-listbox[data-up] { transform-origin: bottom; }
1685
- .crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1686
- .crisp-select-search { display: flex; align-items: center; gap: var(--crisp-space-2); height: 34px; flex-shrink: 0; margin: calc(-1 * var(--crisp-space-1)) calc(-1 * var(--crisp-space-1)) var(--crisp-space-1); padding: 0 var(--crisp-space-2-5); border-bottom: 1px solid var(--crisp-border-subtle); color: var(--crisp-fg-muted); }
1778
+ .crisp-select-listbox[data-up] { transform-origin: bottom left; }
1779
+ .crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
1780
+ .crisp-select-search { display: flex; align-items: center; gap: var(--crisp-space-2); height: 34px; flex-shrink: 0; margin: calc(-1 * var(--crisp-space-2)) calc(-1 * var(--crisp-space-2)) var(--crisp-space-2); padding: 0 var(--crisp-space-2-5); border-bottom: 1px solid var(--crisp-border-subtle); color: var(--crisp-fg-neutral-muted); }
1687
1781
  .crisp-select-search svg { flex-shrink: 0; }
1688
- .crisp-select-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
1689
- .crisp-select-search input::placeholder { color: var(--crisp-fg-muted); }
1690
- .crisp-select-empty { padding: var(--crisp-space-3) var(--crisp-space-2); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
1691
- /* #343: same measured values as Menu's own group heading
1692
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1693
- same "small muted heading above a run of rows" treatment. */
1694
- .crisp-select-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1782
+ .crisp-select-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-select-neutral); }
1783
+ .crisp-select-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
1784
+ .crisp-select-empty { padding: var(--crisp-space-3) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-neutral-subtle); }
1785
+ /* select.yaml's groupLabel -- font-size/line-height/weight are the same
1786
+ across sizes, only paddingY differs (medium 8px, large 10px). */
1787
+ .crisp-select-label { padding: var(--crisp-space-2) var(--crisp-space-4) 0; font-size: var(--crisp-text-md); line-height: 18px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-neutral-subtle); }
1695
1788
  .crisp-select-option {
1696
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1697
- padding: 0 var(--crisp-space-2); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-md);
1698
- font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); text-align: start; cursor: pointer; white-space: nowrap;
1789
+ box-sizing: border-box; display: flex; align-items: center; flex-shrink: 0;
1790
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin-inline: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
1791
+ font-family: inherit; color: var(--crisp-fg-select-neutral); text-align: start; cursor: pointer; white-space: nowrap;
1792
+ transition: background-color .15s cubic-bezier(.35,0,.35,1), margin-inline .15s cubic-bezier(.35,0,.35,1);
1699
1793
  }
1794
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill --
1795
+ seed's spec has no "highlighted" state at all (only pressed/selected/
1796
+ disabled), and its actual pressed tint (below) is a ~3% black hint,
1797
+ too faint to serve as a keyboard-navigation indicator. */
1700
1798
  .crisp-select-option[data-highlighted] { background: var(--crisp-bg-raised); }
1701
- .crisp-select-option[data-disabled] { opacity: 0.5; cursor: not-allowed; }
1702
- .crisp-select-option-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
1799
+ .crisp-select-option:active:not([data-disabled]) { background: var(--crisp-bg-select-pressed); margin-inline: var(--crisp-space-2); }
1800
+ .crisp-select-option[data-disabled] { cursor: not-allowed; }
1801
+ .crisp-select-option[data-disabled] .crisp-select-option-label,
1802
+ .crisp-select-option[data-disabled] .crisp-select-option-description,
1803
+ .crisp-select-option[data-disabled] .crisp-select-option-check { color: var(--crisp-fg-disabled); }
1804
+ .crisp-select-option-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); margin-inline-end: var(--crisp-space-2); }
1805
+ .crisp-select-option-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--crisp-space-0-5); }
1703
1806
  .crisp-select-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1704
- .crisp-select-option-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
1807
+ .crisp-select-option-description { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--crisp-fg-neutral-subtle); }
1808
+ .crisp-select-option-check { flex-shrink: 0; color: var(--crisp-fg-select-neutral); margin-inline-start: var(--crisp-space-2); }
1705
1809
 
1706
1810
  .crisp-select {
1707
1811
  position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1708
- & .crisp-select-trigger { box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-menuRow); width: 100%; min-width: 0; margin: 0; padding: 0 var(--crisp-space-1-5) 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); cursor: pointer; transition: background-color .12s ease, box-shadow .12s ease; }
1709
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); }
1710
- & .crisp-select-trigger[data-open], & .crisp-select-trigger:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); background: var(--crisp-bg-surface); }
1711
- & .crisp-select-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
1712
- & .crisp-select-value { flex: 1; min-width: 0; display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start; }
1713
- & .crisp-select-placeholder { color: var(--crisp-fg-muted); }
1714
- & .crisp-select-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1715
- & .crisp-select-trigger[data-open] .crisp-select-chevron { transform: rotate(180deg); }
1716
- }
1717
- .crisp-select--variant_default {
1718
-
1719
- }
1720
- .crisp-select--variant_ghost {
1721
- & .crisp-select-trigger { background: transparent; box-shadow: none; }
1722
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); box-shadow: none; }
1723
- & .crisp-select-trigger[data-open], & .crisp-select-trigger:focus-visible { background: transparent; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
1812
+ & .crisp-select-trigger { box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; min-width: 0; margin: 0; border: 1px solid var(--crisp-stroke-select-trigger); background: transparent; cursor: pointer; font-family: inherit; color: var(--crisp-fg-select-neutral); transition: background-color .15s cubic-bezier(.35,0,.35,1), border-color .1s cubic-bezier(.35,0,.35,1), border-width .1s cubic-bezier(.35,0,.35,1); }
1813
+ /* seed defines no hover state for this trigger -- only pressed, invalid, disabled and readonly. The scale-down is scoped to the CONTENT (seed's scaleScope: content), not the button itself, so the border
1814
+ never moves. */
1815
+ /* Only the value scales, not the chevron: the chevron's own transform is its open/close rotation, and a scale would fight it for one shared CSS property when both are true at once (pressing while open). */
1816
+ & .crisp-select-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
1817
+ & .crisp-select-trigger:active:not(:disabled):not([aria-readonly="true"]) .crisp-select-value { transform: scale(0.98); }
1818
+ & .crisp-select-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
1819
+ & .crisp-select-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
1820
+ & .crisp-select-trigger:disabled .crisp-select-value, & .crisp-select-trigger:disabled .crisp-select-placeholder, & .crisp-select-trigger:disabled .crisp-select-chevron { color: var(--crisp-fg-disabled); }
1821
+ /* readonly: same fill as disabled, but the value/placeholder text stays normal ink -- it reads as "answered and locked", not "answered and
1822
+ greyed out" (select-trigger.yaml's readonly state only overrides
1823
+ root.color; value/placeholder keep their enabled colours). */
1824
+ & .crisp-select-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
1825
+ & .crisp-select-value { flex: 1; min-width: 0; display: inline-flex; align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start; transition: transform .15s cubic-bezier(0,0,.15,1); }
1826
+ & .crisp-select-placeholder { color: var(--crisp-fg-select-placeholder); }
1827
+ /* closeRotateDuration/closeRotateTimingFunction: 100ms, seed's "easing" curve. Overridden to 150ms on open (openRotateDuration) below -- same curve both ways, only the duration differs. */
1828
+ & .crisp-select-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
1829
+ & .crisp-select-trigger[data-open] .crisp-select-chevron { transform: rotate(180deg); transition-duration: .15s; }
1724
1830
  }
1725
1831
  .crisp-select--width_full {
1726
1832
  width: 100%;
@@ -1728,12 +1834,39 @@
1728
1834
  .crisp-select--width_auto {
1729
1835
  width: auto;
1730
1836
  }
1837
+ .crisp-select--size_medium {
1838
+ & .crisp-select-trigger { height: var(--crisp-size-selectTriggerMd); border-radius: var(--crisp-radius-md); gap: var(--crisp-space-2); padding-inline: var(--crisp-space-3-5); }
1839
+ & .crisp-select-value, & .crisp-select-placeholder { font-size: var(--crisp-text-lg); line-height: 19px; }
1840
+ & .crisp-select-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
1841
+ .crisp-select-option { gap: var(--crisp-space-2); }
1842
+ .crisp-select-option-label { font-size: var(--crisp-text-lg); line-height: 19px; }
1843
+ .crisp-select-option-description { font-size: var(--crisp-text-sm); line-height: 16px; }
1844
+ .crisp-select-option-check { width: var(--crisp-space-3); height: var(--crisp-space-3); }
1845
+ .crisp-select-label { padding-top: var(--crisp-space-2); }
1846
+ }
1847
+ .crisp-select--size_large {
1848
+ & .crisp-select-trigger { height: var(--crisp-size-selectTriggerLg); border-radius: var(--crisp-radius-xl); gap: var(--crisp-space-2-5); padding-inline: var(--crisp-space-4); }
1849
+ & .crisp-select-value, & .crisp-select-placeholder { font-size: var(--crisp-text-xl); line-height: 22px; }
1850
+ & .crisp-select-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
1851
+ .crisp-select-option { gap: var(--crisp-space-3); }
1852
+ .crisp-select-option-label { font-size: var(--crisp-text-xl); line-height: 22px; }
1853
+ .crisp-select-option-description { font-size: var(--crisp-text-md); line-height: 18px; }
1854
+ .crisp-select-option-check { width: var(--crisp-space-3-5); height: var(--crisp-space-3-5); }
1855
+ .crisp-select-label { padding-top: var(--crisp-space-2-5); }
1856
+ }
1731
1857
  .crisp-select--invalid_true {
1732
- & .crisp-select-trigger, & .crisp-select-trigger:hover, & .crisp-select-trigger[data-open], & .crisp-select-trigger:focus-visible { box-shadow: inset 0 0 0 1px var(--crisp-bg-danger); }
1858
+ & .crisp-select-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
1733
1859
  }
1734
1860
  .crisp-select--invalid_false {
1735
1861
 
1736
1862
  }
1863
+ .crisp-select--variant_default {
1864
+
1865
+ }
1866
+ .crisp-select--variant_ghost {
1867
+ & .crisp-select-trigger { height: 100%; border-color: transparent; }
1868
+ & .crisp-select-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
1869
+ }
1737
1870
 
1738
1871
  @media (prefers-reduced-motion: reduce) {
1739
1872
  .crisp-select, .crisp-select * {
@@ -1866,6 +1999,10 @@
1866
1999
 
1867
2000
  }
1868
2001
 
2002
+ .crisp-statusdot {
2003
+ display: inline-block; box-sizing: border-box; flex-shrink: 0; width: 12px; height: 12px; border-radius: var(--crisp-radius-full);
2004
+ }
2005
+
1869
2006
  .crisp-switch {
1870
2007
  display: inline-flex; align-items: center; flex-shrink: 0; width: 24px; height: 16px; padding: 0; margin: 0; border: none; border-radius: var(--crisp-radius-full); background: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .2s ease; -webkit-tap-highlight-color: transparent;
1871
2008
  & .crisp-switch-thumb { width: 12px; height: 12px; margin: 0 2px; border-radius: var(--crisp-radius-full); background: #fff; box-shadow: 0 0 0 1px var(--crisp-ring-switch); transition: transform .2s ease; }
@@ -2119,7 +2256,14 @@
2119
2256
  & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
2120
2257
 
2121
2258
  & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2122
- & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
2259
+ & .crisp-tabletoolbar-sorted-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
2260
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
2261
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
2262
+
2263
+ & .crisp-tabletoolbar-tool-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
2264
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
2265
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft);
2266
+ font-variant-numeric: tabular-nums; }
2123
2267
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2124
2268
  }
2125
2269
 
@@ -2144,6 +2288,12 @@
2144
2288
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 var(--crisp-space-1); border-radius: 4px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: 10px; line-height: 14px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
2145
2289
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
2146
2290
  & .crisp-tabs-indicator { position: absolute; bottom: 0; left: 0; z-index: 1; height: 1px; background: var(--crisp-fg-primary); border-radius: 2px 2px 0 0; transition: transform .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); pointer-events: none; }
2291
+ /* crisp#863: the "+N more" overflow trigger, measured live -- its computed style is identical to a plain .crisp-tabs-tab (same pill, same 6px icon/label gap the tab class already gives via space["1.5"]), and the dropdown it opens is crisp's own Menu/
2292
+ MenuItem chrome (already matches Attio's popover, nothing new to style there). Only the wrapper needs a rule: an overflowed tab (or the trigger itself, when nothing is hidden) is taken out of the flex row and out of the a11y tree without ever making its own offsetWidth unmeasurable the way display:none would -- that's what
2293
+ lets the tabs.tsx effect recompute the cutoff from cached widths on
2294
+ every resize with no flash of an unclipped row. */
2295
+ & .crisp-tabs-more { display: inline-flex; }
2296
+ & [data-tabs-overflow-hidden] { position: absolute; visibility: hidden; pointer-events: none; }
2147
2297
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
2148
2298
  & .crisp-tabs-panel:focus-visible { outline: none; }
2149
2299
  }
@@ -2171,6 +2321,12 @@
2171
2321
  .crisp-text--tone_muted {
2172
2322
  color: var(--crisp-fg-neutral-muted);
2173
2323
  }
2324
+ .crisp-text--tone_brand {
2325
+ color: var(--crisp-bg-brand-solid);
2326
+ }
2327
+ .crisp-text--tone_danger {
2328
+ color: var(--crisp-fg-danger);
2329
+ }
2174
2330
 
2175
2331
  .crisp-textarea {
2176
2332
  position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
@@ -2213,6 +2369,15 @@
2213
2369
  font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-muted);
2214
2370
  &:hover { color: var(--crisp-bg-brand-solid); }
2215
2371
  }
2372
+ .crisp-textlink--size_sm {
2373
+ font-size: var(--crisp-text-sm);
2374
+ }
2375
+ .crisp-textlink--size_md {
2376
+ font-size: var(--crisp-text-md);
2377
+ }
2378
+ .crisp-textlink--size_lg {
2379
+ font-size: var(--crisp-text-lg);
2380
+ }
2216
2381
 
2217
2382
  .crisp-toast {
2218
2383
  display: contents; font-family: var(--crisp-font-sans);
@@ -165,7 +165,14 @@
165
165
  & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
166
166
 
167
167
  & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
168
- & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
168
+ & .crisp-tabletoolbar-sorted-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
169
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
170
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
171
+
172
+ & .crisp-tabletoolbar-tool-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
173
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
174
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft);
175
+ font-variant-numeric: tabular-nums; }
169
176
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
170
177
  }
171
178
 
@@ -166,7 +166,14 @@
166
166
  & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
167
167
 
168
168
  & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
169
- & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
169
+ & .crisp-tabletoolbar-sorted-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
170
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
171
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
172
+
173
+ & .crisp-tabletoolbar-tool-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
174
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
175
+ box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft);
176
+ font-variant-numeric: tabular-nums; }
170
177
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
171
178
  }
172
179
 
package/dist/tabs.css CHANGED
@@ -11,6 +11,12 @@
11
11
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 var(--crisp-space-1); border-radius: 4px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: 10px; line-height: 14px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
12
12
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
13
13
  & .crisp-tabs-indicator { position: absolute; bottom: 0; left: 0; z-index: 1; height: 1px; background: var(--crisp-fg-primary); border-radius: 2px 2px 0 0; transition: transform .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); pointer-events: none; }
14
+ /* crisp#863: the "+N more" overflow trigger, measured live -- its computed style is identical to a plain .crisp-tabs-tab (same pill, same 6px icon/label gap the tab class already gives via space["1.5"]), and the dropdown it opens is crisp's own Menu/
15
+ MenuItem chrome (already matches Attio's popover, nothing new to style there). Only the wrapper needs a rule: an overflowed tab (or the trigger itself, when nothing is hidden) is taken out of the flex row and out of the a11y tree without ever making its own offsetWidth unmeasurable the way display:none would -- that's what
16
+ lets the tabs.tsx effect recompute the cutoff from cached widths on
17
+ every resize with no flash of an unclipped row. */
18
+ & .crisp-tabs-more { display: inline-flex; }
19
+ & [data-tabs-overflow-hidden] { position: absolute; visibility: hidden; pointer-events: none; }
14
20
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
15
21
  & .crisp-tabs-panel:focus-visible { outline: none; }
16
22
  }
@@ -12,6 +12,12 @@
12
12
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 var(--crisp-space-1); border-radius: 4px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: 10px; line-height: 14px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
13
13
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
14
14
  & .crisp-tabs-indicator { position: absolute; bottom: 0; left: 0; z-index: 1; height: 1px; background: var(--crisp-fg-primary); border-radius: 2px 2px 0 0; transition: transform .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); pointer-events: none; }
15
+ /* crisp#863: the "+N more" overflow trigger, measured live -- its computed style is identical to a plain .crisp-tabs-tab (same pill, same 6px icon/label gap the tab class already gives via space["1.5"]), and the dropdown it opens is crisp's own Menu/
16
+ MenuItem chrome (already matches Attio's popover, nothing new to style there). Only the wrapper needs a rule: an overflowed tab (or the trigger itself, when nothing is hidden) is taken out of the flex row and out of the a11y tree without ever making its own offsetWidth unmeasurable the way display:none would -- that's what
17
+ lets the tabs.tsx effect recompute the cutoff from cached widths on
18
+ every resize with no flash of an unclipped row. */
19
+ & .crisp-tabs-more { display: inline-flex; }
20
+ & [data-tabs-overflow-hidden] { position: absolute; visibility: hidden; pointer-events: none; }
15
21
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
16
22
  & .crisp-tabs-panel:focus-visible { outline: none; }
17
23
  }
package/dist/text.css CHANGED
@@ -7,3 +7,9 @@
7
7
  .crisp-text--tone_muted {
8
8
  color: var(--crisp-fg-neutral-muted);
9
9
  }
10
+ .crisp-text--tone_brand {
11
+ color: var(--crisp-bg-brand-solid);
12
+ }
13
+ .crisp-text--tone_danger {
14
+ color: var(--crisp-fg-danger);
15
+ }
package/dist/text.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface TextVariants {
4
- tone?: "default" | "muted";
4
+ tone?: "default" | "muted" | "brand" | "danger";
5
5
  }
6
6
 
7
- export declare const textRecipe: RecipeFn<{ tone: { default: string; muted: string } }>;
7
+ export declare const textRecipe: RecipeFn<{ tone: { default: string; muted: string; brand: string; danger: string } }>;
@@ -8,4 +8,10 @@
8
8
  .crisp-text--tone_muted {
9
9
  color: var(--crisp-fg-neutral-muted);
10
10
  }
11
+ .crisp-text--tone_brand {
12
+ color: var(--crisp-bg-brand-solid);
13
+ }
14
+ .crisp-text--tone_danger {
15
+ color: var(--crisp-fg-danger);
16
+ }
11
17
  }
package/dist/textlink.css CHANGED
@@ -9,3 +9,12 @@
9
9
  font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-muted);
10
10
  &:hover { color: var(--crisp-bg-brand-solid); }
11
11
  }
12
+ .crisp-textlink--size_sm {
13
+ font-size: var(--crisp-text-sm);
14
+ }
15
+ .crisp-textlink--size_md {
16
+ font-size: var(--crisp-text-md);
17
+ }
18
+ .crisp-textlink--size_lg {
19
+ font-size: var(--crisp-text-lg);
20
+ }
@@ -2,6 +2,7 @@ import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface TextlinkVariants {
4
4
  tone?: "brand" | "muted";
5
+ size?: "sm" | "md" | "lg";
5
6
  }
6
7
 
7
- export declare const textlinkRecipe: RecipeFn<{ tone: { brand: string; muted: string } }>;
8
+ export declare const textlinkRecipe: RecipeFn<{ tone: { brand: string; muted: string }; size: { sm: string; md: string; lg: string } }>;
@@ -10,4 +10,13 @@
10
10
  font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-muted);
11
11
  &:hover { color: var(--crisp-bg-brand-solid); }
12
12
  }
13
+ .crisp-textlink--size_sm {
14
+ font-size: var(--crisp-text-sm);
15
+ }
16
+ .crisp-textlink--size_md {
17
+ font-size: var(--crisp-text-md);
18
+ }
19
+ .crisp-textlink--size_lg {
20
+ font-size: var(--crisp-text-lg);
21
+ }
13
22
  }