@crisp-ui-kit/crisp 0.57.0 → 0.57.2

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 (293) 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/authshell.css +9 -4
  5. package/dist/authshell.layered.css +9 -4
  6. package/dist/avatar.css +1 -1
  7. package/dist/avatar.layered.css +1 -1
  8. package/dist/badge.css +5 -5
  9. package/dist/badge.layered.css +5 -5
  10. package/dist/board.css +28 -8
  11. package/dist/board.layered.css +28 -8
  12. package/dist/bottomsheet.css +26 -0
  13. package/dist/bottomsheet.d.ts +7 -0
  14. package/dist/bottomsheet.layered.css +28 -0
  15. package/dist/button.css +9 -8
  16. package/dist/button.layered.css +9 -8
  17. package/dist/checkbox.css +7 -3
  18. package/dist/checkbox.layered.css +7 -3
  19. package/dist/chip.css +15 -0
  20. package/dist/chip.d.ts +3 -1
  21. package/dist/chip.layered.css +15 -0
  22. package/dist/chipgroup.css +5 -0
  23. package/dist/chipgroup.d.ts +7 -0
  24. package/dist/chipgroup.layered.css +7 -0
  25. package/dist/cjs/components/accentpicker/accentpicker.js +66 -0
  26. package/dist/cjs/components/accentpicker/accentpicker.recipe.js +25 -0
  27. package/dist/cjs/components/accentpicker/index.js +7 -0
  28. package/dist/cjs/components/avatar/avatar.recipe.js +4 -1
  29. package/dist/cjs/components/badge/badge.recipe.js +38 -10
  30. package/dist/cjs/components/bottomsheet/bottomsheet.js +90 -0
  31. package/dist/cjs/components/bottomsheet/bottomsheet.recipe.js +77 -0
  32. package/dist/cjs/components/bottomsheet/index.js +9 -0
  33. package/dist/cjs/components/button/button.recipe.js +55 -21
  34. package/dist/cjs/components/checkbox/checkbox.recipe.js +61 -9
  35. package/dist/cjs/components/chip/chip.js +14 -2
  36. package/dist/cjs/components/chip/chip.recipe.js +69 -1
  37. package/dist/cjs/components/chipgroup/chipgroup.js +51 -0
  38. package/dist/cjs/components/chipgroup/chipgroup.recipe.js +36 -0
  39. package/dist/cjs/components/chipgroup/index.js +7 -0
  40. package/dist/cjs/components/datatable/datatable.js +5 -1
  41. package/dist/cjs/components/datatable/datatable.recipe.js +11 -2
  42. package/dist/cjs/components/dialog/dialog.recipe.js +21 -10
  43. package/dist/cjs/components/emptystate/emptystate.recipe.js +7 -2
  44. package/dist/cjs/components/fileupload/fileupload.js +21 -4
  45. package/dist/cjs/components/input/index.js +2 -1
  46. package/dist/cjs/components/input/input.js +33 -3
  47. package/dist/cjs/components/input/input.recipe.js +22 -7
  48. package/dist/cjs/components/menu/menu.recipe.js +21 -7
  49. package/dist/cjs/components/menusheet/index.js +9 -0
  50. package/dist/cjs/components/menusheet/menusheet.js +133 -0
  51. package/dist/cjs/components/menusheet/menusheet.recipe.js +103 -0
  52. package/dist/cjs/components/multiselect/multiselect.js +2 -2
  53. package/dist/cjs/components/multiselect/multiselect.recipe.js +45 -12
  54. package/dist/cjs/components/progress/progress.js +21 -4
  55. package/dist/cjs/components/progress/progress.recipe.js +30 -0
  56. package/dist/cjs/components/select/select.js +3 -3
  57. package/dist/cjs/components/select/select.recipe.js +63 -10
  58. package/dist/cjs/components/snackbar/index.js +8 -0
  59. package/dist/cjs/components/snackbar/snackbar.js +121 -0
  60. package/dist/cjs/components/snackbar/snackbar.recipe.js +71 -0
  61. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  62. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +37 -12
  63. package/dist/cjs/components/tabs/tabs.js +10 -4
  64. package/dist/cjs/components/tabs/tabs.recipe.js +23 -6
  65. package/dist/cjs/components/text/text.recipe.js +8 -7
  66. package/dist/cjs/components/textarea/textarea.js +3 -2
  67. package/dist/cjs/components/textarea/textarea.recipe.js +13 -9
  68. package/dist/cjs/components/textlink/textlink.recipe.js +6 -3
  69. package/dist/cjs/index.js +8 -0
  70. package/dist/cjs/product/authshell/authshell.recipe.js +5 -1
  71. package/dist/cjs/product/command/command.recipe.js +4 -1
  72. package/dist/cjs/product/comments/comments.js +7 -2
  73. package/dist/cjs/product/comments/comments.recipe.js +25 -7
  74. package/dist/cjs/product/memberssettings/index.js +6 -0
  75. package/dist/cjs/product/memberssettings/memberssettings.js +99 -0
  76. package/dist/cjs/product/memberssettings/memberssettings.recipe.js +83 -0
  77. package/dist/cjs/product/notificationssettings/index.js +5 -0
  78. package/dist/cjs/product/notificationssettings/notificationssettings.js +19 -0
  79. package/dist/cjs/product/notificationssettings/notificationssettings.recipe.js +44 -0
  80. package/dist/cjs/product/recordpage/recordpage.js +101 -9
  81. package/dist/cjs/product/recordpage/recordpage.recipe.js +43 -6
  82. package/dist/cjs/product/workspacegeneralsettings/index.js +5 -0
  83. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.js +21 -0
  84. package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +51 -0
  85. package/dist/cjs/tokens/contract.js +1 -0
  86. package/dist/cjs/tokens/elevation.js +8 -5
  87. package/dist/cjs/tokens/palette.js +107 -33
  88. package/dist/cjs/tokens/scale.js +45 -2
  89. package/dist/cjs/tokens/semantic.js +26 -0
  90. package/dist/columncalc.css +15 -7
  91. package/dist/columncalc.layered.css +15 -7
  92. package/dist/command.css +23 -19
  93. package/dist/command.layered.css +23 -19
  94. package/dist/comments.css +28 -19
  95. package/dist/comments.d.ts +2 -2
  96. package/dist/comments.layered.css +28 -19
  97. package/dist/datatable.css +161 -67
  98. package/dist/datatable.layered.css +161 -67
  99. package/dist/datepicker.css +13 -41
  100. package/dist/datepicker.layered.css +13 -41
  101. package/dist/datepickerfield.css +4 -1
  102. package/dist/datepickerfield.layered.css +4 -1
  103. package/dist/dialog.css +12 -7
  104. package/dist/dialog.layered.css +12 -7
  105. package/dist/emptystate.css +5 -3
  106. package/dist/emptystate.layered.css +5 -3
  107. package/dist/esm/components/accentpicker/accentpicker.js +30 -0
  108. package/dist/esm/components/accentpicker/accentpicker.recipe.js +22 -0
  109. package/dist/esm/components/accentpicker/index.js +2 -0
  110. package/dist/esm/components/avatar/avatar.recipe.js +4 -1
  111. package/dist/esm/components/badge/badge.recipe.js +38 -10
  112. package/dist/esm/components/bottomsheet/bottomsheet.js +52 -0
  113. package/dist/esm/components/bottomsheet/bottomsheet.recipe.js +74 -0
  114. package/dist/esm/components/bottomsheet/index.js +2 -0
  115. package/dist/esm/components/button/button.recipe.js +56 -22
  116. package/dist/esm/components/checkbox/checkbox.recipe.js +61 -9
  117. package/dist/esm/components/chip/chip.js +14 -2
  118. package/dist/esm/components/chip/chip.recipe.js +69 -1
  119. package/dist/esm/components/chipgroup/chipgroup.js +15 -0
  120. package/dist/esm/components/chipgroup/chipgroup.recipe.js +33 -0
  121. package/dist/esm/components/chipgroup/index.js +2 -0
  122. package/dist/esm/components/datatable/datatable.js +5 -1
  123. package/dist/esm/components/datatable/datatable.recipe.js +11 -2
  124. package/dist/esm/components/dialog/dialog.recipe.js +22 -11
  125. package/dist/esm/components/emptystate/emptystate.recipe.js +7 -2
  126. package/dist/esm/components/fileupload/fileupload.js +20 -5
  127. package/dist/esm/components/input/index.js +1 -1
  128. package/dist/esm/components/input/input.js +33 -3
  129. package/dist/esm/components/input/input.recipe.js +23 -8
  130. package/dist/esm/components/menu/menu.recipe.js +21 -7
  131. package/dist/esm/components/menusheet/index.js +2 -0
  132. package/dist/esm/components/menusheet/menusheet.js +95 -0
  133. package/dist/esm/components/menusheet/menusheet.recipe.js +100 -0
  134. package/dist/esm/components/multiselect/multiselect.js +2 -2
  135. package/dist/esm/components/multiselect/multiselect.recipe.js +45 -12
  136. package/dist/esm/components/progress/progress.js +22 -5
  137. package/dist/esm/components/progress/progress.recipe.js +30 -0
  138. package/dist/esm/components/select/select.js +3 -3
  139. package/dist/esm/components/select/select.recipe.js +63 -10
  140. package/dist/esm/components/snackbar/index.js +2 -0
  141. package/dist/esm/components/snackbar/snackbar.js +84 -0
  142. package/dist/esm/components/snackbar/snackbar.recipe.js +68 -0
  143. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
  144. package/dist/esm/components/tabletoolbar/tabletoolbar.js +37 -12
  145. package/dist/esm/components/tabs/tabs.js +10 -4
  146. package/dist/esm/components/tabs/tabs.recipe.js +23 -6
  147. package/dist/esm/components/text/text.recipe.js +8 -7
  148. package/dist/esm/components/textarea/textarea.js +3 -2
  149. package/dist/esm/components/textarea/textarea.recipe.js +14 -10
  150. package/dist/esm/components/textlink/textlink.recipe.js +6 -3
  151. package/dist/esm/index.js +8 -0
  152. package/dist/esm/product/authshell/authshell.recipe.js +5 -1
  153. package/dist/esm/product/command/command.recipe.js +4 -1
  154. package/dist/esm/product/comments/comments.js +7 -2
  155. package/dist/esm/product/comments/comments.recipe.js +25 -7
  156. package/dist/esm/product/memberssettings/index.js +1 -0
  157. package/dist/esm/product/memberssettings/memberssettings.js +62 -0
  158. package/dist/esm/product/memberssettings/memberssettings.recipe.js +80 -0
  159. package/dist/esm/product/notificationssettings/index.js +1 -0
  160. package/dist/esm/product/notificationssettings/notificationssettings.js +16 -0
  161. package/dist/esm/product/notificationssettings/notificationssettings.recipe.js +41 -0
  162. package/dist/esm/product/recordpage/recordpage.js +102 -10
  163. package/dist/esm/product/recordpage/recordpage.recipe.js +43 -6
  164. package/dist/esm/product/workspacegeneralsettings/index.js +1 -0
  165. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.js +18 -0
  166. package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +48 -0
  167. package/dist/esm/tokens/contract.js +2 -1
  168. package/dist/esm/tokens/elevation.js +8 -5
  169. package/dist/esm/tokens/palette.js +107 -33
  170. package/dist/esm/tokens/scale.js +44 -1
  171. package/dist/esm/tokens/semantic.js +26 -0
  172. package/dist/fileupload.css +3 -1
  173. package/dist/fileupload.layered.css +3 -1
  174. package/dist/input.css +11 -2
  175. package/dist/input.layered.css +11 -2
  176. package/dist/listpage.css +3 -1
  177. package/dist/listpage.layered.css +3 -1
  178. package/dist/memberssettings.css +67 -0
  179. package/dist/memberssettings.d.ts +7 -0
  180. package/dist/memberssettings.layered.css +69 -0
  181. package/dist/menu.css +2 -2
  182. package/dist/menu.layered.css +2 -2
  183. package/dist/menusheet.css +36 -0
  184. package/dist/menusheet.d.ts +7 -0
  185. package/dist/menusheet.layered.css +38 -0
  186. package/dist/multiselect.css +17 -6
  187. package/dist/multiselect.d.ts +2 -2
  188. package/dist/multiselect.layered.css +17 -6
  189. package/dist/native/tokens.d.ts +2 -1
  190. package/dist/native/tokens.js +53 -25
  191. package/dist/notificationssettings.css +22 -0
  192. package/dist/notificationssettings.d.ts +7 -0
  193. package/dist/notificationssettings.layered.css +24 -0
  194. package/dist/objectsettings.css +4 -4
  195. package/dist/objectsettings.layered.css +4 -4
  196. package/dist/progress.css +3 -0
  197. package/dist/progress.layered.css +3 -0
  198. package/dist/recordpage.css +67 -23
  199. package/dist/recordpage.layered.css +67 -23
  200. package/dist/recordpanel.css +35 -16
  201. package/dist/recordpanel.layered.css +35 -16
  202. package/dist/resizablepanel.css +8 -5
  203. package/dist/resizablepanel.layered.css +8 -5
  204. package/dist/richtext.css +24 -8
  205. package/dist/richtext.layered.css +24 -8
  206. package/dist/savedviews.css +9 -2
  207. package/dist/savedviews.layered.css +9 -2
  208. package/dist/select.css +47 -7
  209. package/dist/select.d.ts +2 -2
  210. package/dist/select.layered.css +47 -7
  211. package/dist/settingsshell.css +2 -3
  212. package/dist/settingsshell.layered.css +2 -3
  213. package/dist/slider.css +6 -27
  214. package/dist/slider.layered.css +6 -27
  215. package/dist/snackbar.css +24 -0
  216. package/dist/snackbar.d.ts +7 -0
  217. package/dist/snackbar.layered.css +26 -0
  218. package/dist/statcard.css +3 -1
  219. package/dist/statcard.layered.css +3 -1
  220. package/dist/styles.css +927 -365
  221. package/dist/styles.layered.css +927 -365
  222. package/dist/table.css +28 -10
  223. package/dist/table.layered.css +28 -10
  224. package/dist/tabletoolbar.css +35 -24
  225. package/dist/tabletoolbar.layered.css +35 -24
  226. package/dist/tabs.css +18 -4
  227. package/dist/tabs.layered.css +18 -4
  228. package/dist/text.css +1 -1
  229. package/dist/text.layered.css +1 -1
  230. package/dist/textarea.css +4 -7
  231. package/dist/textarea.layered.css +4 -7
  232. package/dist/textlink.css +2 -2
  233. package/dist/textlink.layered.css +2 -2
  234. package/dist/themepicker.css +2 -1
  235. package/dist/themepicker.layered.css +2 -1
  236. package/dist/tokens.css +31 -13
  237. package/dist/tokens.json +53 -25
  238. package/dist/types/components/accentpicker/accentpicker.d.ts +23 -0
  239. package/dist/types/components/accentpicker/accentpicker.recipe.d.ts +3 -0
  240. package/dist/types/components/accentpicker/index.d.ts +2 -0
  241. package/dist/types/components/bottomsheet/bottomsheet.d.ts +35 -0
  242. package/dist/types/components/bottomsheet/bottomsheet.recipe.d.ts +3 -0
  243. package/dist/types/components/bottomsheet/index.d.ts +2 -0
  244. package/dist/types/components/button/button.recipe.d.ts +13 -8
  245. package/dist/types/components/checkbox/checkbox.recipe.d.ts +28 -5
  246. package/dist/types/components/chip/chip.d.ts +22 -0
  247. package/dist/types/components/chip/chip.recipe.d.ts +48 -0
  248. package/dist/types/components/chipgroup/chipgroup.d.ts +14 -0
  249. package/dist/types/components/chipgroup/chipgroup.recipe.d.ts +5 -0
  250. package/dist/types/components/chipgroup/index.d.ts +2 -0
  251. package/dist/types/components/datatable/datatable.d.ts +19 -0
  252. package/dist/types/components/fileupload/fileupload.d.ts +26 -0
  253. package/dist/types/components/input/index.d.ts +1 -1
  254. package/dist/types/components/input/input.d.ts +84 -0
  255. package/dist/types/components/menusheet/index.d.ts +2 -0
  256. package/dist/types/components/menusheet/menusheet.d.ts +42 -0
  257. package/dist/types/components/menusheet/menusheet.recipe.d.ts +3 -0
  258. package/dist/types/components/multiselect/multiselect.d.ts +4 -3
  259. package/dist/types/components/multiselect/multiselect.recipe.d.ts +3 -0
  260. package/dist/types/components/progress/progress.d.ts +16 -4
  261. package/dist/types/components/progress/progress.recipe.d.ts +22 -0
  262. package/dist/types/components/select/select.d.ts +6 -5
  263. package/dist/types/components/select/select.recipe.d.ts +3 -0
  264. package/dist/types/components/snackbar/index.d.ts +2 -0
  265. package/dist/types/components/snackbar/snackbar.d.ts +36 -0
  266. package/dist/types/components/snackbar/snackbar.recipe.d.ts +5 -0
  267. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -1
  268. package/dist/types/components/tabs/tabs.d.ts +11 -1
  269. package/dist/types/components/textarea/textarea.d.ts +16 -0
  270. package/dist/types/index.d.ts +8 -0
  271. package/dist/types/product/comments/comments.d.ts +9 -3
  272. package/dist/types/product/comments/comments.recipe.d.ts +12 -2
  273. package/dist/types/product/memberssettings/index.d.ts +1 -0
  274. package/dist/types/product/memberssettings/memberssettings.d.ts +85 -0
  275. package/dist/types/product/memberssettings/memberssettings.recipe.d.ts +3 -0
  276. package/dist/types/product/notificationssettings/index.d.ts +1 -0
  277. package/dist/types/product/notificationssettings/notificationssettings.d.ts +37 -0
  278. package/dist/types/product/notificationssettings/notificationssettings.recipe.d.ts +3 -0
  279. package/dist/types/product/recordpage/recordpage.d.ts +89 -2
  280. package/dist/types/product/workspacegeneralsettings/index.d.ts +1 -0
  281. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.d.ts +48 -0
  282. package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.recipe.d.ts +3 -0
  283. package/dist/types/tokens/contract.d.ts +7 -1
  284. package/dist/types/tokens/elevation.d.ts +8 -5
  285. package/dist/types/tokens/palette.d.ts +28 -12
  286. package/dist/types/tokens/scale.d.ts +12 -1
  287. package/dist/types/tokens/semantic.d.ts +17 -0
  288. package/dist/wheelpicker.css +4 -2
  289. package/dist/wheelpicker.layered.css +4 -2
  290. package/dist/workspacegeneralsettings.css +30 -0
  291. package/dist/workspacegeneralsettings.d.ts +7 -0
  292. package/dist/workspacegeneralsettings.layered.css +32 -0
  293. package/package.json +41 -1
