@crisp-ui-kit/crisp 0.57.1 → 0.57.3

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 (277) hide show
  1. package/dist/authshell.css +9 -4
  2. package/dist/authshell.layered.css +9 -4
  3. package/dist/badge.css +3 -3
  4. package/dist/badge.layered.css +3 -3
  5. package/dist/board.css +28 -8
  6. package/dist/board.layered.css +28 -8
  7. package/dist/bottomsheet.css +26 -0
  8. package/dist/bottomsheet.d.ts +7 -0
  9. package/dist/bottomsheet.layered.css +28 -0
  10. package/dist/button.css +10 -6
  11. package/dist/button.d.ts +2 -2
  12. package/dist/button.layered.css +10 -6
  13. package/dist/checkbox.css +7 -3
  14. package/dist/checkbox.layered.css +7 -3
  15. package/dist/chip.css +16 -1
  16. package/dist/chip.d.ts +3 -1
  17. package/dist/chip.layered.css +16 -1
  18. package/dist/chipgroup.css +5 -0
  19. package/dist/chipgroup.d.ts +7 -0
  20. package/dist/chipgroup.layered.css +7 -0
  21. package/dist/cjs/components/badge/badge.recipe.js +22 -8
  22. package/dist/cjs/components/bottomsheet/bottomsheet.js +90 -0
  23. package/dist/cjs/components/bottomsheet/bottomsheet.recipe.js +77 -0
  24. package/dist/cjs/components/bottomsheet/index.js +9 -0
  25. package/dist/cjs/components/breadcrumb/breadcrumb.js +1 -1
  26. package/dist/cjs/components/button/button.recipe.js +67 -15
  27. package/dist/cjs/components/checkbox/checkbox.recipe.js +61 -9
  28. package/dist/cjs/components/chip/chip.js +14 -2
  29. package/dist/cjs/components/chip/chip.recipe.js +72 -2
  30. package/dist/cjs/components/chipgroup/chipgroup.js +51 -0
  31. package/dist/cjs/components/chipgroup/chipgroup.recipe.js +36 -0
  32. package/dist/cjs/components/chipgroup/index.js +7 -0
  33. package/dist/cjs/components/datatable/datatable.js +5 -1
  34. package/dist/cjs/components/datatable/datatable.recipe.js +21 -6
  35. package/dist/cjs/components/datepickerfield/datepickerfield.js +2 -2
  36. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +16 -3
  37. package/dist/cjs/components/dialog/dialog.recipe.js +21 -10
  38. package/dist/cjs/components/emptystate/emptystate.recipe.js +7 -2
  39. package/dist/cjs/components/fileupload/fileupload.js +21 -4
  40. package/dist/cjs/components/input/index.js +2 -1
  41. package/dist/cjs/components/input/input.js +31 -1
  42. package/dist/cjs/components/input/input.recipe.js +16 -9
  43. package/dist/cjs/components/menu/menu.js +18 -2
  44. package/dist/cjs/components/menu/menu.recipe.js +21 -7
  45. package/dist/cjs/components/menusheet/index.js +9 -0
  46. package/dist/cjs/components/menusheet/menusheet.js +133 -0
  47. package/dist/cjs/components/menusheet/menusheet.recipe.js +103 -0
  48. package/dist/cjs/components/multiselect/multiselect.js +1 -1
  49. package/dist/cjs/components/multiselect/multiselect.recipe.js +33 -9
  50. package/dist/cjs/components/select/select.js +1 -1
  51. package/dist/cjs/components/select/select.recipe.js +38 -12
  52. package/dist/cjs/components/snackbar/index.js +8 -0
  53. package/dist/cjs/components/snackbar/snackbar.js +121 -0
  54. package/dist/cjs/components/snackbar/snackbar.recipe.js +71 -0
  55. package/dist/cjs/components/table/table.recipe.js +7 -1
  56. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  57. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +37 -12
  58. package/dist/cjs/components/tabs/tabs.js +10 -4
  59. package/dist/cjs/components/tabs/tabs.recipe.js +23 -6
  60. package/dist/cjs/components/textarea/textarea.recipe.js +11 -8
  61. package/dist/cjs/core/floating-portal.js +7 -4
  62. package/dist/cjs/index.js +7 -0
  63. package/dist/cjs/product/authshell/authshell.recipe.js +5 -1
  64. package/dist/cjs/product/command/command.recipe.js +4 -1
  65. package/dist/cjs/product/comments/comments.js +7 -2
  66. package/dist/cjs/product/comments/comments.recipe.js +25 -7
  67. package/dist/cjs/product/inbox/inbox.js +11 -3
  68. package/dist/cjs/product/memberssettings/index.js +6 -0
  69. package/dist/cjs/product/memberssettings/memberssettings.js +99 -0
  70. package/dist/cjs/product/memberssettings/memberssettings.recipe.js +83 -0
  71. package/dist/cjs/product/notificationssettings/index.js +5 -0
  72. package/dist/cjs/product/notificationssettings/notificationssettings.js +19 -0
  73. package/dist/cjs/product/notificationssettings/notificationssettings.recipe.js +44 -0
  74. package/dist/cjs/product/recordpage/index.js +2 -1
  75. package/dist/cjs/product/recordpage/recordpage.js +106 -9
  76. package/dist/cjs/product/recordpage/recordpage.recipe.js +68 -6
  77. package/dist/cjs/product/workspacegeneralsettings/index.js +5 -0
  78. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.js +21 -0
  79. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +51 -0
  80. package/dist/cjs/tokens/contract.js +1 -0
  81. package/dist/cjs/tokens/elevation.js +8 -5
  82. package/dist/cjs/tokens/palette.js +28 -8
  83. package/dist/cjs/tokens/scale.js +44 -2
  84. package/dist/cjs/tokens/semantic.js +2 -0
  85. package/dist/columncalc.css +15 -7
  86. package/dist/columncalc.layered.css +15 -7
  87. package/dist/command.css +23 -19
  88. package/dist/command.layered.css +23 -19
  89. package/dist/comments.css +28 -19
  90. package/dist/comments.d.ts +2 -2
  91. package/dist/comments.layered.css +28 -19
  92. package/dist/datatable.css +171 -71
  93. package/dist/datatable.layered.css +171 -71
  94. package/dist/datepicker.css +13 -41
  95. package/dist/datepicker.layered.css +13 -41
  96. package/dist/datepickerfield.css +12 -3
  97. package/dist/datepickerfield.d.ts +2 -2
  98. package/dist/datepickerfield.layered.css +12 -3
  99. package/dist/dialog.css +12 -7
  100. package/dist/dialog.layered.css +12 -7
  101. package/dist/emptystate.css +5 -3
  102. package/dist/emptystate.layered.css +5 -3
  103. package/dist/esm/components/badge/badge.recipe.js +22 -8
  104. package/dist/esm/components/bottomsheet/bottomsheet.js +52 -0
  105. package/dist/esm/components/bottomsheet/bottomsheet.recipe.js +74 -0
  106. package/dist/esm/components/bottomsheet/index.js +2 -0
  107. package/dist/esm/components/breadcrumb/breadcrumb.js +1 -1
  108. package/dist/esm/components/button/button.recipe.js +68 -16
  109. package/dist/esm/components/checkbox/checkbox.recipe.js +61 -9
  110. package/dist/esm/components/chip/chip.js +14 -2
  111. package/dist/esm/components/chip/chip.recipe.js +72 -2
  112. package/dist/esm/components/chipgroup/chipgroup.js +15 -0
  113. package/dist/esm/components/chipgroup/chipgroup.recipe.js +33 -0
  114. package/dist/esm/components/chipgroup/index.js +2 -0
  115. package/dist/esm/components/datatable/datatable.js +5 -1
  116. package/dist/esm/components/datatable/datatable.recipe.js +21 -6
  117. package/dist/esm/components/datepickerfield/datepickerfield.js +2 -2
  118. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +16 -3
  119. package/dist/esm/components/dialog/dialog.recipe.js +22 -11
  120. package/dist/esm/components/emptystate/emptystate.recipe.js +7 -2
  121. package/dist/esm/components/fileupload/fileupload.js +20 -5
  122. package/dist/esm/components/input/index.js +1 -1
  123. package/dist/esm/components/input/input.js +30 -0
  124. package/dist/esm/components/input/input.recipe.js +17 -10
  125. package/dist/esm/components/menu/menu.js +18 -2
  126. package/dist/esm/components/menu/menu.recipe.js +21 -7
  127. package/dist/esm/components/menusheet/index.js +2 -0
  128. package/dist/esm/components/menusheet/menusheet.js +95 -0
  129. package/dist/esm/components/menusheet/menusheet.recipe.js +100 -0
  130. package/dist/esm/components/multiselect/multiselect.js +1 -1
  131. package/dist/esm/components/multiselect/multiselect.recipe.js +33 -9
  132. package/dist/esm/components/select/select.js +1 -1
  133. package/dist/esm/components/select/select.recipe.js +38 -12
  134. package/dist/esm/components/snackbar/index.js +2 -0
  135. package/dist/esm/components/snackbar/snackbar.js +84 -0
  136. package/dist/esm/components/snackbar/snackbar.recipe.js +68 -0
  137. package/dist/esm/components/table/table.recipe.js +7 -1
  138. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  139. package/dist/esm/components/tabletoolbar/tabletoolbar.js +37 -12
  140. package/dist/esm/components/tabs/tabs.js +10 -4
  141. package/dist/esm/components/tabs/tabs.recipe.js +23 -6
  142. package/dist/esm/components/textarea/textarea.recipe.js +12 -9
  143. package/dist/esm/core/floating-portal.js +7 -4
  144. package/dist/esm/index.js +7 -0
  145. package/dist/esm/product/authshell/authshell.recipe.js +5 -1
  146. package/dist/esm/product/command/command.recipe.js +4 -1
  147. package/dist/esm/product/comments/comments.js +7 -2
  148. package/dist/esm/product/comments/comments.recipe.js +25 -7
  149. package/dist/esm/product/inbox/inbox.js +11 -3
  150. package/dist/esm/product/memberssettings/index.js +1 -0
  151. package/dist/esm/product/memberssettings/memberssettings.js +62 -0
  152. package/dist/esm/product/memberssettings/memberssettings.recipe.js +80 -0
  153. package/dist/esm/product/notificationssettings/index.js +1 -0
  154. package/dist/esm/product/notificationssettings/notificationssettings.js +16 -0
  155. package/dist/esm/product/notificationssettings/notificationssettings.recipe.js +41 -0
  156. package/dist/esm/product/recordpage/index.js +1 -1
  157. package/dist/esm/product/recordpage/recordpage.js +106 -10
  158. package/dist/esm/product/recordpage/recordpage.recipe.js +68 -6
  159. package/dist/esm/product/workspacegeneralsettings/index.js +1 -0
  160. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.js +18 -0
  161. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +48 -0
  162. package/dist/esm/tokens/contract.js +2 -1
  163. package/dist/esm/tokens/elevation.js +8 -5
  164. package/dist/esm/tokens/palette.js +28 -8
  165. package/dist/esm/tokens/scale.js +43 -1
  166. package/dist/esm/tokens/semantic.js +2 -0
  167. package/dist/fileupload.css +3 -1
  168. package/dist/fileupload.layered.css +3 -1
  169. package/dist/input.css +6 -4
  170. package/dist/input.layered.css +6 -4
  171. package/dist/listpage.css +3 -1
  172. package/dist/listpage.layered.css +3 -1
  173. package/dist/memberssettings.css +67 -0
  174. package/dist/memberssettings.d.ts +7 -0
  175. package/dist/memberssettings.layered.css +69 -0
  176. package/dist/menu.css +2 -2
  177. package/dist/menu.layered.css +2 -2
  178. package/dist/menusheet.css +36 -0
  179. package/dist/menusheet.d.ts +7 -0
  180. package/dist/menusheet.layered.css +38 -0
  181. package/dist/multiselect.css +13 -6
  182. package/dist/multiselect.layered.css +13 -6
  183. package/dist/native/tokens.d.ts +2 -1
  184. package/dist/native/tokens.js +20 -3
  185. package/dist/notificationssettings.css +22 -0
  186. package/dist/notificationssettings.d.ts +7 -0
  187. package/dist/notificationssettings.layered.css +24 -0
  188. package/dist/objectsettings.css +4 -4
  189. package/dist/objectsettings.layered.css +4 -4
  190. package/dist/recordpage.css +90 -23
  191. package/dist/recordpage.layered.css +90 -23
  192. package/dist/recordpanel.css +35 -16
  193. package/dist/recordpanel.layered.css +35 -16
  194. package/dist/resizablepanel.css +8 -5
  195. package/dist/resizablepanel.layered.css +8 -5
  196. package/dist/richtext.css +24 -8
  197. package/dist/richtext.layered.css +24 -8
  198. package/dist/savedviews.css +9 -2
  199. package/dist/savedviews.layered.css +9 -2
  200. package/dist/select.css +25 -5
  201. package/dist/select.layered.css +25 -5
  202. package/dist/settingsshell.css +2 -3
  203. package/dist/settingsshell.layered.css +2 -3
  204. package/dist/slider.css +6 -27
  205. package/dist/slider.layered.css +6 -27
  206. package/dist/snackbar.css +24 -0
  207. package/dist/snackbar.d.ts +7 -0
  208. package/dist/snackbar.layered.css +26 -0
  209. package/dist/statcard.css +3 -1
  210. package/dist/statcard.layered.css +3 -1
  211. package/dist/styles.css +911 -354
  212. package/dist/styles.layered.css +911 -354
  213. package/dist/table.css +35 -12
  214. package/dist/table.layered.css +35 -12
  215. package/dist/tabletoolbar.css +35 -24
  216. package/dist/tabletoolbar.layered.css +35 -24
  217. package/dist/tabs.css +18 -4
  218. package/dist/tabs.layered.css +18 -4
  219. package/dist/textarea.css +2 -6
  220. package/dist/textarea.layered.css +2 -6
  221. package/dist/themepicker.css +2 -1
  222. package/dist/themepicker.layered.css +2 -1
  223. package/dist/tokens.css +13 -2
  224. package/dist/tokens.json +20 -3
  225. package/dist/types/components/bottomsheet/bottomsheet.d.ts +35 -0
  226. package/dist/types/components/bottomsheet/bottomsheet.recipe.d.ts +3 -0
  227. package/dist/types/components/bottomsheet/index.d.ts +2 -0
  228. package/dist/types/components/breadcrumb/breadcrumb.d.ts +9 -1
  229. package/dist/types/components/button/button.d.ts +2 -0
  230. package/dist/types/components/button/button.recipe.d.ts +24 -6
  231. package/dist/types/components/checkbox/checkbox.recipe.d.ts +28 -5
  232. package/dist/types/components/chip/chip.d.ts +22 -0
  233. package/dist/types/components/chip/chip.recipe.d.ts +49 -1
  234. package/dist/types/components/chipgroup/chipgroup.d.ts +14 -0
  235. package/dist/types/components/chipgroup/chipgroup.recipe.d.ts +5 -0
  236. package/dist/types/components/chipgroup/index.d.ts +2 -0
  237. package/dist/types/components/datatable/datatable.d.ts +19 -0
  238. package/dist/types/components/datepickerfield/datepickerfield.d.ts +4 -1
  239. package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +18 -3
  240. package/dist/types/components/fileupload/fileupload.d.ts +26 -0
  241. package/dist/types/components/input/index.d.ts +1 -1
  242. package/dist/types/components/input/input.d.ts +72 -0
  243. package/dist/types/components/menusheet/index.d.ts +2 -0
  244. package/dist/types/components/menusheet/menusheet.d.ts +42 -0
  245. package/dist/types/components/menusheet/menusheet.recipe.d.ts +3 -0
  246. package/dist/types/components/snackbar/index.d.ts +2 -0
  247. package/dist/types/components/snackbar/snackbar.d.ts +36 -0
  248. package/dist/types/components/snackbar/snackbar.recipe.d.ts +5 -0
  249. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -1
  250. package/dist/types/components/tabs/tabs.d.ts +11 -1
  251. package/dist/types/core/floating-portal.d.ts +6 -5
  252. package/dist/types/index.d.ts +7 -0
  253. package/dist/types/product/comments/comments.d.ts +9 -3
  254. package/dist/types/product/comments/comments.recipe.d.ts +12 -2
  255. package/dist/types/product/inbox/inbox.d.ts +13 -1
  256. package/dist/types/product/memberssettings/index.d.ts +1 -0
  257. package/dist/types/product/memberssettings/memberssettings.d.ts +85 -0
  258. package/dist/types/product/memberssettings/memberssettings.recipe.d.ts +3 -0
  259. package/dist/types/product/notificationssettings/index.d.ts +1 -0
  260. package/dist/types/product/notificationssettings/notificationssettings.d.ts +37 -0
  261. package/dist/types/product/notificationssettings/notificationssettings.recipe.d.ts +3 -0
  262. package/dist/types/product/recordpage/index.d.ts +1 -1
  263. package/dist/types/product/recordpage/recordpage.d.ts +99 -2
  264. package/dist/types/product/workspacegeneralsettings/index.d.ts +1 -0
  265. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.d.ts +48 -0
  266. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.recipe.d.ts +3 -0
  267. package/dist/types/tokens/contract.d.ts +4 -1
  268. package/dist/types/tokens/elevation.d.ts +8 -5
  269. package/dist/types/tokens/palette.d.ts +9 -1
  270. package/dist/types/tokens/scale.d.ts +11 -1
  271. package/dist/types/tokens/semantic.d.ts +2 -0
  272. package/dist/wheelpicker.css +4 -2
  273. package/dist/wheelpicker.layered.css +4 -2
  274. package/dist/workspacegeneralsettings.css +30 -0
  275. package/dist/workspacegeneralsettings.d.ts +7 -0
  276. package/dist/workspacegeneralsettings.layered.css +32 -0
  277. package/package.json +36 -1
