@crisp-ui-kit/crisp 0.56.0 → 0.57.1

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 (139) hide show
  1. package/dist/accentpicker.css +5 -0
  2. package/dist/accentpicker.d.ts +7 -0
  3. package/dist/accentpicker.layered.css +7 -0
  4. package/dist/avatar.css +1 -1
  5. package/dist/avatar.layered.css +1 -1
  6. package/dist/badge.css +2 -2
  7. package/dist/badge.layered.css +2 -2
  8. package/dist/button.css +3 -3
  9. package/dist/button.layered.css +3 -3
  10. package/dist/callout.css +6 -0
  11. package/dist/callout.d.ts +2 -2
  12. package/dist/callout.layered.css +6 -0
  13. package/dist/cjs/components/accentpicker/accentpicker.js +66 -0
  14. package/dist/cjs/components/accentpicker/accentpicker.recipe.js +25 -0
  15. package/dist/cjs/components/accentpicker/index.js +7 -0
  16. package/dist/cjs/components/avatar/avatar.recipe.js +4 -1
  17. package/dist/cjs/components/badge/badge.recipe.js +16 -2
  18. package/dist/cjs/components/button/button.recipe.js +16 -6
  19. package/dist/cjs/components/callout/callout.js +2 -2
  20. package/dist/cjs/components/callout/callout.recipe.js +15 -2
  21. package/dist/cjs/components/input/input.js +2 -2
  22. package/dist/cjs/components/input/input.recipe.js +14 -0
  23. package/dist/cjs/components/menu/menu.js +2 -2
  24. package/dist/cjs/components/menu/menu.recipe.js +8 -0
  25. package/dist/cjs/components/multiselect/multiselect.js +23 -10
  26. package/dist/cjs/components/multiselect/multiselect.recipe.js +104 -65
  27. package/dist/cjs/components/progress/progress.js +21 -4
  28. package/dist/cjs/components/progress/progress.recipe.js +30 -0
  29. package/dist/cjs/components/select/listbox-keyboard.js +7 -1
  30. package/dist/cjs/components/select/select.js +24 -11
  31. package/dist/cjs/components/select/select.recipe.js +177 -78
  32. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +3 -3
  33. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  34. package/dist/cjs/components/tabs/tabs.js +63 -4
  35. package/dist/cjs/components/tabs/tabs.recipe.js +13 -0
  36. package/dist/cjs/components/text/text.recipe.js +8 -7
  37. package/dist/cjs/components/textarea/textarea.js +3 -2
  38. package/dist/cjs/components/textarea/textarea.recipe.js +2 -1
  39. package/dist/cjs/components/textlink/textlink.js +2 -2
  40. package/dist/cjs/components/textlink/textlink.recipe.js +24 -3
  41. package/dist/cjs/index.js +1 -0
  42. package/dist/cjs/product/recordpage/recordpage.js +61 -7
  43. package/dist/cjs/tokens/elevation.js +7 -0
  44. package/dist/cjs/tokens/palette.js +120 -25
  45. package/dist/cjs/tokens/scale.js +9 -0
  46. package/dist/cjs/tokens/semantic.js +42 -0
  47. package/dist/esm/components/accentpicker/accentpicker.js +30 -0
  48. package/dist/esm/components/accentpicker/accentpicker.recipe.js +22 -0
  49. package/dist/esm/components/accentpicker/index.js +2 -0
  50. package/dist/esm/components/avatar/avatar.recipe.js +4 -1
  51. package/dist/esm/components/badge/badge.recipe.js +16 -2
  52. package/dist/esm/components/button/button.recipe.js +16 -6
  53. package/dist/esm/components/callout/callout.js +2 -2
  54. package/dist/esm/components/callout/callout.recipe.js +15 -2
  55. package/dist/esm/components/input/input.js +3 -3
  56. package/dist/esm/components/input/input.recipe.js +14 -0
  57. package/dist/esm/components/menu/menu.js +2 -2
  58. package/dist/esm/components/menu/menu.recipe.js +8 -0
  59. package/dist/esm/components/multiselect/multiselect.js +23 -10
  60. package/dist/esm/components/multiselect/multiselect.recipe.js +104 -65
  61. package/dist/esm/components/progress/progress.js +22 -5
  62. package/dist/esm/components/progress/progress.recipe.js +30 -0
  63. package/dist/esm/components/select/listbox-keyboard.js +7 -1
  64. package/dist/esm/components/select/select.js +24 -11
  65. package/dist/esm/components/select/select.recipe.js +177 -78
  66. package/dist/esm/components/tabletoolbar/tabletoolbar.js +3 -3
  67. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  68. package/dist/esm/components/tabs/tabs.js +63 -4
  69. package/dist/esm/components/tabs/tabs.recipe.js +13 -0
  70. package/dist/esm/components/text/text.recipe.js +8 -7
  71. package/dist/esm/components/textarea/textarea.js +3 -2
  72. package/dist/esm/components/textarea/textarea.recipe.js +2 -1
  73. package/dist/esm/components/textlink/textlink.js +2 -2
  74. package/dist/esm/components/textlink/textlink.recipe.js +24 -3
  75. package/dist/esm/index.js +1 -0
  76. package/dist/esm/product/recordpage/recordpage.js +62 -8
  77. package/dist/esm/tokens/elevation.js +7 -0
  78. package/dist/esm/tokens/palette.js +120 -25
  79. package/dist/esm/tokens/scale.js +9 -0
  80. package/dist/esm/tokens/semantic.js +42 -0
  81. package/dist/input.css +11 -0
  82. package/dist/input.d.ts +2 -2
  83. package/dist/input.layered.css +11 -0
  84. package/dist/menu.css +8 -0
  85. package/dist/menu.layered.css +8 -0
  86. package/dist/multiselect.css +57 -29
  87. package/dist/multiselect.d.ts +3 -1
  88. package/dist/multiselect.layered.css +57 -29
  89. package/dist/native/tokens.d.ts +1 -1
  90. package/dist/native/tokens.js +82 -28
  91. package/dist/progress.css +3 -0
  92. package/dist/progress.layered.css +3 -0
  93. package/dist/select.css +100 -42
  94. package/dist/select.d.ts +3 -2
  95. package/dist/select.layered.css +100 -42
  96. package/dist/styles.css +268 -93
  97. package/dist/styles.layered.css +268 -93
  98. package/dist/tabletoolbar.css +8 -1
  99. package/dist/tabletoolbar.layered.css +8 -1
  100. package/dist/tabs.css +6 -0
  101. package/dist/tabs.layered.css +6 -0
  102. package/dist/text.css +1 -1
  103. package/dist/text.layered.css +1 -1
  104. package/dist/textarea.css +2 -1
  105. package/dist/textarea.layered.css +2 -1
  106. package/dist/textlink.css +11 -2
  107. package/dist/textlink.d.ts +2 -1
  108. package/dist/textlink.layered.css +11 -2
  109. package/dist/tokens.css +43 -11
  110. package/dist/tokens.json +82 -28
  111. package/dist/types/components/accentpicker/accentpicker.d.ts +23 -0
  112. package/dist/types/components/accentpicker/accentpicker.recipe.d.ts +3 -0
  113. package/dist/types/components/accentpicker/index.d.ts +2 -0
  114. package/dist/types/components/button/button.recipe.d.ts +2 -2
  115. package/dist/types/components/callout/callout.d.ts +9 -1
  116. package/dist/types/components/callout/callout.recipe.d.ts +12 -2
  117. package/dist/types/components/input/input.d.ts +14 -0
  118. package/dist/types/components/input/input.recipe.d.ts +3 -0
  119. package/dist/types/components/menu/menu.d.ts +7 -1
  120. package/dist/types/components/multiselect/multiselect.d.ts +25 -6
  121. package/dist/types/components/multiselect/multiselect.recipe.d.ts +27 -0
  122. package/dist/types/components/progress/progress.d.ts +16 -4
  123. package/dist/types/components/progress/progress.recipe.d.ts +22 -0
  124. package/dist/types/components/select/listbox-keyboard.d.ts +10 -1
  125. package/dist/types/components/select/select.d.ts +29 -6
  126. package/dist/types/components/select/select.recipe.d.ts +25 -10
  127. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +14 -0
  128. package/dist/types/components/tabs/tabs.d.ts +9 -1
  129. package/dist/types/components/textarea/textarea.d.ts +16 -0
  130. package/dist/types/components/textlink/textlink.d.ts +8 -1
  131. package/dist/types/components/textlink/textlink.recipe.d.ts +10 -0
  132. package/dist/types/index.d.ts +1 -0
  133. package/dist/types/product/recordpage/recordpage.d.ts +61 -1
  134. package/dist/types/tokens/contract.d.ts +16 -3
  135. package/dist/types/tokens/elevation.d.ts +7 -0
  136. package/dist/types/tokens/palette.d.ts +55 -11
  137. package/dist/types/tokens/scale.d.ts +6 -0
  138. package/dist/types/tokens/semantic.d.ts +33 -0
  139. package/package.json +6 -1