@@ -0,0 +1,48 @@
1
+ import { defineRecipe, resolveRecipe } from "../../core/index.js";
2
+ import { contract as c } from "../../tokens/index.js";
3
+ // Measured live against app.attio.com/<workspace>/settings/workspace
4
+ // (crisp#638, P-128). Logo tile + Name/Slug fields side by side, an
5
+ // "Export workspace data" section (header row + Type/Date table + empty
6
+ // state), and a "Danger zone" section (a red-tinted bordered row).
7
+ const config = defineRecipe({
8
+ name: "workspacegeneralsettings",
9
+ base: `& .crisp-workspacegeneralsettings-page { display: flex; flex-direction: column; gap: ${c.space["6"]}; font-family: ${c.font.sans}; }
10
+ & .crisp-workspacegeneralsettings-heading { display: flex; flex-direction: column; gap: ${c.space["0.5"]}; }
11
+ & .crisp-workspacegeneralsettings-title { font-size: 24px; line-height: 28px; font-weight: ${c.weight.semibold}; letter-spacing: -0.48px; color: ${c.fgPrimary}; margin: 0; }
12
+ & .crisp-workspacegeneralsettings-desc { font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgMuted}; margin: 0; }
13
+
14
+ & .crisp-workspacegeneralsettings-section { display: flex; flex-direction: column; gap: ${c.space["4"]}; }
15
+ & .crisp-workspacegeneralsettings-sectiontitle { font-size: 16px; line-height: 20px; font-weight: ${c.weight.semibold}; color: ${c.fgPrimary}; margin: 0; }
16
+ & .crisp-workspacegeneralsettings-sectionhead { display: flex; align-items: flex-start; justify-content: space-between; gap: ${c.space["4"]}; }
17
+ & .crisp-workspacegeneralsettings-sectionhead-text { display: flex; flex-direction: column; gap: ${c.space["1"]}; }
18
+
19
+ /* Logo row: a tile + helper text + "Upload logo" button. */
20
+ & .crisp-workspacegeneralsettings-logorow { display: flex; align-items: center; gap: ${c.space["4"]}; }
21
+ & .crisp-workspacegeneralsettings-logohelp { font-size: ${c.text.sm}; color: ${c.fgMuted}; }
22
+ & .crisp-workspacegeneralsettings-logotext { display: flex; flex-direction: column; align-items: flex-start; gap: ${c.space["1"]}; }
23
+
24
+ /* Name + Slug sit side by side, each ~half the content width. */
25
+ & .crisp-workspacegeneralsettings-fieldrow { display: flex; gap: ${c.space["4"]}; }
26
+ & .crisp-workspacegeneralsettings-fieldrow > * { flex: 1; min-width: 0; }
27
+ & .crisp-workspacegeneralsettings-slugfield { display: flex; align-items: center; gap: ${c.space["1.5"]}; }
28
+ & .crisp-workspacegeneralsettings-slugfield > :first-child { flex: 1; min-width: 0; }
29
+ & .crisp-workspacegeneralsettings-copybtn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 32px; height: 32px; border: none; border-radius: ${c.radius.md}; background: none; font-family: inherit; color: ${c.fgMuted}; cursor: pointer; }
30
+ & .crisp-workspacegeneralsettings-copybtn:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
31
+
32
+ /* Export-data empty state -- measured: 14/500/63%-opacity, centred in a
33
+ 40px row, no border of its own (the Table wrapper already draws it). */
34
+ & .crisp-workspacegeneralsettings-emptyrow { padding: ${c.space["2.5"]} ${c.space["4"]}; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); text-align: center; }
35
+
36
+ /* Danger zone: a red-tinted bordered row, distinct from every other
37
+ card on the page. */
38
+ & .crisp-workspacegeneralsettings-dangerrow { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["6"]}; padding: ${c.space["3"]} ${c.space["4"]}; border: 1px solid color-mix(in srgb, ${c.bgDanger} 30%, transparent); border-radius: 12px; background: color-mix(in srgb, ${c.bgDanger} 6%, transparent); }
39
+ & .crisp-workspacegeneralsettings-dangertext { display: flex; flex-direction: column; gap: ${c.space["1"]}; }
40
+ & .crisp-workspacegeneralsettings-rowtitle { font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
41
+ & .crisp-workspacegeneralsettings-dangerwarning { font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); }
42
+ `,
43
+ variants: {},
44
+ defaultVariants: {},
45
+ compoundVariants: [],
46
+ });
47
+ export const workspacegeneralsettingsRecipe = resolveRecipe(config);
48
+ export default config;
@@ -1,5 +1,5 @@
1
1
  import { VAR_PREFIX } from "../core/namespace.js";
