@crisp-ui-kit/crisp 0.56.0 → 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 (90) 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/tabletoolbar/tabletoolbar.js +3 -3
  15. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  16. package/dist/cjs/components/tabs/tabs.js +63 -4
  17. package/dist/cjs/components/tabs/tabs.recipe.js +13 -0
  18. package/dist/cjs/components/textlink/textlink.js +2 -2
  19. package/dist/cjs/components/textlink/textlink.recipe.js +18 -0
  20. package/dist/cjs/product/recordpage/recordpage.js +17 -4
  21. package/dist/cjs/tokens/elevation.js +7 -0
  22. package/dist/cjs/tokens/palette.js +41 -0
  23. package/dist/cjs/tokens/scale.js +8 -0
  24. package/dist/cjs/tokens/semantic.js +18 -0
  25. package/dist/esm/components/callout/callout.js +2 -2
  26. package/dist/esm/components/callout/callout.recipe.js +15 -2
  27. package/dist/esm/components/input/input.recipe.js +6 -0
  28. package/dist/esm/components/menu/menu.js +2 -2
  29. package/dist/esm/components/menu/menu.recipe.js +8 -0
  30. package/dist/esm/components/multiselect/multiselect.js +23 -10
  31. package/dist/esm/components/multiselect/multiselect.recipe.js +95 -65
  32. package/dist/esm/components/select/listbox-keyboard.js +7 -1
  33. package/dist/esm/components/select/select.js +24 -11
  34. package/dist/esm/components/select/select.recipe.js +150 -78
  35. package/dist/esm/components/tabletoolbar/tabletoolbar.js +3 -3
  36. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  37. package/dist/esm/components/tabs/tabs.js +63 -4
  38. package/dist/esm/components/tabs/tabs.recipe.js +13 -0
  39. package/dist/esm/components/textlink/textlink.js +2 -2
  40. package/dist/esm/components/textlink/textlink.recipe.js +18 -0
  41. package/dist/esm/product/recordpage/recordpage.js +18 -5
  42. package/dist/esm/tokens/elevation.js +7 -0
  43. package/dist/esm/tokens/palette.js +41 -0
  44. package/dist/esm/tokens/scale.js +8 -0
  45. package/dist/esm/tokens/semantic.js +18 -0
  46. package/dist/input.css +4 -0
  47. package/dist/input.d.ts +2 -2
  48. package/dist/input.layered.css +4 -0
  49. package/dist/menu.css +8 -0
  50. package/dist/menu.layered.css +8 -0
  51. package/dist/multiselect.css +53 -29
  52. package/dist/multiselect.d.ts +3 -1
  53. package/dist/multiselect.layered.css +53 -29
  54. package/dist/native/tokens.d.ts +1 -1
  55. package/dist/native/tokens.js +49 -6
  56. package/dist/select.css +80 -42
  57. package/dist/select.d.ts +3 -2
  58. package/dist/select.layered.css +80 -42
  59. package/dist/styles.css +199 -72
  60. package/dist/styles.layered.css +199 -72
  61. package/dist/tabletoolbar.css +8 -1
  62. package/dist/tabletoolbar.layered.css +8 -1
  63. package/dist/tabs.css +6 -0
  64. package/dist/tabs.layered.css +6 -0
  65. package/dist/textlink.css +9 -0
  66. package/dist/textlink.d.ts +2 -1
  67. package/dist/textlink.layered.css +9 -0
  68. package/dist/tokens.css +25 -0
  69. package/dist/tokens.json +49 -6
  70. package/dist/types/components/callout/callout.d.ts +9 -1
  71. package/dist/types/components/callout/callout.recipe.d.ts +12 -2
  72. package/dist/types/components/input/input.d.ts +2 -0
  73. package/dist/types/components/input/input.recipe.d.ts +3 -0
  74. package/dist/types/components/menu/menu.d.ts +7 -1
  75. package/dist/types/components/multiselect/multiselect.d.ts +24 -6
  76. package/dist/types/components/multiselect/multiselect.recipe.d.ts +24 -0
  77. package/dist/types/components/select/listbox-keyboard.d.ts +10 -1
  78. package/dist/types/components/select/select.d.ts +28 -6
  79. package/dist/types/components/select/select.recipe.d.ts +22 -10
  80. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +14 -0
  81. package/dist/types/components/tabs/tabs.d.ts +9 -1
  82. package/dist/types/components/textlink/textlink.d.ts +8 -1
  83. package/dist/types/components/textlink/textlink.recipe.d.ts +10 -0
  84. package/dist/types/product/recordpage/recordpage.d.ts +25 -1
  85. package/dist/types/tokens/contract.d.ts +13 -3
  86. package/dist/types/tokens/elevation.d.ts +7 -0
  87. package/dist/types/tokens/palette.d.ts +36 -0
  88. package/dist/types/tokens/scale.d.ts +5 -0
  89. package/dist/types/tokens/semantic.d.ts +18 -0
  90. package/package.json +1 -1