@@ -1,5 +1,5 @@
1
1
  import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
2
- import { contract as c, elevation as e } from "../../tokens/index.js";
2
+ import { contract as c } from "../../tokens/index.js";
3
3
  const spec = defineSpec({
4
4
  name: "button",
5
5
  base: {
@@ -22,6 +22,12 @@ const spec = defineSpec({
22
22
  },
23
23
  parts: [
24
24
  { selector: "&:disabled", decls: { opacity: 0.5, cursor: "not-allowed" } },
25
+ // your-moon/crisp#910: seed-design's every Button variant
26
+ // gives press feedback as a `scaleScope: self` micro-interaction, not a
27
+ // shadow/fill change alone (`action-button.yaml`, read in the seed raw
28
+ // dump) -- on top of, not instead of, each intent's own colour-on-press
29
+ // below. Root-level so it applies uniformly across all four intents.
30
+ { selector: "&:active", decls: { transform: "scale(0.98)" } },
25
31
  ],
26
32
  variants: {
27
33
  intent: {
@@ -35,12 +41,13 @@ const spec = defineSpec({
35
41
  // buttons use white).
36
42
  background: c.bgBrandContrast,
37
43
  color: c.fgOnBrand,
38
- // crisp#494: the inset ring is a black tint, not white -- live-
39
- // measured on Attio's own "New Company" button as
40
- // rgba(0,0,0,0.06), not the rgba(255,255,255,0.07) both solid
41
- // intents shared. The other two shadow layers already matched
42
- // exactly.
43
- boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08)",
44
+ // your-moon/crisp#910: seed's brandSolid carries no
45
+ // shadow/ring at all (`action-button.yaml`: `border: none`, no
46
+ // shadow key) -- dropped the coloured ambient drop-shadow and the
47
+ // black-tint inset ring (crisp#494) that were tuned for Attio's
48
+ // own look, per the owner's directive that elevation/control
49
+ // styling now follows seed, not Attio.
50
+ boxShadow: "none",
44
51
  },
45
52
  // your-moon/crisp#897: a brightness filter, not a second background
46
53
  // swap — matches `danger`'s own pattern below. `bgBrandSolidPressed`
@@ -56,20 +63,29 @@ const spec = defineSpec({
56
63
  },
57
64
  neutral: {
58
65
  decls: {
59
- background: c.bgSurface,
66
+ // your-moon/crisp#910: seed's `neutralWeak` fill is
67
+ // `bg.neutral-weak` (#f3f4f5) at rest, not an opaque white --
68
+ // `bgNeutralWeak` is already this exact hex (no new measurement).
69
+ // No border/shadow in seed's spec either; dropped `e.field`'s
70
+ // 3-layer button-chrome glow for the same reason primary/danger
71
+ // lost theirs.
72
+ background: c.bgNeutralWeak,
60
73
  color: c.fgPrimary,
61
- boxShadow: e.field,
74
+ boxShadow: "none",
62
75
  },
63
- parts: [{ selector: "&:hover", decls: { background: c.bgRaised } }],
76
+ parts: [
77
+ // seed's neutralWeak "pressed" bg (#eeeff1) is `borderSubtle`'s
78
+ // own already-exact value -- reused, not a fresh literal.
79
+ { selector: "&:hover", decls: { background: c.borderSubtle } },
80
+ ],
64
81
  },
65
82
  danger: {
66
83
  decls: {
67
84
  background: c.bgDangerSolid,
68
85
  color: c.fgOnBrand,
69
- // Same corrected inset ring as primary (crisp#494) -- shared
70
- // literal, same solid-fill button family; the coloured layers
71
- // themselves weren't independently re-measured this round.
72
- boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(186,37,37,0.20), 0 3px 6px -2px rgba(186,37,37,0.12)",
86
+ // your-moon/crisp#910: same "no shadow at all" call as
87
+ // primary above -- seed's criticalSolid carries no ring either.
88
+ boxShadow: "none",
73
89
  },
74
90
  parts: [
75
91
  { selector: "&:hover", decls: { filter: "brightness(0.96)" } },
@@ -79,7 +95,12 @@ const spec = defineSpec({
79
95
  ghost: {
80
96
  decls: {
81
97
  background: "transparent",
82
- color: c.fgMuted,
98
+ // your-moon/crisp#910: seed's `ghost` enabled label/icon
99
+ // colour is `fg.neutral` (full ink), not a de-emphasised muted
100
+ // grey that only darkens on hover -- `action-button.yaml`'s own
101
+ // enabled-state colour table has no separate "muted" step for
102
+ // ghost. Real behaviour change, flagged in HANDOFF/CHANGELOG.
103
+ color: c.fgPrimary,
83
104
  boxShadow: "none",
84
105
  },
85
106
  parts: [
@@ -100,6 +121,34 @@ const spec = defineSpec({
100
121
  fontSize: c.text.md,
101
122
  borderRadius: c.radius.md,
102
123
  },
124
+ // crisp#688: Attio's own 20px small button, split out of #682 rather
125
+ // than redefining `sm` -- 72 existing `size="sm"` call sites (sidebar,
126
+ // comments, recordpage, workspacegeneralsettings, personchip,
127
+ // datatable) already rely on sm's 24px, unverified against `xs`'s own
128
+ // context. Box measured live on app.attio.com Home/Tasks ("View all",
129
+ // "Today, <date>", the date-stepper's Previous/Next-day icon
130
+ // buttons): 20px height, 6px radius, 6px inline padding on text
131
+ // buttons (a 20x20 icon-only button centers a 16px icon the same way
132
+ // shape="icon" already does for every other size, so it needs no
133
+ // extra rule), 4px gap between multiple children.
134
+ //
135
+ // The label itself is NOT 14px/#682's tracking -- getComputedStyle on
136
+ // the live button (fonts loaded, DPR 2, zero zoom/transform anywhere
137
+ // in the ancestor chain) reads 10px/regular/15px line-height/normal
138
+ // tracking, independently matching `recordpanel.recipe.ts`'s own
139
+ // "View all" measurement (10px/400) elsewhere in this codebase. 10px
140
+ // sits below the text scale's 11px floor, so it's a literal here, the
141
+ // same as recordpanel's.
142
+ xs: {
143
+ height: "20px",
144
+ paddingInline: c.space["1.5"],
145
+ fontSize: "10px",
146
+ fontWeight: c.weight.regular,
147
+ lineHeight: "15px",
148
+ letterSpacing: "normal",
149
+ borderRadius: c.radius.sm,
150
+ gap: c.space["1"],
151
+ },
103
152
  md: {
104
153
  decls: {
105
154
  height: "28px",
@@ -129,7 +178,10 @@ const spec = defineSpec({
129
178
  height: "40px",
130
179
  paddingInline: "14px",
131
180
  fontSize: c.text.lg,
132
- borderRadius: "10px",
181
+ // your-moon/crisp#910: was a stray literal `10px`, matching neither
182
+ // the token scale nor seed's own large-size radius (`$radius.r3` =
183
+ // 12px, `action-button.yaml`) -- now the actual `xl` token.
184
+ borderRadius: c.radius.xl,
133
185
  },
134
186
  },
135
187
  shape: {
@@ -1,8 +1,10 @@
1
1
  import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
2
2
  import { contract as c } from "../../tokens/index.js";
3
- // A 16px square checkbox measured from a record grid: r6, a subtle 1px
4
- // inset ring when empty, brand fill + white check when on. The check is a
5
- // data-URI SVG background so it renders reliably (input pseudo-content doesn't).
3
+ // A 16px square checkbox measured from a record grid: brand fill + white
4
+ // check when on. The check is a data-URI SVG background so it renders
5
+ // reliably (input pseudo-content doesn't). Radius (r4) and the real 1px
6
+ // border when empty are seed-design's own (your-moon/crisp#926),
7
+ // not the original 6px/box-shadow-ring measurement.
6
8
  const CHECK = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E\")";
7
9
  const spec = defineSpec({
8
10
  name: "checkbox",
@@ -14,25 +16,32 @@ const spec = defineSpec({
14
16
  margin: 0,
15
17
  width: "16px",
16
18
  height: "16px",
17
- border: "none",
18
- borderRadius: c.radius.sm,
19
+ // your-moon/crisp#926: was `border: none` + a box-shadow
20
+ // inset ring -- seed-design's own checkmark (`checkmark.yaml`) is a real
21
+ // 1px border (`stroke.neutral-weak`, `borderDefault`'s exact value),
22
+ // removed entirely (not hidden via boxShadow:none) when selected, same
23
+ // technique change already made for Input (your-moon/crisp#910).
24
+ border: `1px solid ${c.borderDefault}`,
25
+ // your-moon/crisp#926: seed's checkmark box corner is
26
+ // `$radius.r1` (4px, `radius.xs`), not the 6px `radius.sm` this shared
27
+ // with Chip's old rounded-rect shape.
28
+ borderRadius: c.radius.xs,
19
29
  background: c.bgSurface,
20
- boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 9%, transparent)`,
21
30
  cursor: "pointer",
22
- transition: "background-color .12s, box-shadow .12s",
31
+ transition: "background-color .12s, border-color .12s",
23
32
  },
24
33
  parts: [
25
34
  {
26
35
  selector: "&:hover",
27
36
  decls: {
28
- boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 30%, transparent)`,
37
+ borderColor: `color-mix(in srgb, ${c.fgPrimary} 30%, transparent)`,
29
38
  },
30
39
  },
31
40
  {
32
41
  selector: "&:checked, &:indeterminate",
33
42
  decls: {
34
43
  background: `${c.bgBrandSolid} center / 10px no-repeat`,
35
- boxShadow: "none",
44
+ borderColor: "transparent",
36
45
  },
37
46
  },
38
47
  { selector: "&:checked", decls: { backgroundImage: CHECK } },
@@ -50,6 +59,49 @@ const spec = defineSpec({
50
59
  outlineOffset: "1px",
51
60
  },
52
61
  },
62
+ // your-moon/crisp#963: was no `:disabled` styling at all
63
+ // -- seed-design's own checkmark spec (`checkmark.yaml`) treats disabled
64
+ // as its own recolor, not a blanket opacity dim: fill `bg.disabled`
65
+ // (crisp's already-exact `bgNeutralWeak`, #f3f4f5), border
66
+ // `stroke.neutral-muted` (crisp's already-exact `borderSubtle`, applied
67
+ // here as the disabled border colour rather than its usual ring role).
68
+ // "disabled (any state)" per seed -- checked/indeterminate get the same
69
+ // muted fill/border, not the brand fill, hence the compound selectors
70
+ // below outranking the plain `:checked`/`:indeterminate` rule above.
71
+ {
72
+ selector: "&:disabled",
73
+ decls: {
74
+ background: c.bgNeutralWeak,
75
+ borderColor: c.borderSubtle,
76
+ cursor: "not-allowed",
77
+ },
78
+ },
79
+ {
80
+ // Higher specificity than the plain `&:checked, &:indeterminate` rule
81
+ // above (two pseudo-classes vs one), so this wins regardless of
82
+ // declaration order. Deliberately keeps the existing white check icon
83
+ // rather than recolouring it to seed's `fg.disabled` -- that glyph is
84
+ // baked into a static SVG data-URI with no theme-aware way to retint
85
+ // it without switching techniques entirely (mask + a separate colour
86
+ // layer), and white still reads clearly against the muted grey fill.
87
+ selector: "&:checked:disabled, &:indeterminate:disabled",
88
+ decls: {
89
+ background: `${c.bgNeutralWeak} center / 10px no-repeat`,
90
+ borderColor: c.borderSubtle,
91
+ },
92
+ },
93
+ { selector: "&:checked:disabled", decls: { backgroundImage: CHECK } },
94
+ {
95
+ // The `background` shorthand just above resets `background-image` to
96
+ // `none` on every property it doesn't restate -- without this, a
97
+ // disabled indeterminate checkbox would show the muted fill but lose
98
+ // its dash entirely.
99
+ selector: "&:indeterminate:disabled",
100
+ decls: {
101
+ backgroundImage: `linear-gradient(${c.fgOnBrand}, ${c.fgOnBrand})`,
102
+ backgroundSize: "8px 2px",
103
+ },
104
+ },
53
105
  ],
54
106
  });
55
107
  const config = specToRecipe(spec);
@@ -15,9 +15,21 @@ import { chipRecipe } from "./chip.recipe.js";
15
15
  * an inline ×) — so `Chip` has no `onRemove`/remove button yet. Add one once
16
16
  * that surface can be measured; don't guess the icon, its hit target, or its
17
17
  * spacing from the label.
18
+ *
19
+ * `appearance` (`"filled"` default | `"outline"`) and `selected` separate
20
+ * three visual roles crisp/#907's study found conflated: a filled chip reads
21
+ * as a status/value (not clickable), an outline chip reads as actionable (a
22
+ * tag or filter you can tap), and `selected` overrides either to a dark fill
23
+ * once tapped — see `chip.recipe.ts` for the structural reference.
18
24
  */
19
- export const Chip = React.forwardRef(({ tone, truncate, className, asChild, children, ...props }, ref) => {
25
+ export const Chip = React.forwardRef(({ tone, truncate, appearance, selected, className, asChild, children, ...props }, ref) => {
20
26
  const content = truncate && !asChild && typeof children === "string" ? (_jsx("span", { className: "crisp-chip-label", children: children })) : (children);
21
- return (_jsx(Primitive.span, { ref: ref, asChild: asChild, className: chipRecipe({ tone, truncate, className }), ...props, children: content }));
27
+ return (_jsx(Primitive.span, { ref: ref, asChild: asChild, className: chipRecipe({
28
+ tone,
29
+ truncate,
30
+ appearance,
31
+ selected,
32
+ className,
33
+ }), ...props, children: content }));
22
34
  });
23
35
  Chip.displayName = "Chip";
@@ -9,6 +9,15 @@ import { contract as c } from "../../tokens/index.js";
9
9
  // can't be reused verbatim here — Chip's root IS the styled element, not a
10
10
  // wrapper around one. The values below are transcribed from recordTagCss,
11
11
  // not re-measured; keep the two in sync by hand if either changes.
12
+ //
13
+ // your-moon/crisp#919: `radius.chip` itself (tokens/scale.ts) moved
14
+ // from Attio's measured 7px to a pill (999px), matching seed-design's own
15
+ // `chip.yaml` (always a pill, no size-based radius step at all). Every
16
+ // consumer sharing this one token — DataTable/RecordPage/RecordsTable/
17
+ // MultiSelect's tag & mention chips, TableToolbar/DatePickerField's date
18
+ // presets — goes pill-shaped together from this single token change, not a
19
+ // per-file edit; that's the whole reason those rules keep re-declaring
20
+ // `border-radius: ${c.radius.chip}` instead of a literal.
12
21
  const ring = (v, pct) => `inset 0 0 0 1px color-mix(in srgb, ${v} ${pct}%, transparent)`;
13
22
  const soft = (v, pct) => `color-mix(in srgb, ${v} ${pct}%, transparent)`;
14
23
  const onFg = (v, pct) => `color-mix(in srgb, ${v} ${pct}%, ${c.fgPrimary})`;
@@ -64,7 +73,9 @@ const spec = defineSpec({
64
73
  selector: "& .crisp-chip-label",
65
74
  decls: {
66
75
  minWidth: 0,
67
- overflow: "hidden",
76
+ // #719: `clip`, not `hidden` -- immune to a programmatic
77
+ // `scrollLeft` assignment; renders the ellipsis identically.
78
+ overflow: "clip",
68
79
  textOverflow: "ellipsis",
69
80
  whiteSpace: "nowrap",
70
81
  display: "block",
@@ -116,8 +127,67 @@ const spec = defineSpec({
116
127
  boxShadow: ring(c.bgOrange, 30),
117
128
  },
118
129
  },
130
+ // your-moon/crisp#907: a three-state read (actionable / informational /
131
+ // selected), the same shape as seed-design's own `chip.yaml` (`variant:
132
+ // solid|outlineStrong|outlineWeak`, each with a `selected` state) --
133
+ // structural reference only, not Naver/Kakao's colours (the study only
134
+ // supplied the pattern: outline reads as tappable, filled reads as
135
+ // status, dark fill reads as selected). `outline` only clears
136
+ // `background`, keeping the `tone`'s own `color`/`boxShadow` ring --
137
+ // SEED's own spec keeps `label.color` identical between `solid` and
138
+ // `outlineWeak`/`outlineStrong`, so the ring is the only thing that
139
+ // changes. Declared after `tone` so its rule wins the cascade at equal
140
+ // specificity (`core/resolve.ts` emits variant blocks in declaration
141
+ // order; see `build/generate-recipes.mjs`).
142
+ appearance: {
143
+ filled: {},
144
+ outline: { background: "transparent" },
145
+ },
146
+ // Overrides `tone`+`appearance` entirely to crisp's own dark-inverted
147
+ // pair (`bgNeutralInverted`/`fgOnNeutralInverted`, already shipped for
148
+ // `Snackbar` #911) -- SEED's `solid`/`outlineStrong` selected state, not
149
+ // Daum's `rgb(44,46,51)` (crisp's own token is `#2a3038`, close but
150
+ // deliberately not copied). Declared last so it always wins regardless
151
+ // of `tone`/`appearance`. Simplified from SEED, which gives
152
+ // `outlineWeak` a DIFFERENT, lighter selected treatment (a stronger
153
+ // stroke + weak fill, not a full inversion) -- one consistent dark-fill
154
+ // "selected" reads clearer across every tone than three different
155
+ // selected looks, and matches what #907 actually asked for.
156
+ selected: {
157
+ true: {
158
+ background: c.bgNeutralInverted,
159
+ color: c.fgOnNeutralInverted,
160
+ boxShadow: "none",
161
+ },
162
+ false: {},
163
+ },
164
+ },
165
+ // Team-lead review of #955's screenshot: the neutral tone's outline ring
166
+ // reuses `borderSubtle` (`#eeeff1`, meant for hairline dividers under a
167
+ // fill, not a standalone edge) -- on outline's transparent background
168
+ // it's nearly invisible, defeating the "outline reads as tappable" point
169
+ // of the appearance itself. Every other tone's ring is already a 30%
170
+ // tint of a saturated colour, visible enough on white; only neutral needs
171
+ // the fix. Matches `Input`'s own border token, per the reviewer's note.
172
+ // `selected: false` keeps this from re-drawing a border under `selected`'s
173
+ // dark fill -- compound rules emit after every plain variant rule
174
+ // (including `selected`'s own), so without this an outline+selected+
175
+ // neutral chip would get its border back on top of `selected`'s
176
+ // `boxShadow: "none"`.
177
+ compoundVariants: [
178
+ {
179
+ tone: "neutral",
180
+ appearance: "outline",
181
+ selected: false,
182
+ decls: { boxShadow: `inset 0 0 0 1px ${c.borderDefault}` },
183
+ },
184
+ ],
185
+ defaultVariants: {
186
+ tone: "neutral",
187
+ truncate: false,
188
+ appearance: "filled",
189
+ selected: false,
119
190
  },
120
- defaultVariants: { tone: "neutral", truncate: false },
121
191
  });
122
192
  const config = specToRecipe(spec);
123
193
  export const chipRecipe = Object.assign(resolveRecipe(config), { config });
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Primitive } from "../../core/index.js";
4
+ import { chipgroupRecipe } from "./chipgroup.recipe.js";
5
+ /**
6
+ * A single-line, horizontally-scrolling row of `Chip`s (or anything else),
7
+ * with scroll-snap — for a filter/status row that would otherwise wrap onto
8
+ * a second line once it runs out of width (crisp#908). Every child snaps to
9
+ * the row's start edge and never shrinks, so scrolling always lands a chip
10
+ * flush against the left edge rather than mid-chip.
11
+ */
12
+ export const ChipGroup = React.forwardRef(({ className, asChild, ...props }, ref) => {
13
+ return (_jsx(Primitive.div, { ref: ref, asChild: asChild, role: "group", className: chipgroupRecipe({ className }), ...props }));
14
+ });
15
+ ChipGroup.displayName = "ChipGroup";
@@ -0,0 +1,33 @@
1
+ import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
+ import { contract as c } from "../../tokens/index.js";
3
+ // your-moon/crisp#908: a horizontally-scrolling, single-line row for a set of
4
+ // `Chip`s -- Naver/Kakao both solve "many filter/status chips" this way
5
+ // (docs/study/korean/: Naver home `comp_snap_list`, `scroll-snap-type: x
6
+ // mandatory`; Daum `UL.grid_xscroll`, native overflow with no snap). crisp
7
+ // had no equivalent: a consumer's own flex row wraps chips onto a second
8
+ // line once they run out of horizontal space, which breaks the one-line
9
+ // table-header density Attio parity is built on (GRC's own toolbar filter
10
+ // row and record tabs, per the issue). No new visual values -- the gap
11
+ // between chips reuses `Chip`'s own measured 3px inter-child gap
12
+ // (chip.recipe.ts's `gap: "3px"` comment), and the scrollbar is hidden the
13
+ // same way `WheelPicker`'s already-shipped scroll column does
14
+ // (`scrollbar-width: none` + the `-webkit-scrollbar` pseudo), not a new
15
+ // pattern invented for this component.
16
+ const config = defineRecipe({
17
+ name: "chipgroup",
18
+ base: `
19
+ display: flex; flex-wrap: nowrap; align-items: center; gap: ${c.space["1.5"]};
20
+ overflow-x: auto; overscroll-behavior-x: contain;
21
+ scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
22
+ scrollbar-width: none; -ms-overflow-style: none;
23
+ &::-webkit-scrollbar { display: none; }
24
+ & > * { flex-shrink: 0; scroll-snap-align: start; }
25
+ `,
26
+ variants: {},
27
+ defaultVariants: {},
28
+ compoundVariants: [],
29
+ });
30
+ export const chipgroupRecipe = Object.assign(resolveRecipe(config), {
31
+ config,
32
+ });
33
+ export default config;
@@ -0,0 +1,2 @@
1
+ export { ChipGroup } from "./chipgroup.js";
2
+ export { chipgroupRecipe } from "./chipgroup.recipe.js";
@@ -369,7 +369,11 @@ function renderTyped(col, value, avatarTone, row, display, locale = "en-US", ope
369
369
  const e = display ? { ...raw, name: display } : raw;
370
370
  if (!e.name)
371
371
  return empty;
372
- return (_jsxs("span", { className: "crisp-datatable-entity", children: [_jsx("span", { className: "crisp-datatable-avatar", "data-tone": avatarTone === "hashed" ? toneFor(raw.name) : undefined, children: e.avatar ? (_jsx("img", { src: e.avatar, alt: "" })) : (initials(e.name).slice(0, 2)) }), col.onEntityClick ? (_jsx("button", { type: "button", className: "crisp-datatable-entity-name crisp-datatable-entity-name--link", onClick: (ev) => {
372
+ return (_jsxs("span", { className: "crisp-datatable-entity", children: [_jsx("span", { className: "crisp-datatable-avatar", "data-tone": avatarTone === "hashed" ? toneFor(raw.name) : undefined, "data-shape": col.avatarShape === "circle" ? "circle" : undefined, children: e.avatar ? (_jsx("img", { src: e.avatar, alt: "" })) : (initials(e.name).slice(0, 2)) }), col.href ? (_jsx("a", { href: col.href(row), className: "crisp-datatable-entity-name crisp-datatable-entity-name--link", onClick: (ev) => {
373
+ // same reasoning as onEntityClick's stopPropagation below --
374
+ // this must not also trigger the row's own onRowClick.
375
+ ev.stopPropagation();
376
+ }, children: e.name })) : col.onEntityClick ? (_jsx("button", { type: "button", className: "crisp-datatable-entity-name crisp-datatable-entity-name--link", onClick: (ev) => {
373
377
  // the cell's own click already just selects (see the
374
378
  // TableCell onClick above) -- this must additionally stop
375
379
  // the row's onRowClick from firing, since this name points
@@ -113,7 +113,13 @@ const config = defineRecipe({
113
113
  & .crisp-datatable-group-count { margin-inline-start: ${c.space["1.5"]}; color: ${c.fgMuted}; font-variant-numeric: tabular-nums; }
114
114
  & .crisp-datatable-th-rename { width: 100%; min-width: 0; height: ${c.size.row}; margin: 0; padding: 0 ${c.space["1"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px ${c.bgBrandSolid}; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; outline: none; }
115
115
  & .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px ${c.bgBrandSolid}; }
116
- & .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
116
+ /* #719: overflow: clip, not hidden, on every truncation rule below --
117
+ hidden still honours a programmatic scrollLeft assignment (found via
118
+ a screenshot tool that pans anything with scrollWidth > clientWidth),
119
+ blanking or fragmenting the visible text even though real user
120
+ scrolling was already blocked. clip renders the ellipsis identically
121
+ and leaves no scroll position to move. */
122
+ & .crisp-datatable-th-label { overflow: clip; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
117
123
  /* #465: 12px per the comment above (and Attio's measured header cell
118
124
  padding: 0 12px) -- was still c.space["2"] (8px), drifted from its
119
125
  own documented value. */
@@ -200,7 +206,7 @@ const config = defineRecipe({
200
206
  /* reference picker: a leading 16px avatar per option, plus a trailing
201
207
  "Create X" row when the consumer supplies one (#449) -- same measured
202
208
  values as RecordsTable's own .crisp-recordstable-refoption*. */
203
- & .crisp-datatable-refoption { display: flex; align-items: center; gap: ${c.space["2"]}; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
209
+ & .crisp-datatable-refoption { display: flex; align-items: center; gap: ${c.space["2"]}; min-width: 0; overflow: clip; text-overflow: ellipsis; }
204
210
  & .crisp-datatable-refoption-avatar { flex-shrink: 0; }
205
211
  & .crisp-datatable-refoption--create { color: ${c.fgMuted}; }
206
212
  & .crisp-datatable-empty { padding: ${c.space["8"]} ${c.space["3"]}; text-align: center; color: ${c.fgMuted}; font-size: ${c.text.lg}; }
@@ -286,8 +292,11 @@ const config = defineRecipe({
286
292
  (#833/#835/#848) that happened to apply the same 500 to every
287
293
  primary-column body cell regardless of type. Made explicit here so
288
294
  fixing that leak doesn't also (wrongly) drop entity names to 400. */
289
- & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: ${c.weight.medium}; text-decoration: underline; text-decoration-color: color-mix(in srgb, ${c.fgPrimary} 9%, transparent); }
290
- & button.crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
295
+ & .crisp-datatable-entity-name { overflow: clip; text-overflow: ellipsis; white-space: nowrap; font-weight: ${c.weight.medium}; text-decoration: underline; text-decoration-color: color-mix(in srgb, ${c.fgPrimary} 9%, transparent); }
296
+ /* #893: shared by the button (onEntityClick, a different record) and
297
+ the anchor (href, the row's own record) -- was button-only, which
298
+ silently left a real <a> unstyled the day #893 added one. */
299
+ & .crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
291
300
  /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
292
301
  column, revealed on ROW hover (the issue's own title says "row
293
302
  hover", and a live check confirmed hovering well past the name --
@@ -301,7 +310,13 @@ const config = defineRecipe({
301
310
  & tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
302
311
  & .crisp-datatable-entity-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: ${c.radius.sm}; background: none; font-family: inherit; color: color-mix(in srgb, ${c.fgPrimary} 55%, transparent); cursor: pointer; flex-shrink: 0; }
303
312
  & .crisp-datatable-entity-action:hover { background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); color: ${c.fgPrimary}; }
304
- & .crisp-datatable-avatar { width: ${c.size.icon}; height: ${c.size.icon}; border-radius: ${c.radius.sm}; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: ${c.weight.semibold}; text-transform: uppercase; color: ${c.fgOnBrand}; background: ${c.bgBrandSolid}; }
313
+ /* #892: the default shape's radius was a stray literal (radius.sm, 6px)
314
+ -- Attio's own real company/object avatar measures 30%, which is
315
+ Avatar's own already-shipped shape="rounded" value, not a new number.
316
+ shape="circle" reuses Avatar's own shape="circle" value (radius.full)
317
+ the same way, measured live on a real Attio person avatar. */
318
+ & .crisp-datatable-avatar { width: ${c.size.icon}; height: ${c.size.icon}; border-radius: 30%; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: ${c.weight.semibold}; text-transform: uppercase; color: ${c.fgOnBrand}; background: ${c.bgBrandSolid}; }
319
+ & .crisp-datatable-avatar[data-shape="circle"] { border-radius: ${c.radius.full}; }
305
320
  & .crisp-datatable-avatar[data-tone="success"] { background: ${c.bgSuccess}; } & .crisp-datatable-avatar[data-tone="warning"] { background: ${c.bgWarning}; } & .crisp-datatable-avatar[data-tone="danger"] { background: ${c.bgDanger}; } & .crisp-datatable-avatar[data-tone="purple"] { background: ${c.bgPurple}; } & .crisp-datatable-avatar[data-tone="cyan"] { background: ${c.bgCyan}; } & .crisp-datatable-avatar[data-tone="orange"] { background: ${c.bgOrange}; }
306
321
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
307
322
  & .crisp-datatable-tags { display: flex; align-items: center; gap: ${c.space["1"]}; overflow: hidden; }
@@ -323,7 +338,7 @@ const config = defineRecipe({
323
338
  own .crisp-recordstable-link, which this mirrors: no underline at
324
339
  rest, underline on hover, own-colour text rather than a browser-blue
325
340
  link. */
326
- & .crisp-datatable-link { color: ${c.fgPrimary}; text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
341
+ & .crisp-datatable-link { color: ${c.fgPrimary}; text-decoration: none; overflow: clip; text-overflow: ellipsis; }
327
342
  & .crisp-datatable-link:hover { text-decoration: underline; }
328
343
  /* phone cell type/editor (#765) -- measured live: the dial-code prefix
329
344
  is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text
@@ -22,7 +22,7 @@ const fromDate = (d) => ({
22
22
  * popover. This is what `DatePicker` used to be; `DatePicker` is now
23
23
  * seed-design's bare calendar, and this composes it.
24
24
  */
25
- export function DatePickerField({ value, defaultValue, onValueChange, onChange, placeholder: placeholderProp, variant = "default", format = defaultFormat, clearable = true, clearLabel: clearLabelProp, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
25
+ export function DatePickerField({ value, defaultValue, onValueChange, onChange, placeholder: placeholderProp, variant = "default", width = "full", format = defaultFormat, clearable = true, clearLabel: clearLabelProp, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
26
26
  // crisp/#471: same resolution order as Select's own.
27
27
  const placeholder = useCrispMessage(placeholderProp, "Set date…");
28
28
  const clearLabel = useCrispMessage(clearLabelProp, "Clear date");
@@ -43,7 +43,7 @@ export function DatePickerField({ value, defaultValue, onValueChange, onChange,
43
43
  onValueChange?.(null);
44
44
  onChange?.(null);
45
45
  };
46
- return (_jsx("div", { className: datepickerfieldRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-datepickerfield-box", "data-ghost": variant === "ghost" ? "" : undefined, "data-placeholder": selected ? undefined : "", children: [_jsx(Popover, { open: open, onOpenChange: setOpen, "aria-label": ariaLabel, trigger: _jsxs("button", { type: "button", className: "crisp-datepickerfield-trigger", "aria-label": ariaLabel, children: [_jsx(CalendarDays, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), selected ? format(selected) : placeholder] }), children: _jsxs("div", { className: "crisp-datepickerfield-panel", children: [_jsx(DatePicker
46
+ return (_jsx("div", { className: datepickerfieldRecipe({ width, className }), ...rest, children: _jsxs("div", { className: "crisp-datepickerfield-box", "data-ghost": variant === "ghost" ? "" : undefined, "data-placeholder": selected ? undefined : "", children: [_jsx(Popover, { open: open, onOpenChange: setOpen, "aria-label": ariaLabel, trigger: _jsxs("button", { type: "button", className: "crisp-datepickerfield-trigger", "aria-label": ariaLabel, children: [_jsx(CalendarDays, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), selected ? format(selected) : placeholder] }), children: _jsxs("div", { className: "crisp-datepickerfield-panel", children: [_jsx(DatePicker
47
47
  // DatePickerProps is a union over selectionMode, and TypeScript
48
48
  // cannot narrow it through a spread: it tries every branch and
49
49
  // reports the range one as missing props. The field is always
@@ -10,7 +10,7 @@ const config = defineRecipe({
10
10
  display: inline-flex; font-family: ${c.font.sans};
11
11
 
12
12
  & .crisp-datepickerfield-box {
13
- display: inline-flex; align-items: center;
13
+ display: inline-flex; align-items: center; width: 100%;
14
14
  box-sizing: border-box; height: 32px; margin: 0;
15
15
  border-radius: ${c.radius.md}; background: transparent;
16
16
  box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 7%, transparent);
@@ -19,6 +19,7 @@ const config = defineRecipe({
19
19
  & .crisp-datepickerfield-box[data-ghost] { background: transparent; box-shadow: none; }
20
20
  & .crisp-datepickerfield-box[data-ghost]:hover { background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); }
21
21
  & .crisp-datepickerfield-trigger {
22
+ flex: 1; min-width: 0;
22
23
  display: inline-flex; align-items: center; gap: ${c.space["2"]};
23
24
  height: 100%; padding: 0 ${c.space["2.5"]}; margin: 0;
24
25
  border: none; background: none; color: inherit; font-family: inherit;
@@ -48,8 +49,20 @@ const config = defineRecipe({
48
49
  }
49
50
  & .crisp-datepickerfield-preset:hover { background: color-mix(in srgb, ${c.fgPrimary} 8%, transparent); }
50
51
  `,
51
- variants: {},
52
- defaultVariants: {},
52
+ variants: {
53
+ // crisp#980: full (default) fills its container, matching Select's own
54
+ // width variant/default -- so a form composing DatePickerField alongside
55
+ // Input/Select via FormField gets one consistent column width across
56
+ // every field type, not just some of them. auto sizes to content, the
57
+ // behaviour every consumer got unconditionally before this.
58
+ width: {
59
+ full: `width: 100%;`,
60
+ auto: `width: auto;`,
61
+ },
62
+ },
63
+ defaultVariants: {
64
+ width: "full",
65
+ },
53
66
  compoundVariants: [],
54
67
  });
55
68
  export const datepickerfieldRecipe = Object.assign(resolveRecipe(config), {
@@ -1,10 +1,18 @@
1
1
  import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
- import { contract as c, elevation as e } from "../../tokens/index.js";
3
- // A modal dialog surface — measured from a create-record modal: a radius-16
4
- // frosted-glass card (60% tinted background + 15px backdrop blur, both re-measured
5
- // per theme — light uses a blue-tinted 3-layer shadow, dark drops to a plain
6
- // 2-layer black shadow) with a bordered header (title + close), a scrollable
7
- // body, and a bordered footer for the primary action.
2
+ import { contract as c } from "../../tokens/index.js";
3
+ // your-moon/crisp#934: was a radius-16 frosted-glass card (a
4
+ // 60%-tinted, 15px-backdrop-blurred outer shell wrapping a solid radius-12
5
+ // inner card) -- owner-directed: Dialog's identity now follows seed-design
6
+ // (daangn), whose own Dialog spec has no shadow/blur/tint property at all;
7
+ // elevation there is conveyed purely by a darker backdrop scrim plus the
8
+ // existing scale+fade entrance animation (which crisp already had). Flat
9
+ // single surface now: `.crisp-dialog-content` is a bare, invisible sizing
10
+ // wrapper (no background/radius/shadow of its own); `.crisp-dialog-card` is
11
+ // the one real surface, radius bumped from `xl` (12px) to `3xl` (20px,
12
+ // seed's own shared floating-surface radius, `$radius.r5`), no shadow.
13
+ // `bgScrim` itself also moved toward seed's own darker backdrop value
14
+ // (`tokens/palette.ts`). The alert variant no longer needs its own override
15
+ // of either rule -- both variants now render the identical flat surface.
8
16
  //
9
17
  // #414: the footer's submit-key caps (⌘/Enter) used to be one span drawing a
10
18
  // visible pill (18px, 22%-white background). Attio's own are two separate
@@ -18,10 +26,8 @@ const config = defineRecipe({
18
26
  & .crisp-dialog-backdrop { position: fixed; inset: 0; z-index: 60; background: ${c.bgScrim}; }
19
27
  & .crisp-dialog-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh ${c.space["4"]} ${c.space["4"]}; }
20
28
  & .crisp-dialog-positioner[data-variant="alert"] { align-items: center; padding: ${c.space["4"]}; }
21
- & .crisp-dialog-content { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; padding: ${c.space["1"]}; border-radius: ${c.radius["2xl"]}; background: color-mix(in srgb, ${c.bgOverlayFrost} 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: ${e.modal}; opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
22
- & .crisp-dialog-content[data-variant="alert"] { padding: 0; background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
23
- & .crisp-dialog-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: ${c.radius.xl}; background: ${c.bgSurface}; box-shadow: 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 8%, transparent); overflow: hidden; }
24
- & .crisp-dialog-content[data-variant="alert"] .crisp-dialog-card { border-radius: ${c.radius.xl}; background: ${c.bgPopover}; box-shadow: ${e.floating}; }
29
+ & .crisp-dialog-content { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
30
+ & .crisp-dialog-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: ${c.radius["3xl"]}; background: ${c.bgSurface}; overflow: hidden; }
25
31
  & .crisp-dialog-header { display: flex; align-items: center; gap: ${c.space["2"]}; flex-shrink: 0; height: 52px; padding: 0 ${c.space["2"]} 0 ${c.space["4"]}; border-bottom: 1px solid ${c.borderSubtle}; }
26
32
  & .crisp-dialog-header svg { flex-shrink: 0; color: ${c.fgMuted}; }
27
33
  & .crisp-dialog-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; margin: 0; }
@@ -43,7 +49,12 @@ const config = defineRecipe({
43
49
  & .crisp-dialog-field { display: flex; flex-direction: column; gap: ${c.space["1.5"]}; }
44
50
  & .crisp-dialog-field-label { font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 50%, transparent); }
45
51
  & .crisp-dialog-more { display: inline-flex; align-items: center; gap: ${c.space["2"]}; margin-inline-end: auto; border: none; background: none; padding: 0; font-family: inherit; font-size: ${c.text.md}; color: ${c.fgMuted}; cursor: pointer; }
46
- & .crisp-dialog-submit { display: inline-flex; align-items: center; gap: ${c.space["2"]}; height: 28px; padding: 0 ${c.space["1"]} 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandSolid}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; }
52
+ /* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
53
+ the accent (#0298d9) only clears 3.23:1 with white; the darker
54
+ sibling clears 4.5:1+, same fix already applied to Button/Badge/
55
+ Avatar (#897). Scoped to this one fill only -- Dialog's own frosted-
56
+ surface identity (#934) is untouched here. */
57
+ & .crisp-dialog-submit { display: inline-flex; align-items: center; gap: ${c.space["2"]}; height: 28px; padding: 0 ${c.space["1"]} 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; }
47
58
  & .crisp-dialog-submit-key { display: inline-flex; align-items: center; gap: ${c.space["1"]}; }
48
59
  & .crisp-dialog-submit-key kbd { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0 3px; border-radius: 4px; background: transparent; color: ${c.fgOnBrand}; font-family: inherit; font-style: normal; font-size: 10px; }`,
49
60
  variants: {},