2
- import { font as fontScale, radius as radiusScale, size as sizeScale, space as spaceScale, text as textScale, weight as weightScale, } from "./scale.js";
2
+ import { font as fontScale, radius as radiusScale, size as sizeScale, space as spaceScale, text as textScale, tracking as trackingScale, weight as weightScale, } from "./scale.js";
3
3
  import { semantic } from "./semantic.js";
4
4
  const kebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
5
5
  const toVar = (key) => `var(--${VAR_PREFIX}${kebab(key)})`;
@@ -16,4 +16,5 @@ export const contract = Object.assign(colors, {
16
16
  font: mapScale("font", fontScale),
17
17
  text: mapScale("text", textScale),
18
18
  weight: mapScale("weight", weightScale),
19
+ tracking: mapScale("tracking", trackingScale),
19
20
  });
@@ -12,9 +12,11 @@ import { contract as c } from "./contract.js";
12
12
  */
13
13
  export const elevation = {
14
14
  /**
15
- * Tier 1 — anything floating and *attached*: menus, popovers, dropdowns,
15
+ * Tier 1 — anything floating and *attached*: popovers, dropdowns,
16
16
  * hover-preview cards, the floating sidebar, the record peek, drawers, the
17
17
  * bulk bar. Pairs with a 10–12px radius and, for panels, an 8px gutter.
18
+ * NOT `Menu` (your-moon/crisp#934) -- that moved to
19
+ * `selectFloating`, seed-design's own flat shadow, below.
18
20
  */
19
21
  floating: `inset 0 0 0 1px ${c.ringPopover}, 0 0 0 1px ${c.shadowPopover1}, 0 4px 8px -4px ${c.shadowPopover2}, 0 4px 12px -2px ${c.shadowPopover3}`,
20
22
  /**
@@ -57,10 +59,11 @@ export const elevation = {
57
59
  */
58
60
  fieldNoRing: `0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04)`,
59
61
  /**
60
- * Select's own listbox shadow (crisp/#867) — seed-design's `$shadow.s3`, a
61
- * single layer, NOT `floating` above. This component follows seed, not
62
- * Attio, for the same reason `DatePicker` follows seed's calendar
63
- * structure: an explicit owner request to match seed-design exactly.
62
+ * Seed-design's own `$shadow.s3`, a single flat layer, NOT `floating`
63
+ * above. Originally Select's own listbox shadow (crisp/#867); also `Menu`'s
64
+ * (your-moon/crisp#934) -- the raw seed dump confirms Menu
65
+ * is the one component seed's own spec gives a shadow to at all, and it's
66
+ * this exact value. Despite the name, not exclusive to Select.
64
67
  */
65
68
  selectFloating: `0 4px 16px ${c.shadowSelect}`,
66
69
  };