package/dist/styles.css CHANGED
@@ -82,6 +82,16 @@
82
82
  --crisp-bg-purple-soft: #f5f0ff;
83
83
  --crisp-fg-purple-soft: #6238b5;
84
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;
85
95
  }
86
96
 
87
97
  [data-theme="dark"] {
@@ -140,6 +150,16 @@
140
150
  --crisp-bg-purple-soft: #2f1e5a;
141
151
  --crisp-fg-purple-soft: #d8c4ff;
142
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;
143
163
  }
144
164
 
145
165
  :root {
@@ -154,12 +174,14 @@
154
174
  --crisp-space-0-5: 2px;
155
175
  --crisp-space-1-5: 6px;
156
176
  --crisp-space-2-5: 10px;
177
+ --crisp-space-3-5: 14px;
157
178
  --crisp-radius-chip: 7px;
158
179
  --crisp-radius-sm: 6px;
159
180
  --crisp-radius-md: 8px;
160
181
  --crisp-radius-lg: 9px;
161
182
  --crisp-radius-xl: 12px;
162
183
  --crisp-radius-2xl: 16px;
184
+ --crisp-radius-3xl: 20px;
163
185
  --crisp-radius-full: 999px;
164
186
  --crisp-size-icon: 16px;
165
187
  --crisp-size-chip: 22px;
@@ -170,6 +192,9 @@
170
192
  --crisp-size-header: 48px;
171
193
  --crisp-size-calendarCell: 34px;
172
194
  --crisp-size-calendarPill: 32px;
195
+ --crisp-size-selectTriggerMd: 40px;
196
+ --crisp-size-selectTriggerLg: 52px;
197
+ --crisp-size-selectIconLg: 20px;
173
198
  --crisp-text-xs: 11px;
174
199
  --crisp-text-sm: 12px;
175
200
  --crisp-text-md: 13px;
@@ -436,6 +461,12 @@
436
461
  &[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
437
462
  &[data-tone="danger"] .crisp-callout-icon { color: var(--crisp-bg-danger); }
438
463
  }
464
+ .crisp-callout--bordered_true {
465
+ 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);
466
+ }
467
+ .crisp-callout--bordered_false {
468
+
469
+ }
439
470
 
440
471
  .crisp-card {
441
472
  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);
@@ -1355,6 +1386,10 @@
1355
1386
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
1356
1387
  &:has(.crisp-input-value:disabled) { opacity: 1; }
1357
1388
  }
1389
+ .crisp-input--size_sm {
1390
+ height: var(--crisp-size-row); border-radius: var(--crisp-radius-md);
1391
+ & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
1392
+ }
1358
1393
  .crisp-input--size_medium {
1359
1394
  height: 32px; border-radius: var(--crisp-radius-md);
1360
1395
  & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
@@ -1435,6 +1470,14 @@
1435
1470
  .crisp-menu-item--danger svg { color: var(--crisp-bg-danger); }
1436
1471
  .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); }