@@ -7,6 +7,17 @@
7
7
  & .crisp-input-value::placeholder { color: var(--crisp-fg-muted); }
8
8
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
9
9
  &:has(.crisp-input-value:disabled) { opacity: 1; }
10
+ /* crisp#634: measured live on Attio's own Settings > Profile read-only
11
+ primary-email field -- a tinted field, not just muted text, driven off
12
+ the native `readOnly` attribute. `ringField` (rgba(0,0,0,.06)) is an
13
+ exact match for the measured ring; the 2%-fgPrimary tint reuses the
14
+ same color-mix idiom every other soft crisp surface already uses. */
15
+ &[data-readonly] { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-ring-field); }
16
+ & .crisp-input-suffix { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); }
17
+ }
18
+ .crisp-input--size_sm {
19
+ height: var(--crisp-size-row); border-radius: var(--crisp-radius-md);
20
+ & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
10
21
  }
11
22
  .crisp-input--size_medium {
12
23
  height: 32px; border-radius: var(--crisp-radius-md);
package/dist/menu.css CHANGED
@@ -30,6 +30,14 @@
30
30
  .crisp-menu-item--danger svg { color: var(--crisp-bg-danger); }
31
31
  .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); }
32
32
  .crisp-menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
33
+ /* crisp#875: same measured neutral chip TableToolbar's own ToolbarTool
34
+ count prop uses (re-confirmed on two live Attio surfaces this round:
35
+ the Sort tool's own "+N" overflow, a Kanban board's stage-column
36
+ count) -- a small badge, not bare text. */
37
+ .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;
38
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
39
+ 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);
40
+ font-variant-numeric: tabular-nums; }
33
41
  .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; }
