@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
@@ -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;
@@ -437,6 +462,12 @@
437
462
  &[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
438
463
  &[data-tone="danger"] .crisp-callout-icon { color: var(--crisp-bg-danger); }
439
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
+ }
440
471
 
441
472
  .crisp-card {
442
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);
@@ -1356,6 +1387,10 @@
1356
1387
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
1357
1388
  &:has(.crisp-input-value:disabled) { opacity: 1; }
1358
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
+ }
1359
1394
  .crisp-input--size_medium {
1360
1395
  height: 32px; border-radius: var(--crisp-radius-md);
1361
1396
  & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
@@ -1436,6 +1471,14 @@
1436
1471
  .crisp-menu-item--danger svg { color: var(--crisp-bg-danger); }
1437
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); }
1438
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; }
1439
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; }
1440
1483
  .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
1441
1484
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -1456,38 +1499,42 @@
1456
1499
  }
1457
1500
 
1458
1501
 
1459
- /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1460
- on document.body while open (Zag re-enables it only on layers IT
1461
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1462
- inherited the lock, so every option was unclickable, hit-testing
1463
- right through to whatever real element sat behind it. */
1502
+ /* pointer-events: auto (#375) -- see Select's matching comment. */
1464
1503
  .crisp-multiselect-listbox {
1465
1504
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1466
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1467
- max-height: 300px; overflow-y: auto; padding: var(--crisp-space-1);
1468
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1469
- 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);
1470
- 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);
1471
1511
  }
1472
1512
  .crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
1473
- .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1474
- .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); }
1475
1518
  .crisp-multiselect-search svg { flex-shrink: 0; }
1476
- .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); }
1477
- .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-muted); }
1478
- /* #343: same measured values as Menu's own group heading
1479
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1480
- same "small muted heading above a run of rows" treatment. */
1481
- .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); }
1482
1523
  .crisp-multiselect-option {
1483
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1484
- 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);
1485
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). */
1486
1532
  .crisp-multiselect-option[data-highlighted] { background: var(--crisp-bg-raised); }
1533
+ .crisp-multiselect-option:active { background: var(--crisp-bg-select-pressed); }
1487
1534
  .crisp-multiselect-option-chip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
1488
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; }
1489
1536
  .crisp-multiselect-option[aria-selected="true"] .crisp-multiselect-check { background: var(--crisp-bg-brand-solid); box-shadow: none; color: #fff; }
1490
- .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); }
1491
1538
 
1492
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); }
1493
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); }
@@ -1500,21 +1547,41 @@
1500
1547
 
1501
1548
  .crisp-multiselect {
1502
1549
  position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1503
- & .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; }
1504
- & .crisp-multiselect-trigger:hover { background: var(--crisp-bg-raised); }
1505
- & .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); }
1506
- & .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; }
1507
1558
  & .crisp-multiselect-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); }
1508
- & .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
1509
- & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1510
- & .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); }
1511
1571
  }
1512
1572
  .crisp-multiselect--invalid_true {
1513
- & .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); }
1514
1574
  }
1515
1575
  .crisp-multiselect--invalid_false {
1516
1576
 
1517
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
+ }
1518
1585
 
1519
1586
  @media (prefers-reduced-motion: reduce) {
1520
1587
  .crisp-multiselect, .crisp-multiselect * {
@@ -1697,58 +1764,69 @@
1697
1764
 
1698
1765
 
1699
1766
  /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1700
- on document.body while open (Zag re-enables it only on layers IT
1701
- tracks), and the portaled listbox is outside Zag's knowledge -- it
1702
- inherited the lock, so every option was unclickable, hit-testing
1703
- 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. */
1704
1769
  .crisp-select-listbox {
1705
1770
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1706
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
1707
- max-height: 280px; overflow-y: auto; padding: var(--crisp-space-1);
1708
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
1709
- 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);
1710
- 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);
1711
1777
  }
1712
- .crisp-select-listbox[data-up] { transform-origin: bottom; }
1713
- .crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
1714
- .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); }
1715
1781
  .crisp-select-search svg { flex-shrink: 0; }
1716
- .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); }
1717
- .crisp-select-search input::placeholder { color: var(--crisp-fg-muted); }
1718
- .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); }
1719
- /* #343: same measured values as Menu's own group heading
1720
- (crisp-menu-label) -- reused rather than re-measured, since it's the
1721
- same "small muted heading above a run of rows" treatment. */
1722
- .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); }
1723
1788
  .crisp-select-option {
1724
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
1725
- padding: 0 var(--crisp-space-2); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-md);
1726
- 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);
1727
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. */
1728
1798
  .crisp-select-option[data-highlighted] { background: var(--crisp-bg-raised); }
1729
- .crisp-select-option[data-disabled] { opacity: 0.5; cursor: not-allowed; }
1730
- .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); }
1731
1806
  .crisp-select-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1732
- .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); }
1733
1809
 
1734
1810
  .crisp-select {
1735
1811
  position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1736
- & .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; }
1737
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); }
1738
- & .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); }
1739
- & .crisp-select-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
1740
- & .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; }
1741
- & .crisp-select-placeholder { color: var(--crisp-fg-muted); }
1742
- & .crisp-select-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
1743
- & .crisp-select-trigger[data-open] .crisp-select-chevron { transform: rotate(180deg); }
1744
- }
1745
- .crisp-select--variant_default {
1746
-
1747
- }
1748
- .crisp-select--variant_ghost {
1749
- & .crisp-select-trigger { background: transparent; box-shadow: none; }
1750
- & .crisp-select-trigger:hover { background: var(--crisp-bg-raised); box-shadow: none; }
1751
- & .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; }
1752
1830
  }
1753
1831
  .crisp-select--width_full {
1754
1832
  width: 100%;
@@ -1756,12 +1834,39 @@
1756
1834
  .crisp-select--width_auto {
1757
1835
  width: auto;
1758
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
+ }
1759
1857
  .crisp-select--invalid_true {
1760
- & .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); }
1761
1859
  }
1762
1860
  .crisp-select--invalid_false {
1763
1861
 
1764
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
+ }
1765
1870
 
1766
1871
  @media (prefers-reduced-motion: reduce) {
1767
1872
  .crisp-select, .crisp-select * {
@@ -2151,7 +2256,14 @@
2151
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; }
2152
2257
 
2153
2258
  & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2154
- & .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; }
2155
2267
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2156
2268
  }
2157
2269
 
@@ -2176,6 +2288,12 @@
2176
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); }
2177
2289
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
2178
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; }
2179
2297
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
2180
2298
  & .crisp-tabs-panel:focus-visible { outline: none; }
2181
2299
  }
@@ -2251,6 +2369,15 @@
2251
2369
  font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-muted);
2252
2370
  &:hover { color: var(--crisp-bg-brand-solid); }
2253
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
+ }
2254
2381
 
2255
2382
  .crisp-toast {
2256
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/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
  }
package/dist/tokens.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;