1437
1472
  .crisp-menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1473
+ /* crisp#875: same measured neutral chip TableToolbar's own ToolbarTool
1474
+ count prop uses (re-confirmed on two live Attio surfaces this round:
1475
+ the Sort tool's own "+N" overflow, a Kanban board's stage-column
1476
+ count) -- a small badge, not bare text. */
1477
+ .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;
1478
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
1479
+ 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);
1480
+ font-variant-numeric: tabular-nums; }
1438
1481
  .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; }
1439
1482
  .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
1440
1483
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -1455,38 +1498,42 @@
1455
1498
  }
1456
1499
 
1457
1500
 
1458
- /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1459
- on document.body while open (Zag re-enables it only on layers IT
1460
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1461
- inherited the lock, so every option was unclickable, hit-testing
1462
- right through to whatever real element sat behind it. */
1501
+ /* pointer-events: auto (#375) -- see Select's matching comment. */
1463
1502
  .crisp-multiselect-listbox {
1464
1503
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1465
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1466
- max-height: 300px; overflow-y: auto; padding: var(--crisp-space-1);
1467
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1468
- 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);
1469
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
1504
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
1505
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
1506
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
1507
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
1508
+ transform-origin: top left; opacity: 1; transform: none;
1509
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
1470
1510
  }
1471
1511
  .crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
1472
- .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1473
- .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); }
1512
+ .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
1513
+ /* LOCKED geometry (#481, guarded by build/spec-check.mjs): height 36px,
1514
+ gap space["1.5"] (6px), no border-bottom -- an Attio measurement, not
1515
+ part of this seed-parity pass. Only colours below follow Select. */
1516
+ .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); }
1474
1517
  .crisp-multiselect-search svg { flex-shrink: 0; }
1475
- .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); }
1476
- .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-muted); }
1477
- /* #343: same measured values as Menu's own group heading
1478
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1479
- same "small muted heading above a run of rows" treatment. */
1480
- .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); }
1518
+ .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); }
1519
+ .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
1520
+ /* Same groupLabel treatment as Select's (13px/regular/neutral-subtle). */
1521
+ .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); }
1481
1522
  .crisp-multiselect-option {
1482
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1483
- 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;
1523
+ box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0;
1524
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
1525
+ font-family: inherit; text-align: start; cursor: pointer;
1526
+ transition: background-color .15s cubic-bezier(.35,0,.35,1);
1484
1527
  }
1528
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill, same
1529
+ reasoning as Select's matching rule (seed's own pressed tint is too
1530
+ faint for a keyboard-navigation indicator). */
1485
1531
  .crisp-multiselect-option[data-highlighted] { background: var(--crisp-bg-raised); }
1532
+ .crisp-multiselect-option:active { background: var(--crisp-bg-select-pressed); }
1486
1533
  .crisp-multiselect-option-chip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
1487
1534
  .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; }
1488
1535
  .crisp-multiselect-option[aria-selected="true"] .crisp-multiselect-check { background: var(--crisp-bg-brand-solid); box-shadow: none; color: #fff; }
1489
- .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); }
1536
+ .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); }
1490
1537
 
1491
1538
  .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); }
1492
1539
  .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); }
@@ -1499,21 +1546,41 @@
1499
1546
 
1500
1547
  .crisp-multiselect {
1501
1548
  position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1502
- & .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; }
1503
- & .crisp-multiselect-trigger:hover { background: var(--crisp-bg-raised); }
1504
- & .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); }
1505
- & .crisp-multiselect-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
1549
+ & .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); }
1550
+ /* Same pressed/disabled/readonly/focus model as Select's trigger -- see
1551
+ that recipe's comment for why hover and an open/focus ring are gone. */
1552
+ & .crisp-multiselect-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
1553
+ & .crisp-multiselect-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
1554
+ & .crisp-multiselect-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
1555
+ & .crisp-multiselect-trigger:disabled .crisp-multiselect-placeholder, & .crisp-multiselect-trigger:disabled .crisp-multiselect-chevron { color: var(--crisp-fg-disabled); }
1556
+ & .crisp-multiselect-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
1506
1557
  & .crisp-multiselect-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); }