34
42
  .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
35
43
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -31,6 +31,14 @@
31
31
  .crisp-menu-item--danger svg { color: var(--crisp-bg-danger); }
32
32
  .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); }
33
33
  .crisp-menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
34
+ /* crisp#875: same measured neutral chip TableToolbar's own ToolbarTool
35
+ count prop uses (re-confirmed on two live Attio surfaces this round:
36
+ the Sort tool's own "+N" overflow, a Kanban board's stage-column
37
+ count) -- a small badge, not bare text. */
38
+ .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;
39
+ padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent);
40
+ 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);
41
+ font-variant-numeric: tabular-nums; }
34
42
  .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; }
35
43
  .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
36
44
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -1,36 +1,40 @@
1
1
 
2
- /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
3
- on document.body while open (Zag re-enables it only on layers IT
4
- tracks), and the portaled listbox is outside Zag's knowledge -- it
5
- inherited the lock, so every option was unclickable, hit-testing
6
- right through to whatever real element sat behind it. */
2
+ /* pointer-events: auto (#375) -- see Select's matching comment. */
7
3
  .crisp-multiselect-listbox {
8
4
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
9
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
10
- max-height: 300px; overflow-y: auto; padding: var(--crisp-space-1);
11
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
12
- 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);
13
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
5
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
6
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
7
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
8
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
9
+ transform-origin: top left; opacity: 1; transform: none;
10
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
14
11
  }
15
12
  .crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
16
- .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
17
- .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); }
13
+ .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
14
+ /* LOCKED geometry (#481, guarded by build/spec-check.mjs): height 36px,
15
+ gap space["1.5"] (6px), no border-bottom -- an Attio measurement, not
16
+ part of this seed-parity pass. Only colours below follow Select. */
17
+ .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); }
18
18
  .crisp-multiselect-search svg { flex-shrink: 0; }
19
- .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); }
20
- .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-muted); }
21
- /* #343: same measured values as Menu's own group heading
22
- (crisp-menu-label) -- reused rather than re-measured, since it's the
23
- same "small muted heading above a run of rows" treatment. */
24
- .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); }
19
+ .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); }
20
+ .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
21
+ /* Same groupLabel treatment as Select's (13px/regular/neutral-subtle). */
22
+ .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); }
25
23
  .crisp-multiselect-option {
26
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
27
- 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;
24
+ box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0;
25
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
26
+ font-family: inherit; text-align: start; cursor: pointer;
27
+ transition: background-color .15s cubic-bezier(.35,0,.35,1);
28
28
  }
29
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill, same
30
+ reasoning as Select's matching rule (seed's own pressed tint is too
31
+ faint for a keyboard-navigation indicator). */
29
32
  .crisp-multiselect-option[data-highlighted] { background: var(--crisp-bg-raised); }
33
+ .crisp-multiselect-option:active { background: var(--crisp-bg-select-pressed); }
30
34
  .crisp-multiselect-option-chip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
31
35
  .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; }
32
36
  .crisp-multiselect-option[aria-selected="true"] .crisp-multiselect-check { background: var(--crisp-bg-brand-solid); box-shadow: none; color: #fff; }
33
- .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); }
37
+ .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); }
34
38
 
35
39
  .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); }
36
40
  .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); }
@@ -43,21 +47,45 @@
43
47
 
44
48
  .crisp-multiselect {
45
49
  position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
46
- & .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; }
47
- & .crisp-multiselect-trigger:hover { background: var(--crisp-bg-raised); }
48
- & .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); }
49
- & .crisp-multiselect-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
50
+ & .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); }
51
+ /* Same pressed/disabled/readonly/focus model as Select's trigger -- see
52
+ that recipe's comment for why hover and an open/focus ring are gone. */
53
+ & .crisp-multiselect-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
54
+ & .crisp-multiselect-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
55
+ & .crisp-multiselect-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
56
+ & .crisp-multiselect-trigger:disabled .crisp-multiselect-placeholder, & .crisp-multiselect-trigger:disabled .crisp-multiselect-chevron { color: var(--crisp-fg-disabled); }
57
+ & .crisp-multiselect-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
50
58
  & .crisp-multiselect-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); }