@@ -5,9 +5,14 @@ export const palette = {
5
5
  // on a bright tone fill. Same value as gray900's light side (the measured
6
6
  // near-black), named separately because its job is not "primary foreground".
7
7
  ink: { light: "#101112", dark: "#101112" },
8
- gray50: { light: "#f4f4f5", dark: "#101112" },
8
+ // seed-identity migration (your-moon/crisp#890): seed's bg.layer-default-
9
+ // pressed / gray-100. Light side only — dark is untouched, unmeasured
10
+ // Attio territory this migration doesn't scope into.
11
+ gray50: { light: "#f7f8f9", dark: "#101112" },
9
12
  gray900: { light: "#101112", dark: "#f5f5f5" },
10
- line200: { light: "#e4e4e7", dark: "#212224" },
13
+ // seed-identity migration (your-moon/crisp#890): seed's stroke.neutral-
14
+ // weak / gray-400. Light side only, same scoping as `gray50` above.
15
+ line200: { light: "#dcdee3", dark: "#212224" },
11
16
  line100: { light: "#eeeff1", dark: "#1c1d1f" },
12
17
  // ImportDialog's dropzone border — measured live from Attio's Import CSV
13
18
  // flow (`rgb(230,231,234)`), distinct from `line200`/`line100` (#e4e4e7 /
@@ -19,8 +24,11 @@ export const palette = {
19
24
  line150: { light: "#e6e7ea", dark: "#212224" },
20
25
  // The filter chip's field/value segments while the condition is
21
26
  // incomplete (no value typed yet) — measured live on Attio's own filter
22
- // chip (`rgb(246,247,247)`), lighter than `gray50` (#f4f4f5, close but not
23
- // equal). The chip's operator/kebab segments stay `gray00` in the same
27
+ // chip (`rgb(246,247,247)`), close to but not equal to `gray50`
28
+ // (`#f7f8f9` since the seed-identity migration, your-moon/crisp#890 — the
29
+ // two used to differ the other direction, gray25 lighter; the relative
30
+ // order isn't the point, staying off `gray50` while remaining a
31
+ // near-neighbour is). The chip's operator/kebab segments stay `gray00` in the same
24
32
  // state; only the two text-content segments carry this muted fill. Dark
25
33
  // value not independently re-measured (Attio's dark theme is off-limits to
26
34
  // toggle) — reuses `gray50`'s dark value, the nearest existing "muted
@@ -55,14 +63,34 @@ export const palette = {
55
63
  ringBrandSoft: { light: "#d6e5ff", dark: "#212224" },
56
64
  tileActive: { light: "#ffffff", dark: "#101112" },
57
65
  tileActiveShadow: { light: "rgba(0,0,0,0.04)", dark: "rgba(0,0,0,0)" },
58
- // Dialog's page-dimming backdrop (crisp#677) — measured live behind the
59
- // Create-attribute FormDialog, both themes: `rgba(0,0,0,.25)` light,
60
- // `rgba(0,0,0,.55)` dark. `Dialog`'s own backdrop had shipped fully
61
- // `transparent` (AUTHORING's Lesson 14 measured the command palette has
62
- // NO dark backdrop and that got generalized to every `Dialog`, which this
63
- // corrects — the palette is a different surface from a plain content
64
- // dialog and really does dim the page).
65
- bgScrim: { light: "rgba(0,0,0,0.25)", dark: "rgba(0,0,0,0.55)" },
66
+ // Dialog's page-dimming backdrop (crisp#677, Attio's own measured value:
67
+ // `rgba(0,0,0,.25)` light, `rgba(0,0,0,.55)` dark — `Dialog`'s own backdrop
68
+ // had shipped fully `transparent`, AUTHORING's Lesson 14 measured the
69
+ // command palette has NO dark backdrop and that got generalized to every
70
+ // `Dialog`, which this corrected).
71
+ //
72
+ // your-moon/crisp#934: light theme moved to seed-design's own scrim
73
+ // (`#00000074` ≈ 45% black, `bg.overlay`) — the owner's flat-dialog
74
+ // directive names "seed's own scrim" specifically, now that elevation is
75
+ // conveyed by the scrim alone (no more frosted-glass shadow/blur). Dark
76
+ // theme's `.55` is UNCHANGED: seed's own token system has no separate
77
+ // dark-theme scrim value to adopt, and `.55` wasn't found wrong -- only
78
+ // light had a sourced replacement value to move to.
79
+ bgScrim: { light: "rgba(0,0,0,0.45)", dark: "rgba(0,0,0,0.55)" },
80
+ // seed-design's `$color.bg.overlay` (BottomSheet/MenuSheet's own backdrop,
81
+ // crisp#911) — kept as its OWN token rather than folded into `bgScrim`
82
+ // above, even though #934 (landed after this one was added) moved
83
+ // `bgScrim`'s LIGHT value to the same `#00000074`: `bgOverlayStrong` is
84
+ // seed's literally theme-CONSTANT value (`color.yaml` gives the same hex
85
+ // for both `theme-light` and `theme-dark`), while `bgScrim` still differs
86
+ // by theme (`.45`/`.55`) -- the two tokens express different design
87
+ // decisions that currently coincide in one theme only.
88
+ bgOverlayStrong: { light: "#00000074", dark: "#00000074" },
89
+ // seed-design's `$color.stroke.neutral-muted` (MenuSheet group divider,
90
+ // crisp#911) -- a faint alpha hairline between grouped rows, theme-flipping
91
+ // (`color.yaml`: `static-black-alpha-300` light / `static-white-alpha-100`
92
+ // dark).
93
+ strokeGroupDivider: { light: "#00000010", dark: "#ffffff17" },
66
94
  // The discard-confirm's description line (crisp#677, `Dialog`'s new
67
95
  // `variant="alert"`) — measured live, both themes, over `popoverBg`: a
68
96
  // theme-relative alpha rather than a flat gray, so it stays legible if
@@ -81,25 +109,54 @@ export const palette = {
81
109
  shadowPopover2: { light: "rgba(28,40,64,0.12)", dark: "rgba(0,0,0,0.24)" },
82
110
  shadowPopover3: { light: "rgba(24,41,75,0.16)", dark: "rgba(0,0,0,0.32)" },
83
111
  gray400: { light: "#71717a", dark: "#9a9aa2" },
84
- brand600: { light: "#266df0", dark: "#266df0" },
85
- brand700: { light: "#1f5fd6", dark: "#1f5fd6" },
86
- red600: { light: "#e5484d", dark: "#ff5a5f" },
87
- // A red dark enough to carry white text. red600 cannot: white on it is
88
- // 3.91:1 light / 3.05:1 dark, and the solid button's hover DARKENS the fill
89
- // (filter: brightness), so dark ink fails there instead (4.49 at hover).
90
- // Measured from the reference stylesheet, which ships this red alongside the
91
- // #EB3B3B it uses for destructive menu TEXT. Does not flip: a filled
92
- // destructive control needs the same white-bearing fill in both themes.
93
- red700: { light: "#ba2525", dark: "#ba2525" },
94
- // The #EB3B3B named in red700's own comment above (destructive menu text) —
95
- // it had never actually been given a token. Independently re-measured live
96
- // on `app.attio.com/.../tasks`, `rgb(235,59,59)`, as the overdue due-date
97
- // text colour on the Tasks page (crisp#462) — same hex, second surface.
98
- // Dark not independently re-measured (Attio's theme toggle is off-limits,
99
- // same restriction noted throughout this file); reuses red600's dark value,
100
- // the same "brightens for legibility on a dark surface" tone this red
101
- // already sits next to, rather than inventing a third red.
102
- red500: { light: "#eb3b3b", dark: "#ff5a5f" },
112
+ // seed-identity migration (your-moon/crisp#890, "Attio structure, seed
113
+ // look"): the owner's TechPartners primary (#0298D9, sourced from the
114
+ // official logo SVG + the live site's own compiled CSS — see
115
+ // ~/grc-parity/seed-identity-spec.md §1) replaces the old measured-from-
116
+ // Attio brand blue, built as seed's own carrot-scale RELATIVE lightness
117
+ // shape around that exact hex at step 600 ("Method A" in the spec — the
118
+ // verified logo hex stays literally in the scale, at the step that's
119
+ // actually rendered, rather than a mathematically tidier neighbour).
120
+ // Does not flip with theme, matching this token's own prior precedent.
121
+ brand600: { light: "#0298d9", dark: "#0298d9" },
122
+ // Method A's raw carrot-shape output (L 37.1%) — the hover/pressed-darken
123
+ // step of the ACCENT colour above, same role brand700 has always had.
124
+ // Not tuned for any particular contrast target; see `brandContrast` below
125
+ // for the token that carries that job.
126
+ brand700: { light: "#0283bb", dark: "#0283bb" },
127
+ // your-moon/crisp#897: #0298d9 (the verified brand hex, kept exactly as
128
+ // the ACCENT everywhere else — focus rings, selected states, icons,
129
+ // tints) only clears 3.23:1 against white, short of the 4.5:1 a SOLID
130
+ // fill carrying white text needs (Button primary, Badge solid+brand,
131
+ // Avatar's brand tone), or brand-coloured TEXT sitting directly on a white
132
+ // page (TextLink/Text's brand tone). This is that darker, AA-safe step —
133
+ // same hue (198°), L dropped to 34% — clearing 4.88:1 with white. An
134
+ // earlier round of this fix used dark ink text instead of finding this
135
+ // value; that read off-brand (seed's own solid buttons use white) and got
136
+ // reverted in favour of this.
137
+ brandContrast: { light: "#0278ac", dark: "#0278ac" },
138
+ // seed's red-700 (fg.critical) — the accent used for plain danger TEXT
139
+ // (an overdue due-date, a destructive menu label) and for tinting
140
+ // (Chip/Badge's soft-danger `color-mix()` inputs). NOT used for a solid
141
+ // fill paired with white text — see `red700` below for why that split
142
+ // matters here specifically.
143
+ red600: { light: "#fa342c", dark: "#ff5a5f" },
144
+ // seed's red-800 (bg.critical-solid-**pressed**), not red-700
145
+ // (bg.critical-solid-enabled, `red600` above) — deliberately the darker
146
+ // of the two. `red700` is crisp's SOLID destructive fill (Button danger,
147
+ // Badge solid+danger), which pairs with white text and, on Button,
148
+ // DARKENS further on hover/active via a brightness filter (not a second
149
+ // token, per this token's own original comment below this migration note
150
+ // — that invariant is why a dark-enough red matters at all). Seed's own
151
+ // red-700 only clears 3.76:1 with white (measured) — short of AA even
152
+ // before any hover darkening — so reusing crisp's existing "needs to
153
+ // survive getting darker" role means red-800 (5.68:1 at rest, improving
154
+ // through hover/active) is the correct step, not seed's literal
155
+ // "enabled" one. `red500` below is pure text again (no fill/contrast
156
+ // constraint), so it stays on the
157
+ // lighter red-700, matching seed exactly.
158
+ red700: { light: "#ca1d13", dark: "#ba2525" },
159
+ red500: { light: "#fa342c", dark: "#ff5a5f" },
103
160
  // seed-design's field focus ring (stroke-neutral-contrast = gray-1000),
104
161
  // measured from @seed-design/css. A high-contrast neutral rather than a brand
105
162
  // hue: the ring reads as "this field has focus", not as a state colour.
@@ -119,8 +176,25 @@ export const palette = {
119
176
  neutralSubtle: { light: "#868b94", dark: "#b0b3ba" },
120
177
  neutralMuted: { light: "#555d6d", dark: "#dcdee3" },
121
178
  focusRing: { light: "#1e82eb", dark: "#5e98fe" },
122
- green600: { light: "#1f9d57", dark: "#2ea968" },
123
- amber600: { light: "#b5730f", dark: "#e0982a" },
179
+ // seed's fg.positive / bg.positive-solid (green-700).
180
+ green600: { light: "#079171", dark: "#2ea968" },
181
+ // seed's fg.warning / stroke.warning-solid (yellow-700) — the ACCENT used
182
+ // for tinting (Badge/Chip's `soft()`/`ring()` colour-mix formulas), not
183
+ // seed's actual solid-fill colour, which is a much paler yellow at a
184
+ // different scale step — see `amberSolid` below for why the two need to
185
+ // stay separate tokens.
186
+ amber600: { light: "#9b7821", dark: "#e0982a" },
187
+ // seed's bg.warning-solid (yellow-300) — genuinely NOT amber600 tinted
188
+ // lighter: seed's warning-solid is its own pale scale step, distinct from
189
+ // the darker accent every other tone's solid fill derives from (see the
190
+ // spec's Badge section). Badge's `variant="solid" tone="warning"` uses
191
+ // this directly, unmixed, the same way `red700`'s solid destructive fill
192
+ // is used directly rather than computed from a tint. Dark side is NOT
193
+ // independently sourced (this is a new token; no prior Attio measurement
194
+ // exists to preserve) — reuses amber600's own dark value as the nearest
195
+ // existing "warning, dark theme" tone, same convention this file uses
196
+ // throughout for an unmeasured dark side (ringSwitch, line150, gray25).
197
+ amberSolid: { light: "#fbdc65", dark: "#e0982a" },
124
198
  orange600: { light: "#f97514", dark: "#f97514" },
125
199
  cyan600: { light: "#00b9eb", dark: "#00b9eb" },
126
200
  purple600: { light: "#9b69ff", dark: "#9b69ff" },
@@ -26,13 +26,23 @@ export const space = {
26
26
  // Corner radius. sm/md/lg are the three used on stacked surfaces
27
27
  // (inputs, rows, tiles); full is the pill.
28
28
  export const radius = {
29
- chip: "7px", // tag / chip corner — the single most used radius
29
+ xs: "4px", // seed-design's $radius.r1 — small badge / checkbox box corner
30
+ // your-moon/crisp#919: was `7px` (Attio's own most-measured
31
+ // radius, 70 occurrences) -- now a pill, matching seed-design's own
32
+ // `chip.yaml` (`$radius.full` at every size, no size-based radius step at
33
+ // all, unlike Button/Badge). Same numeric value as `full` below, kept as
34
+ // its own named token because every one of this token's ~9 consumers
35
+ // (Chip, DataTable/RecordPage/RecordsTable/MultiSelect's own tag/mention
36
+ // rules, TableToolbar/DatePickerField's date-preset chips) reasons about
37
+ // "the chip corner" as its own concept, not literally "a generic pill".
38
+ chip: "999px",
30
39
  sm: "6px", // chips, badges
31
40
  md: "8px", // inputs, search box
32
41
  lg: "9px", // rows, tiles, list items
33
42
  xl: "12px", // overlay items (command rows)
34
43
  "2xl": "16px", // overlay panels (command surface)
35
44
  "3xl": "20px", // seed-design's $radius.r5 — Select's listbox corner (crisp/#867)
45
+ "4xl": "24px", // seed-design's $radius.r6 — BottomSheet's top corner radius (crisp/#911)
36
46
  full: "999px", // pills
37
47
  };
38
48
  // Component sizing — recurring measured dimensions that are neither spacing nor
@@ -84,3 +94,36 @@ export const weight = {
84
94
  semibold: "600",
85
95
  bold: "700", // seed-design's font-weight-bold, for the seed-styled calendar
86
96
  };
97
+ // Size-based letter-spacing ramp for DISPLAY/HEADING type (18px and up —
98
+ // `text.*` above tops out at 16px and stays untracked). crisp's five
99
+ // already-shipped headings tighten as they grow — authshell 20px/-0.4px (2%
100
+ // of size), objectsettings 24px/-0.48px (2%), richtext 32px/-0.6px (1.875%)
101
+ // — all Attio-measured (Inter) and left exactly as they are; this token
102
+ // never touches them, or the button (-0.07px) / table-cell (-0.14px)
103
+ // tracking, which are body sizes, not display type. (statcard's 28px/-0.4px,
104
+ // 1.43%, is a looser outlier — it sets tabular numerals, not prose — and is
105
+ // also left alone.)
106
+ //
107
+ // crisp#909: the study at docs/study/korean/ observed that Naver and Kakao
108
+ // both tighten tracking as type gets bigger, and crisp's OWN large type
109
+ // (EmptyState's 20px title, Dialog's 18px alert title, and any future
110
+ // heading) had no tracking at all — it read as default, not set. The study
111
+ // supplied the PATTERN only, never a number: Naver/Kakao's own values are
112
+ // Korean-specific and were not copied. The numbers below come from rendering
113
+ // real Mongolian Cyrillic ("Эрсдэлийн бүртгэл", "Хяналтын самбар") in VK Sans
114
+ // Display — crisp's own font override, see `font.sans` above — at every
115
+ // display size crisp uses today (18/20/24/26/28/32px), screenshotting six
116
+ // candidates per size (0%, 1%, 1.5%, 2%, 2.5%, 3% of the font-size) and
117
+ // checking for glyph collision. None collided even at 3%, so the pre-existing
118
+ // 2%-of-size rule the five Attio headings already happen to follow was kept
119
+ // as the ramp, rounded to the nearest 0.01px — it reads as deliberately set
120
+ // with headroom to spare, and keeps new display type visually consistent
121
+ // with the Attio-measured headings already shipped.
122
+ export const tracking = {
123
+ "18": "-0.36px",
124
+ "20": "-0.4px",
125
+ "24": "-0.48px",
126
+ "26": "-0.52px",
127
+ "28": "-0.56px",
128
+ "32": "-0.64px",
129
+ };
@@ -1,6 +1,17 @@
1
1
  export const semantic = {
2
2
  bgBrandSolid: "brand600",
3
3
  bgBrandSolidPressed: "brand700",
4
+ /** your-moon/crisp#897: the AA-safe darker sibling of `bgBrandSolid` — a
5
+ * SOLID fill carrying white text (Button primary, Badge solid+brand,
6
+ * Avatar's brand tone). `bgBrandSolid` itself stays the exact brand
7
+ * accent (focus rings, selected states, icons, tints), unpaired with
8
+ * white text anywhere that matters for contrast. */
9
+ bgBrandContrast: "brandContrast",
10
+ /** your-moon/crisp#897: brand-coloured TEXT sitting on a white page
11
+ * (TextLink/Text's `brand` tone) — same value as `bgBrandContrast`,
12
+ * named separately because one's a background and one's a foreground,
13
+ * matching every other fg/bg pair in this file. */
14
+ fgBrandContrast: "brandContrast",
4
15
  fgOnBrand: "white",
5
16
  // text on a solid success/warning/danger fill. Those tone tokens BRIGHTEN in
6
17
  // dark (they are tuned for tinted backgrounds and coloured text), so white on
@@ -27,6 +38,8 @@ export const semantic = {
27
38
  bgTileActive: "tileActive",
28
39
  shadowTileActive: "tileActiveShadow",
29
40
  bgScrim: "bgScrim",
41
+ bgOverlayStrong: "bgOverlayStrong",
42
+ strokeGroupDivider: "strokeGroupDivider",
30
43
  fgAlertDescription: "fgAlertDescription",
31
44
  bgOverlayFrost: "overlayFrost",
32
45
  shadowOverlay1: "shadowOverlay1",
@@ -38,6 +51,15 @@ export const semantic = {
38
51
  shadowPopover2: "shadowPopover2",
39
52
  shadowPopover3: "shadowPopover3",
40
53
  fgPrimary: "gray900",
54
+ // seed-identity migration (your-moon/crisp#890): NOT touched here, on
55
+ // purpose. seed's closest analog (fg.neutral-subtle, #868b94) is lighter
56
+ // than crisp's current #71717a, and Badge's neutral-SOLID variant reuses
57
+ // this exact token as a BACKGROUND paired with white text
58
+ // (`badge.recipe.ts`'s compound variant) — #868b94 drops that pairing to
59
+ // 3.42:1, below AA. Retargeting `fgMuted` and fixing that one background
60
+ // choice belong together in Badge's own upcoming look PR, not split
61
+ // across two PRs where the token change alone would ship a known
62
+ // contrast regression.
41
63
  fgMuted: "gray400",
42
64
  bgDanger: "red600",
43
65
  /** Fill for a SOLID destructive control that carries white text. */
@@ -63,6 +85,10 @@ export const semantic = {
63
85
  strokeFocusRing: "focusRing",
64
86
  bgSuccess: "green600",
65
87
  bgWarning: "amber600",
88
+ /** A SOLID warning fill (Badge's `variant="solid" tone="warning"`) — seed's
89
+ * own warning-solid is a distinct, paler yellow, not `bgWarning` tinted
90
+ * lighter. See `amberSolid` in palette.ts. */
91
+ bgWarningSolid: "amberSolid",
66
92
  bgOrange: "orange600",
67
93
  bgCyan: "cyan600",
68
94
  bgPurple: "purple600",
@@ -25,7 +25,9 @@
25
25
  & .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
26
26
  & .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
27
27
  & .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
28
- /* out of flow while empty, so the always-mounted live region costs no layout: display:none would take it out of the accessibility tree, which is the one thing it exists to be in */
28
+ /* out of flow while empty, so the always-mounted live region costs no
29
+ layout: display:none would take it out of the accessibility tree, which
30
+ is the one thing it exists to be in */
29
31
  & .crisp-fileupload-errors:empty { position: absolute; }
30
32
  & .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
31
33
  }
@@ -26,7 +26,9 @@
26
26
  & .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
27
27
  & .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
28
28
  & .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
29
- /* out of flow while empty, so the always-mounted live region costs no layout: display:none would take it out of the accessibility tree, which is the one thing it exists to be in */
29
+ /* out of flow while empty, so the always-mounted live region costs no
30
+ layout: display:none would take it out of the accessibility tree, which
31
+ is the one thing it exists to be in */
30
32
  & .crisp-fileupload-errors:empty { position: absolute; }
31
33
  & .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
32
34
  }
package/dist/input.css CHANGED
@@ -1,11 +1,20 @@
1
1
  .crisp-input {
2
- position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-field); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
2
+ position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); border: 1px solid var(--crisp-border-default); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
3
3
  &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
4
4
  &:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
5
5
  & .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
6
6
  & .crisp-input-value::placeholder { color: var(--crisp-fg-muted); }
7
7
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
8
8
  &:has(.crisp-input-value:disabled) { opacity: 1; }
9
+ /* crisp#634: measured live on Attio's own Settings > Profile read-only
10
+ primary-email field -- a tinted field, not just muted text, driven off
11
+ the native `readOnly` attribute. `ringField` (rgba(0,0,0,.06)) is an
12
+ exact match for the measured ring; the 2%-fgPrimary tint reuses the
13
+ same color-mix idiom every other soft crisp surface already uses.
14
+ `border-color: transparent` neutralises #910's new real rest border
15
+ so this measured ring isn't drawn twice. */
16
+ &[data-readonly] { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); border-color: transparent; box-shadow: inset 0 0 0 1px var(--crisp-ring-field); }
17
+ & .crisp-input-suffix { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); }
9
18
  }
10
19
  .crisp-input--size_sm {
11
20
  height: var(--crisp-size-row); border-radius: var(--crisp-radius-md);
@@ -16,7 +25,7 @@
16
25
  & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
17
26
  }
18
27
  .crisp-input--size_large {
19
- height: 36px; border-radius: 10px;
28
+ height: 36px; border-radius: var(--crisp-radius-xl);
20
29
  & .crisp-input-value { padding-inline: var(--crisp-space-3); }
21
30
  }
22
31
  .crisp-input--invalid_true {
@@ -1,12 +1,21 @@
1
1
  @layer crisp-components {
2
2
  .crisp-input {
3
- position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-field); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
3
+ position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); border: 1px solid var(--crisp-border-default); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
4
4
  &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
5
5
  &:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
6
6
  & .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
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
+ `border-color: transparent` neutralises #910's new real rest border
16
+ so this measured ring isn't drawn twice. */
17
+ &[data-readonly] { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); border-color: transparent; box-shadow: inset 0 0 0 1px var(--crisp-ring-field); }
18
+ & .crisp-input-suffix { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); }
10
19
  }
11
20
  .crisp-input--size_sm {
12
21
  height: var(--crisp-size-row); border-radius: var(--crisp-radius-md);
@@ -17,7 +26,7 @@
17
26
  & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
18
27
  }
19
28
  .crisp-input--size_large {
20
- height: 36px; border-radius: 10px;
29
+ height: 36px; border-radius: var(--crisp-radius-xl);
21
30
  & .crisp-input-value { padding-inline: var(--crisp-space-3); }
22
31
  }
23
32
  .crisp-input--invalid_true {
package/dist/listpage.css CHANGED
@@ -5,7 +5,9 @@
5
5
  & .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
6
6
  /* #641: trailing (View settings / New) has to reach the toolbar's own
7
7
  inline-end edge on its own -- the only thing pushing it there used
8
- to be .crisp-listpage-count's margin-inline-start: auto, which a consumer with no count badge (TasksTable, measured live: trailing sat right after leading, nowhere near the row's own right edge)
8
+ to be .crisp-listpage-count's margin-inline-start: auto, which a
9
+ consumer with no count badge (TasksTable, measured live: trailing
10
+ sat right after leading, nowhere near the row's own right edge)
9
11
  never renders. A bare margin-inline-start: auto here is safely
10
12
  overridden by the more specific rule right below whenever count IS
11
13
  present, so the two cases don't fight -- count's own auto margin
@@ -6,7 +6,9 @@
6
6
  & .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
7
7
  /* #641: trailing (View settings / New) has to reach the toolbar's own
8
8
  inline-end edge on its own -- the only thing pushing it there used
9
- to be .crisp-listpage-count's margin-inline-start: auto, which a consumer with no count badge (TasksTable, measured live: trailing sat right after leading, nowhere near the row's own right edge)
9
+ to be .crisp-listpage-count's margin-inline-start: auto, which a
10
+ consumer with no count badge (TasksTable, measured live: trailing
11
+ sat right after leading, nowhere near the row's own right edge)
10
12
  never renders. A bare margin-inline-start: auto here is safely
11
13
  overridden by the more specific rule right below whenever count IS
12
14
  present, so the two cases don't fight -- count's own auto margin
@@ -0,0 +1,67 @@
1
+ .crisp-memberssettings {
2
+ & .crisp-memberssettings-page { display: flex; flex-direction: column; gap: var(--crisp-space-6); font-family: var(--crisp-font-sans); }
3
+ & .crisp-memberssettings-heading { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); }
4
+ & .crisp-memberssettings-title { font-size: 24px; line-height: 28px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.48px; color: var(--crisp-fg-primary); margin: 0; }
5
+ & .crisp-memberssettings-desc { font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-muted); margin: 0; }
6
+ /* Segmented Members/Teams switcher -- measured: 28px pills, radius 8,
7
+ 4px gap, 6px icon-label gap, 14/500 label, selected bg bgSubtle +
8
+ inset borderSubtle ring, unselected transparent + a flat 63%-opacity
9
+ fgPrimary mix (measured rgba(0,0,0,.63) on the live page, not the
10
+ solid fgMuted token — same alpha technique this file's table header
11
+ already uses). */
12
+ & .crisp-memberssettings-tabpair { display: inline-flex; align-items: center; gap: 4px; }
13
+ & .crisp-memberssettings-tab { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: none; box-shadow: inset 0 0 0 1px transparent; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease, box-shadow .12s ease; }
14
+ & .crisp-memberssettings-tab svg { flex-shrink: 0; }
15
+ & .crisp-memberssettings-tab:hover:not([data-selected]) { color: var(--crisp-fg-primary); }
16
+ & .crisp-memberssettings-tab[data-selected] { color: var(--crisp-fg-primary); background: var(--crisp-bg-subtle); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); }
17
+ & .crisp-memberssettings-tab:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
18
+ /* Toolbar row: search grows, Filter + Invite sit trailing -- measured
19
+ 32px height on all three, search radius 9 with a 6% ink ring,
20
+ Filter/Invite radius 9 with the button drop-shadow pairing. */
21
+ & .crisp-memberssettings-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
22
+ & .crisp-memberssettings-search { display: flex; align-items: center; flex: 1; min-width: 0; gap: var(--crisp-space-1-5); height: 32px; padding: 0 var(--crisp-space-2-5) 0 var(--crisp-space-2); border-radius: 9px; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-muted); }
23
+ & .crisp-memberssettings-search:focus-within { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
24
+ & .crisp-memberssettings-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
25
+ & .crisp-memberssettings-search input::placeholder { color: var(--crisp-fg-muted); }
26
+ & .crisp-memberssettings-filterbtn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 32px; padding: 6px 10px 6px 8px; border: none; border-radius: 9px; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px transparent, 0 0 2px 0 rgba(28,40,64,.18), 0 1px 3px 0 rgba(0,0,0,.04); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); cursor: pointer; white-space: nowrap; }
27
+ & .crisp-memberssettings-filterbtn:hover { background: var(--crisp-bg-raised); }
28
+ /* your-moon/crisp#979: bgBrandContrast, not bgBrandSolid -- the accent
29
+ (#0298d9) only clears 3.23:1 with white; the darker sibling clears
30
+ 4.5:1+, same fix already applied to Button/Badge/Avatar (#897) and
31
+ TableToolbar/Dialog/Command/Comments/AuthShell (#953/#961), which
32
+ explicitly flagged this button as found-not-fixed. */
33
+ & .crisp-memberssettings-invitebtn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 32px; padding: 6px 10px 6px 8px; border: none; border-radius: 9px; background: var(--crisp-bg-brand-contrast); box-shadow: inset 0 0 0 1px rgba(0,0,0,.06), 0 2px 4px -2px color-mix(in srgb, var(--crisp-bg-brand-contrast) 12%, transparent), 0 3px 6px -2px color-mix(in srgb, var(--crisp-bg-brand-contrast) 8%, transparent); color: var(--crisp-fg-on-brand); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); cursor: pointer; white-space: nowrap; }
34
+ & .crisp-memberssettings-invitebtn:hover { filter: brightness(0.95); }
35
+ /* Table wrapper -- measured card: radius 12, 1px borderSubtle, 40px
36
+ rows, header label 12/500/63%-opacity fgPrimary. */
37
+ & .crisp-memberssettings-table-wrap { border-radius: 12px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
38
+ & .crisp-memberssettings-table { width: 100%; border-collapse: collapse; font-family: inherit; }
39
+ & .crisp-memberssettings-table th { height: 32px; padding: 0 var(--crisp-space-4); text-align: start; font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); border-bottom: 1px solid var(--crisp-border-subtle); background: var(--crisp-bg-surface); }
40
+ & .crisp-memberssettings-table td { height: 40px; padding: 0 var(--crisp-space-4); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); border-bottom: 1px solid var(--crisp-border-subtle); }
41
+ & .crisp-memberssettings-table tr:last-child td { border-bottom: none; }
42
+ & .crisp-memberssettings-user { display: flex; align-items: center; gap: var(--crisp-space-2); }
43
+ & .crisp-memberssettings-avatarimg { width: 24px; height: 24px; border-radius: var(--crisp-radius-full); object-fit: cover; }
44
+ /* Name + email sit on ONE line, not stacked -- confirmed live: both spans
45
+ share the same y and the same 14px size, only the email's colour
46
+ differs (a flat 63%-opacity fgPrimary mix, not a smaller muted font). */
47
+ & .crisp-memberssettings-usermeta { display: flex; align-items: baseline; gap: var(--crisp-space-2); min-width: 0; }
48
+ & .crisp-memberssettings-usermeta > span:first-child { flex-shrink: 0; }
49
+ & .crisp-memberssettings-useremail { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-regular); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
50
+ & .crisp-memberssettings-kebab { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-inline-start: auto; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: pointer; }
51
+ & .crisp-memberssettings-kebab:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
52
+ & .crisp-memberssettings-teamspanel { display: flex; align-items: center; justify-content: center; padding: var(--crisp-space-8) 0; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
53
+ /* Invite dialog fields -- measured 708x386 dialog, radius 16; the
54
+ "Invite as"/"Add to team" selects are 32/34px with a flat 1px
55
+ 6%-ink border and 9/10px radius respectively (Attio's own two
56
+ selects don't share one radius). */
57
+ & .crisp-memberssettings-invitefield { display: flex; flex-direction: column; gap: var(--crisp-space-1-5); }
58
+ & .crisp-memberssettings-invitelabel { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
59
+ }
60
+
61
+ @media (prefers-reduced-motion: reduce) {
62
+ .crisp-memberssettings, .crisp-memberssettings * {
63
+ animation-duration: 0.01ms !important;
64
+ animation-iteration-count: 1 !important;
65
+ transition-duration: 0.01ms !important;
66
+ }
67
+ }
@@ -0,0 +1,7 @@
1
+ import type { RecipeFn } from "../core/types";
2
+
3
+ export interface MemberssettingsVariants {
4
+
5
+ }
6
+
7
+ export declare const memberssettingsRecipe: RecipeFn<{ }>;