1507
- & .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
1508
- & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1509
- & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); }
1558
+ & .crisp-multiselect-placeholder { color: var(--crisp-fg-select-placeholder); }
1559
+ /* closeRotateDuration/openRotateDuration, same asymmetric timing as Select's own chevron (see that recipe's comment). */
1560
+ & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
1561
+ & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); transition-duration: .15s; }
1562
+ }
1563
+ .crisp-multiselect--size_medium {
1564
+ & .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); }
1565
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
1566
+ }
1567
+ .crisp-multiselect--size_large {
1568
+ & .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); }
1569
+ & .crisp-multiselect-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
1510
1570
  }
1511
1571
  .crisp-multiselect--invalid_true {
1512
- & .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); }
1572
+ & .crisp-multiselect-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
1513
1573
  }
1514
1574
  .crisp-multiselect--invalid_false {
1515
1575
 
1516
1576
  }
1577
+ .crisp-multiselect--variant_default {
1578
+
1579
+ }
1580
+ .crisp-multiselect--variant_ghost {
1581
+ & .crisp-multiselect-trigger { min-height: 0; height: 100%; border-color: transparent; }
1582
+ & .crisp-multiselect-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
1583
+ }
1517
1584
 
1518
1585
  @media (prefers-reduced-motion: reduce) {
1519
1586
  .crisp-multiselect, .crisp-multiselect * {
@@ -1696,58 +1763,69 @@
1696
1763
 
1697
1764
 
1698
1765
  /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1699
- on document.body while open (Zag re-enables it only on layers IT
1700
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1701
- inherited the lock, so every option was unclickable, hit-testing
1702
- right through to whatever real element sat behind it. */
1766
+ on document.body while open, and the portaled listbox is outside its
1767
+ knowledge -- it inherited the lock without this. */
1703
1768
  .crisp-select-listbox {
1704
1769
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1705
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1706
- max-height: 280px; overflow-y: auto; padding: var(--crisp-space-1);
1707
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1708
- 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);
1709
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
1770
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
1771
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
1772
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
1773
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
1774
+ transform-origin: top left; opacity: 1; transform: none;
1775
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
1710
1776
  }
1711
- .crisp-select-listbox[data-up] { transform-origin: bottom; }
1712
- .crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1713
- .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); }
1777
+ .crisp-select-listbox[data-up] { transform-origin: bottom left; }
1778
+ .crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
1779
+ .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); }
1714
1780
  .crisp-select-search svg { flex-shrink: 0; }
1715
- .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); }
1716
- .crisp-select-search input::placeholder { color: var(--crisp-fg-muted); }
1717
- .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); }
1718
- /* #343: same measured values as Menu's own group heading
1719
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1720
- same "small muted heading above a run of rows" treatment. */
1721
- .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); }
1781
+ .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); }
1782
+ .crisp-select-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
1783
+ .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); }
1784
+ /* select.yaml's groupLabel -- font-size/line-height/weight are the same
1785
+ across sizes, only paddingY differs (medium 8px, large 10px). */
1786
+ .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); }
1722
1787
  .crisp-select-option {
1723
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1724
- padding: 0 var(--crisp-space-2); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-md);
1725
- font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); text-align: start; cursor: pointer; white-space: nowrap;
1788
+ box-sizing: border-box; display: flex; align-items: center; flex-shrink: 0;
1789
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin-inline: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
1790
+ font-family: inherit; color: var(--crisp-fg-select-neutral); text-align: start; cursor: pointer; white-space: nowrap;
1791
+ transition: background-color .15s cubic-bezier(.35,0,.35,1), margin-inline .15s cubic-bezier(.35,0,.35,1);
1726
1792
  }