51
- & .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
52
- & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
53
- & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); }
59
+ & .crisp-multiselect-placeholder { color: var(--crisp-fg-select-placeholder); }
60
+ /* closeRotateDuration/openRotateDuration, same asymmetric timing as Select's own chevron (see that recipe's comment). */
61
+ & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
62
+ & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); transition-duration: .15s; }
63
+ }
64
+ .crisp-multiselect--size_small {
65
+ & .crisp-multiselect-trigger { min-height: var(--crisp-size-menuRow); border-radius: var(--crisp-radius-md); gap: var(--crisp-space-1); padding: var(--crisp-space-0-5) var(--crisp-space-1-5) var(--crisp-space-0-5) var(--crisp-space-2); }
66
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
67
+ }
68
+ .crisp-multiselect--size_medium {
69
+ & .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); }
70
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
71
+ }
72
+ .crisp-multiselect--size_large {
73
+ & .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); }
74
+ & .crisp-multiselect-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
54
75
  }
55
76
  .crisp-multiselect--invalid_true {
56
- & .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); }
77
+ & .crisp-multiselect-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
57
78
  }
58
79
  .crisp-multiselect--invalid_false {
59
80
 
60
81
  }
82
+ .crisp-multiselect--variant_default {
83
+
84
+ }
85
+ .crisp-multiselect--variant_ghost {
86
+ & .crisp-multiselect-trigger { min-height: 0; height: 100%; border-color: transparent; }
87
+ & .crisp-multiselect-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
88
+ }
61
89
 
62
90
  @media (prefers-reduced-motion: reduce) {
63
91
  .crisp-multiselect, .crisp-multiselect * {
@@ -1,7 +1,9 @@
1
1
  import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface MultiselectVariants {
4
+ size?: "small" | "medium" | "large";
4
5
  invalid?: boolean;
6
+ variant?: "default" | "ghost";
5
7
  }
6
8
 
7
- export declare const multiselectRecipe: RecipeFn<{ invalid: { true: string; false: string } }>;
9
+ export declare const multiselectRecipe: RecipeFn<{ size: { small: string; medium: string; large: string }; invalid: { true: string; false: string }; variant: { default: string; ghost: string } }>;
@@ -1,37 +1,41 @@
1
1
  @layer crisp-components {
2
2
 
3
- /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
4
- on document.body while open (Zag re-enables it only on layers IT
5
- tracks), and the portaled listbox is outside Zag's knowledge -- it
6
- inherited the lock, so every option was unclickable, hit-testing
7
- right through to whatever real element sat behind it. */
3
+ /* pointer-events: auto (#375) -- see Select's matching comment. */
8
4
  .crisp-multiselect-listbox {
9
5
  font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
10
- position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
11
- max-height: 300px; overflow-y: auto; padding: var(--crisp-space-1);
12
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
13
- 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);
14
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
6
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-2);
7
+ max-height: 480px; overflow-y: auto; padding: var(--crisp-space-2);
8
+ border-radius: var(--crisp-radius-3xl); background: var(--crisp-bg-select-listbox);
9
+ box-shadow: 0 4px 16px var(--crisp-shadow-select);
10
+ transform-origin: top left; opacity: 1; transform: none;
11
+ transition: opacity .15s cubic-bezier(0,0,.15,1), transform .15s cubic-bezier(0,0,.15,1);
15
12
  }
16
13
  .crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
17
- .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
18
- .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); }
14
+ .crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
15
+ /* LOCKED geometry (#481, guarded by build/spec-check.mjs): height 36px,
16
+ gap space["1.5"] (6px), no border-bottom -- an Attio measurement, not
17
+ part of this seed-parity pass. Only colours below follow Select. */
18
+ .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); }
19
19
  .crisp-multiselect-search svg { flex-shrink: 0; }
20
- .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); }
21
- .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-muted); }
22
- /* #343: same measured values as Menu's own group heading
23
- (crisp-menu-label) -- reused rather than re-measured, since it's the
24
- same "small muted heading above a run of rows" treatment. */
25
- .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); }
20
+ .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); }
21
+ .crisp-multiselect-search input::placeholder { color: var(--crisp-fg-select-placeholder); }
22
+ /* Same groupLabel treatment as Select's (13px/regular/neutral-subtle). */
23
+ .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); }
26
24
  .crisp-multiselect-option {
27
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); flex-shrink: 0;
28
- 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;
25
+ box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0;
26
+ padding: var(--crisp-space-2-5) var(--crisp-space-4); margin: 0; border: none; background: none; border-radius: var(--crisp-radius-xl);
27
+ font-family: inherit; text-align: start; cursor: pointer;
28
+ transition: background-color .15s cubic-bezier(.35,0,.35,1);
29
29
  }