1793
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill --
1794
+ seed's spec has no "highlighted" state at all (only pressed/selected/
1795
+ disabled), and its actual pressed tint (below) is a ~3% black hint,
1796
+ too faint to serve as a keyboard-navigation indicator. */
1727
1797
  .crisp-select-option[data-highlighted] { background: var(--crisp-bg-raised); }
1728
- .crisp-select-option[data-disabled] { opacity: 0.5; cursor: not-allowed; }
1729
- .crisp-select-option-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
1798
+ .crisp-select-option:active:not([data-disabled]) { background: var(--crisp-bg-select-pressed); margin-inline: var(--crisp-space-2); }
1799
+ .crisp-select-option[data-disabled] { cursor: not-allowed; }
1800
+ .crisp-select-option[data-disabled] .crisp-select-option-label,
1801
+ .crisp-select-option[data-disabled] .crisp-select-option-description,
1802
+ .crisp-select-option[data-disabled] .crisp-select-option-check { color: var(--crisp-fg-disabled); }
1803
+ .crisp-select-option-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); margin-inline-end: var(--crisp-space-2); }
1804
+ .crisp-select-option-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--crisp-space-0-5); }
1730
1805
  .crisp-select-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1731
- .crisp-select-option-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
1806
+ .crisp-select-option-description { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--crisp-fg-neutral-subtle); }
1807
+ .crisp-select-option-check { flex-shrink: 0; color: var(--crisp-fg-select-neutral); margin-inline-start: var(--crisp-space-2); }
1732
1808
 
1733
1809
  .crisp-select {
1734
1810
  position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1735
- & .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; }
1736
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); }
1737
- & .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); }
1738
- & .crisp-select-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
1739
- & .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; }
1740
- & .crisp-select-placeholder { color: var(--crisp-fg-muted); }
1741
- & .crisp-select-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1742
- & .crisp-select-trigger[data-open] .crisp-select-chevron { transform: rotate(180deg); }
1743
- }
1744
- .crisp-select--variant_default {
1745
-
1746
- }
1747
- .crisp-select--variant_ghost {
1748
- & .crisp-select-trigger { background: transparent; box-shadow: none; }
1749
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); box-shadow: none; }
1750
- & .crisp-select-trigger[data-open], & .crisp-select-trigger:focus-visible { background: transparent; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
1811
+ & .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); }
1812
+ /* 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
1813
+ never moves. */
1814
+ /* 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). */
1815
+ & .crisp-select-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
1816
+ & .crisp-select-trigger:active:not(:disabled):not([aria-readonly="true"]) .crisp-select-value { transform: scale(0.98); }
1817
+ & .crisp-select-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
1818
+ & .crisp-select-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
1819
+ & .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); }
1820
+ /* readonly: same fill as disabled, but the value/placeholder text stays normal ink -- it reads as "answered and locked", not "answered and
1821
+ greyed out" (select-trigger.yaml's readonly state only overrides
1822
+ root.color; value/placeholder keep their enabled colours). */
1823
+ & .crisp-select-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
1824
+ & .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); }
1825
+ & .crisp-select-placeholder { color: var(--crisp-fg-select-placeholder); }
1826
+ /* closeRotateDuration/closeRotateTimingFunction: 100ms, seed's "easing" curve. Overridden to 150ms on open (openRotateDuration) below -- same curve both ways, only the duration differs. */
1827
+ & .crisp-select-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
1828
+ & .crisp-select-trigger[data-open] .crisp-select-chevron { transform: rotate(180deg); transition-duration: .15s; }
1751
1829
  }
1752
1830
  .crisp-select--width_full {
1753
1831
  width: 100%;
@@ -1755,12 +1833,39 @@
1755
1833
  .crisp-select--width_auto {
1756
1834
  width: auto;
1757
1835
  }
1836
+ .crisp-select--size_medium {
1837
+ & .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); }
1838
+ & .crisp-select-value, & .crisp-select-placeholder { font-size: var(--crisp-text-lg); line-height: 19px; }
1839
+ & .crisp-select-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
1840
+ .crisp-select-option { gap: var(--crisp-space-2); }
1841
+ .crisp-select-option-label { font-size: var(--crisp-text-lg); line-height: 19px; }
1842
+ .crisp-select-option-description { font-size: var(--crisp-text-sm); line-height: 16px; }
1843
+ .crisp-select-option-check { width: var(--crisp-space-3); height: var(--crisp-space-3); }
1844
+ .crisp-select-label { padding-top: var(--crisp-space-2); }
1845
+ }
1846
+ .crisp-select--size_large {
1847
+ & .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); }
1848
+ & .crisp-select-value, & .crisp-select-placeholder { font-size: var(--crisp-text-xl); line-height: 22px; }
1849
+ & .crisp-select-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
1850
+ .crisp-select-option { gap: var(--crisp-space-3); }
1851
+ .crisp-select-option-label { font-size: var(--crisp-text-xl); line-height: 22px; }
1852
+ .crisp-select-option-description { font-size: var(--crisp-text-md); line-height: 18px; }
1853
+ .crisp-select-option-check { width: var(--crisp-space-3-5); height: var(--crisp-space-3-5); }
1854
+ .crisp-select-label { padding-top: var(--crisp-space-2-5); }
1855
+ }
1758
1856
  .crisp-select--invalid_true {
1759
- & .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); }
1857
+ & .crisp-select-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
1760
1858
  }
1761
1859
  .crisp-select--invalid_false {
1762
1860
 
1763
1861
  }
1862
+ .crisp-select--variant_default {
1863
+
1864
+ }
1865
+ .crisp-select--variant_ghost {
1866
+ & .crisp-select-trigger { height: 100%; border-color: transparent; }
1867
+ & .crisp-select-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
1868
+ }
1764
1869
 
1765
1870
  @media (prefers-reduced-motion: reduce) {
1766
1871
  .crisp-select, .crisp-select * {
@@ -2150,7 +2255,14 @@
2150
2255
  & .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; }
2151
2256
 
2152
2257
  & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2153
- & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
2258
+ & .crisp-tabletoolbar-sorted-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
2259
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
2260
+ 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); }
2261
+
2262
+ & .crisp-tabletoolbar-tool-count { display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
2263
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
2264
+ 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);
2265
+ font-variant-numeric: tabular-nums; }
2154
2266
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2155
2267
  }
2156
2268
 
@@ -2175,6 +2287,12 @@
2175
2287
  & .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); }
2176
2288
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
2177
2289
  & .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; }
2290
+ /* 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/
2291
+ 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
2292
+ lets the tabs.tsx effect recompute the cutoff from cached widths on
2293
+ every resize with no flash of an unclipped row. */
2294
+ & .crisp-tabs-more { display: inline-flex; }
2295
+ & [data-tabs-overflow-hidden] { position: absolute; visibility: hidden; pointer-events: none; }
2178
2296
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
2179
2297
  & .crisp-tabs-panel:focus-visible { outline: none; }
2180
2298
  }
@@ -2250,6 +2368,15 @@
2250
2368
  font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-muted);
2251
2369
  &:hover { color: var(--crisp-bg-brand-solid); }
2252
2370
  }
2371
+ .crisp-textlink--size_sm {
2372
+ font-size: var(--crisp-text-sm);
2373
+ }
2374
+ .crisp-textlink--size_md {
2375
+ font-size: var(--crisp-text-md);
2376
+ }
2377
+ .crisp-textlink--size_lg {
2378
+ font-size: var(--crisp-text-lg);
2379
+ }
2253
2380
 
2254
2381
  .crisp-toast {
2255
2382
  display: contents; font-family: var(--crisp-font-sans);