30
+ /* Keyboard/hover highlight keeps crisp's own visible neutral fill, same
31
+ reasoning as Select's matching rule (seed's own pressed tint is too
32
+ faint for a keyboard-navigation indicator). */
30
33
  .crisp-multiselect-option[data-highlighted] { background: var(--crisp-bg-raised); }
34
+ .crisp-multiselect-option:active { background: var(--crisp-bg-select-pressed); }
31
35
  .crisp-multiselect-option-chip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
32
36
  .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; }
33
37
  .crisp-multiselect-option[aria-selected="true"] .crisp-multiselect-check { background: var(--crisp-bg-brand-solid); box-shadow: none; color: #fff; }
34
- .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); }
38
+ .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); }
35
39
 
36
40
  .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); }
37
41
  .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); }
@@ -44,21 +48,45 @@
44
48
 
45
49
  .crisp-multiselect {
46
50
  position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
47
- & .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; }
48
- & .crisp-multiselect-trigger:hover { background: var(--crisp-bg-raised); }
49
- & .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); }
50
- & .crisp-multiselect-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
51
+ & .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); }
52
+ /* Same pressed/disabled/readonly/focus model as Select's trigger -- see
53
+ that recipe's comment for why hover and an open/focus ring are gone. */
54
+ & .crisp-multiselect-trigger:active:not(:disabled):not([aria-readonly="true"]) { background: var(--crisp-bg-select-pressed); }
55
+ & .crisp-multiselect-trigger:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: -2px; }
56
+ & .crisp-multiselect-trigger:disabled { background: var(--crisp-bg-select-disabled); cursor: not-allowed; }
57
+ & .crisp-multiselect-trigger:disabled .crisp-multiselect-placeholder, & .crisp-multiselect-trigger:disabled .crisp-multiselect-chevron { color: var(--crisp-fg-disabled); }
58
+ & .crisp-multiselect-trigger[aria-readonly="true"] { background: var(--crisp-bg-select-disabled); cursor: default; }
51
59
  & .crisp-multiselect-chips { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); }
52
- & .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
53
- & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); transition: transform .15s ease; }
54
- & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); }
60
+ & .crisp-multiselect-placeholder { color: var(--crisp-fg-select-placeholder); }
61
+ /* closeRotateDuration/openRotateDuration, same asymmetric timing as Select's own chevron (see that recipe's comment). */
62
+ & .crisp-multiselect-chevron { flex-shrink: 0; color: var(--crisp-fg-neutral-muted); transition: transform .1s cubic-bezier(.35,0,.35,1); }
63
+ & .crisp-multiselect-trigger[data-open] .crisp-multiselect-chevron { transform: rotate(180deg); transition-duration: .15s; }
64
+ }
65
+ .crisp-multiselect--size_small {
66
+ & .crisp-multiselect-trigger { min-height: var(--crisp-size-menuRow); border-radius: var(--crisp-radius-md); gap: var(--crisp-space-1); padding: var(--crisp-space-0-5) var(--crisp-space-1-5) var(--crisp-space-0-5) var(--crisp-space-2); }
67
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
68
+ }
69
+ .crisp-multiselect--size_medium {
70
+ & .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); }
71
+ & .crisp-multiselect-chevron { width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
72
+ }
73
+ .crisp-multiselect--size_large {
74
+ & .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); }
75
+ & .crisp-multiselect-chevron { width: var(--crisp-size-selectIconLg); height: var(--crisp-size-selectIconLg); }
55
76
  }
56
77
  .crisp-multiselect--invalid_true {
57
- & .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); }
78
+ & .crisp-multiselect-trigger { border-width: 2px; border-color: var(--crisp-stroke-select-invalid); }
58
79
  }
59
80
  .crisp-multiselect--invalid_false {
60
81
 
61
82
  }
83
+ .crisp-multiselect--variant_default {
84
+
85
+ }
86
+ .crisp-multiselect--variant_ghost {
87
+ & .crisp-multiselect-trigger { min-height: 0; height: 100%; border-color: transparent; }
88
+ & .crisp-multiselect-trigger:hover:not(:disabled) { background: var(--crisp-bg-select-pressed); }
89
+ }
62
90
 
63
91
  @media (prefers-reduced-motion: reduce) {
64
92
  .crisp-multiselect, .crisp-multiselect * {
@@ -2,7 +2,7 @@
2
2
  // The same tokens the CSS custom properties carry, resolved per theme, with
3
3
  // dimensions as numbers for React Native and Lynx style objects.
4
4
  export type Theme = "light" | "dark";
5
- export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "bgSurfaceMuted" | "borderDefault" | "borderSubtle" | "borderDropzone" | "borderDivider" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgScrim" | "fgAlertDescription" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "fgDanger" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft" | "bgPurpleSoft" | "fgPurpleSoft" | "ringPurpleSoft", string>;
5
+ export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "bgBrandContrast" | "fgBrandContrast" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "bgSurfaceMuted" | "borderDefault" | "borderSubtle" | "borderDropzone" | "borderDivider" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgScrim" | "fgAlertDescription" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "fgDanger" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgWarningSolid" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft" | "bgPurpleSoft" | "fgPurpleSoft" | "ringPurpleSoft" | "ringBrandSoft" | "fgSelectNeutral" | "bgSelectListbox" | "strokeSelectTrigger" | "bgSelectPressed" | "bgSelectDisabled" | "strokeSelectInvalid" | "strokeSelectDivider" | "fgSelectPlaceholder" | "shadowSelect", string>;
6
6
  export declare const tokens: {
7
7
  color: Record<Theme, ColorTokens>;
8
8
  palette: Record<Theme, Record<string, string>>;
@@ -4,14 +4,16 @@
4
4
  export const tokens = {
5
5
  "color": {
6
6
  "light": {
7
- "bgBrandSolid": "#266df0",
8
- "bgBrandSolidPressed": "#1f5fd6",
7
+ "bgBrandSolid": "#0298d9",
8
+ "bgBrandSolidPressed": "#0283bb",
9
+ "bgBrandContrast": "#0278ac",
10
+ "fgBrandContrast": "#0278ac",
9
11
  "fgOnBrand": "#ffffff",
10
12
  "fgOnTone": "#101112",
11
13
  "bgSurface": "#ffffff",
12
- "bgRaised": "#f4f4f5",
14
+ "bgRaised": "#f7f8f9",
13
15
  "bgSurfaceMuted": "#f6f7f7",
14
- "borderDefault": "#e4e4e7",
16
+ "borderDefault": "#dcdee3",
15
17
  "borderSubtle": "#eeeff1",
16
18
  "borderDropzone": "#e6e7ea",
17
19
  "borderDivider": "#e6e7ea",
@@ -33,9 +35,9 @@ export const tokens = {
33
35
  "shadowPopover3": "rgba(24,41,75,0.16)",
34
36
  "fgPrimary": "#101112",
35
37
  "fgMuted": "#71717a",
36
- "bgDanger": "#e5484d",
37
- "bgDangerSolid": "#ba2525",
38
- "fgDanger": "#eb3b3b",
38
+ "bgDanger": "#fa342c",
39
+ "bgDangerSolid": "#ca1d13",
40
+ "fgDanger": "#fa342c",
39
41
  "strokeFieldFocus": "#1a1c20",
40
42
  "fgDisabled": "#d1d3d8",
41
43
  "bgNeutralWeak": "#f3f4f5",
@@ -44,8 +46,9 @@ export const tokens = {
44
46
  "fgNeutralSubtle": "#868b94",
45
47
  "fgNeutralMuted": "#555d6d",
46
48
  "strokeFocusRing": "#1e82eb",
47
- "bgSuccess": "#1f9d57",
48
- "bgWarning": "#b5730f",
49
+ "bgSuccess": "#079171",
50
+ "bgWarning": "#9b7821",
51
+ "bgWarningSolid": "#fbdc65",
49
52
  "bgOrange": "#f97514",
50
53
  "bgCyan": "#00b9eb",
51
54
  "bgPurple": "#9b69ff",
@@ -58,11 +61,23 @@ export const tokens = {
58
61
  "fgNeutralSoft": "#505155",
59
62
  "bgPurpleSoft": "#f5f0ff",
60
63
  "fgPurpleSoft": "#6238b5",
61
- "ringPurpleSoft": "#e8ddfe"
64
+ "ringPurpleSoft": "#e8ddfe",
65
+ "ringBrandSoft": "#d6e5ff",
66
+ "fgSelectNeutral": "#1a1c20",
67
+ "bgSelectListbox": "#ffffff",
68
+ "strokeSelectTrigger": "#dcdee3",
69
+ "bgSelectPressed": "#00000007",
70
+ "bgSelectDisabled": "#f3f4f5",
71
+ "strokeSelectInvalid": "#fa342c",
72
+ "strokeSelectDivider": "#00000010",
73
+ "fgSelectPlaceholder": "#b0b3ba",
74
+ "shadowSelect": "#0000001f"
62
75
  },
63
76
  "dark": {
64
- "bgBrandSolid": "#266df0",
65
- "bgBrandSolidPressed": "#1f5fd6",
77
+ "bgBrandSolid": "#0298d9",
78
+ "bgBrandSolidPressed": "#0283bb",
79
+ "bgBrandContrast": "#0278ac",
80
+ "fgBrandContrast": "#0278ac",
66
81
  "fgOnBrand": "#ffffff",
67
82
  "fgOnTone": "#101112",
68
83
  "bgSurface": "#0a0a0a",
@@ -103,6 +118,7 @@ export const tokens = {
103
118
  "strokeFocusRing": "#5e98fe",
104
119
  "bgSuccess": "#2ea968",
105
120
  "bgWarning": "#e0982a",
121
+ "bgWarningSolid": "#e0982a",
106
122
  "bgOrange": "#f97514",
107
123
  "bgCyan": "#00b9eb",
108
124
  "bgPurple": "#9b69ff",
@@ -115,7 +131,17 @@ export const tokens = {
115
131
  "fgNeutralSoft": "#9a9aa2",
116
132
  "bgPurpleSoft": "#2f1e5a",
117
133
  "fgPurpleSoft": "#d8c4ff",
118
- "ringPurpleSoft": "#45297d"
134
+ "ringPurpleSoft": "#45297d",
135
+ "ringBrandSoft": "#212224",
136
+ "fgSelectNeutral": "#f3f4f5",
137
+ "bgSelectListbox": "#1d2025",
138
+ "strokeSelectTrigger": "#393d46",
139
+ "bgSelectPressed": "#ffffff0d",
140
+ "bgSelectDisabled": "#2b2e35",
141
+ "strokeSelectInvalid": "#ff6e60",
142
+ "strokeSelectDivider": "#ffffff17",
143
+ "fgSelectPlaceholder": "#868b94",
144
+ "shadowSelect": "#000000cc"
119
145
  }
120
146
  },
121
147
  "palette": {
@@ -123,15 +149,16 @@ export const tokens = {
123
149
  "gray00": "#ffffff",
124
150
  "white": "#ffffff",
125
151
  "ink": "#101112",
126
- "gray50": "#f4f4f5",
152
+ "gray50": "#f7f8f9",
127
153
  "gray900": "#101112",
128
- "line200": "#e4e4e7",
154
+ "line200": "#dcdee3",
129
155
  "line100": "#eeeff1",
130
156
  "line150": "#e6e7ea",
131
157
  "gray25": "#f6f7f7",
132
158
  "ringNeutral": "transparent",
133
159
  "ringField": "rgba(0,0,0,0.06)",
134
160
  "ringSwitch": "#d9dade",
161
+ "ringBrandSoft": "#d6e5ff",
135
162
  "tileActive": "#ffffff",
136
163
  "tileActiveShadow": "rgba(0,0,0,0.04)",
137
164
  "bgScrim": "rgba(0,0,0,0.25)",
@@ -146,11 +173,12 @@ export const tokens = {
146
173
  "shadowPopover2": "rgba(28,40,64,0.12)",
147
174
  "shadowPopover3": "rgba(24,41,75,0.16)",
148
175
  "gray400": "#71717a",
149
- "brand600": "#266df0",
150
- "brand700": "#1f5fd6",
151
- "red600": "#e5484d",
152
- "red700": "#ba2525",
153
- "red500": "#eb3b3b",
176
+ "brand600": "#0298d9",
177
+ "brand700": "#0283bb",
178
+ "brandContrast": "#0278ac",
179
+ "red600": "#fa342c",
180
+ "red700": "#ca1d13",
181
+ "red500": "#fa342c",
154
182
  "fieldFocus": "#1a1c20",
155
183
  "disabledFg": "#d1d3d8",
156
184
  "neutralWeak": "#f3f4f5",
@@ -159,8 +187,9 @@ export const tokens = {
159
187
  "neutralSubtle": "#868b94",
160
188
  "neutralMuted": "#555d6d",
161
189
  "focusRing": "#1e82eb",
162
- "green600": "#1f9d57",
163
- "amber600": "#b5730f",
190
+ "green600": "#079171",
191
+ "amber600": "#9b7821",
192
+ "amberSolid": "#fbdc65",
164
193
  "orange600": "#f97514",
165
194
  "cyan600": "#00b9eb",
166
195
  "purple600": "#9b69ff",
@@ -172,7 +201,15 @@ export const tokens = {
172
201
  "softNeutralText": "#505155",
173
202
  "purpleTint": "#f5f0ff",
174
203
  "softPurpleText": "#6238b5",
175
- "purpleRing": "#e8ddfe"
204
+ "purpleRing": "#e8ddfe",
205
+ "selectListboxBg": "#ffffff",
206
+ "selectTriggerStroke": "#dcdee3",
207
+ "selectPressedTint": "#00000007",
208
+ "selectDisabledBg": "#f3f4f5",
209
+ "selectInvalidStroke": "#fa342c",
210
+ "selectDivider": "#00000010",
211
+ "selectPlaceholder": "#b0b3ba",
212
+ "selectShadow": "#0000001f"
176
213
  },
177
214
  "dark": {
178
215
  "gray00": "#0a0a0a",
@@ -187,6 +224,7 @@ export const tokens = {
187
224
  "ringNeutral": "#212224",
188
225
  "ringField": "#212224",
189
226
  "ringSwitch": "#212224",
227
+ "ringBrandSoft": "#212224",
190
228
  "tileActive": "#101112",
191
229
  "tileActiveShadow": "rgba(0,0,0,0)",
192
230
  "bgScrim": "rgba(0,0,0,0.55)",
@@ -201,8 +239,9 @@ export const tokens = {
201
239
  "shadowPopover2": "rgba(0,0,0,0.24)",
202
240
  "shadowPopover3": "rgba(0,0,0,0.32)",
203
241
  "gray400": "#9a9aa2",
204
- "brand600": "#266df0",
205
- "brand700": "#1f5fd6",
242
+ "brand600": "#0298d9",
243
+ "brand700": "#0283bb",
244
+ "brandContrast": "#0278ac",
206
245
  "red600": "#ff5a5f",
207
246
  "red700": "#ba2525",
208
247
  "red500": "#ff5a5f",
@@ -216,6 +255,7 @@ export const tokens = {
216
255
  "focusRing": "#5e98fe",
217
256
  "green600": "#2ea968",
218
257
  "amber600": "#e0982a",
258
+ "amberSolid": "#e0982a",
219
259
  "orange600": "#f97514",
220
260
  "cyan600": "#00b9eb",
221
261
  "purple600": "#9b69ff",
@@ -227,7 +267,15 @@ export const tokens = {
227
267
  "softNeutralText": "#9a9aa2",
228
268
  "purpleTint": "#2f1e5a",
229
269
  "softPurpleText": "#d8c4ff",
230
- "purpleRing": "#45297d"
270
+ "purpleRing": "#45297d",
271
+ "selectListboxBg": "#1d2025",
272
+ "selectTriggerStroke": "#393d46",
273
+ "selectPressedTint": "#ffffff0d",
274
+ "selectDisabledBg": "#2b2e35",
275
+ "selectInvalidStroke": "#ff6e60",
276
+ "selectDivider": "#ffffff17",
277
+ "selectPlaceholder": "#868b94",
278
+ "selectShadow": "#000000cc"
231
279
  }
232
280
  },
233
281
  "space": {
@@ -241,15 +289,18 @@ export const tokens = {
241
289
  "px": 1,
242
290
  "0.5": 2,
243
291
  "1.5": 6,
244
- "2.5": 10
292
+ "2.5": 10,
293
+ "3.5": 14
245
294
  },
246
295
  "radius": {
296
+ "xs": 4,
247
297
  "chip": 7,
248
298
  "sm": 6,
249
299
  "md": 8,
250
300
  "lg": 9,
251
301
  "xl": 12,
252
302
  "2xl": 16,
303
+ "3xl": 20,
253
304
  "full": 999
254
305
  },
255
306
  "size": {
@@ -261,7 +312,10 @@ export const tokens = {
261
312
  "tableHeader": 40,
262
313
  "header": 48,
263
314
  "calendarCell": 34,
264
- "calendarPill": 32
315
+ "calendarPill": 32,
316
+ "selectTriggerMd": 40,
317
+ "selectTriggerLg": 52,
318
+ "selectIconLg": 20
265
319
  },
266
320
  "text": {
267
321
  "xs": 11,
package/dist/progress.css CHANGED
@@ -4,6 +4,9 @@
4
4
  &[data-segmented] { display: flex; gap: var(--crisp-space-1); background: transparent; overflow: visible; }
5
5
  &[data-segmented] .crisp-progress-seg { flex: 1; height: 100%; border-radius: var(--crisp-radius-full); background: color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); transition: background 0.2s; }
6
6
  &[data-segmented] .crisp-progress-seg[data-filled] { background: var(--crisp-bg-brand-solid); }
7
+ &[data-variant="circular"] { display: inline-flex; height: auto; border-radius: 0; background: transparent; overflow: visible; }
8
+ & .crisp-progress-circle-track { stroke: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); }
9
+ & .crisp-progress-circle-fill { stroke: var(--crisp-bg-brand-solid); transition: stroke-dashoffset 0.25s ease; }
7
10
  }
8
11
 
9
12
  @media (prefers-reduced-motion: reduce) {
@@ -5,6 +5,9 @@
5
5
  &[data-segmented] { display: flex; gap: var(--crisp-space-1); background: transparent; overflow: visible; }
6
6
  &[data-segmented] .crisp-progress-seg { flex: 1; height: 100%; border-radius: var(--crisp-radius-full); background: color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); transition: background 0.2s; }
7
7
  &[data-segmented] .crisp-progress-seg[data-filled] { background: var(--crisp-bg-brand-solid); }
8
+ &[data-variant="circular"] { display: inline-flex; height: auto; border-radius: 0; background: transparent; overflow: visible; }
9
+ & .crisp-progress-circle-track { stroke: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); }
10
+ & .crisp-progress-circle-fill { stroke: var(--crisp-bg-brand-solid); transition: stroke-dashoffset 0.25s ease; }
8
11
  }
9
12
 
10
13
  @media (prefers-reduced-motion: reduce) {