@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,7 +1,11 @@
1
1
  .crisp-datatable {
2
- display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans); /* The toolbar carried no horizontal inset of its own, so its content (search box, sort/filter chips) sat flush at the table's outer edge
3
- (x=0) while the header row's own th/td padding (table.recipe.ts, #465) starts every column's text at 12px -- one row down, the SAME
4
- grid's own header cell already begins at that inset. c.space["3"] here just matches the row directly below it, measured live against Attio (the sort/filter row's search chip lines up with the first
2
+ display: flex; flex-direction: column; gap: var(--crisp-space-2-5); font-family: var(--crisp-font-sans); /* The toolbar carried no horizontal inset of its own, so its content
3
+ (search box, sort/filter chips) sat flush at the table's outer edge
4
+ (x=0) while the header row's own th/td padding (table.recipe.ts,
5
+ #465) starts every column's text at 12px -- one row down, the SAME
6
+ grid's own header cell already begins at that inset. c.space["3"]
7
+ here just matches the row directly below it, measured live against
8
+ Attio (the sort/filter row's search chip lines up with the first
5
9
  column header's text, not the grid's raw edge). */
6
10
  & .crisp-datatable-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: 49px; border-top: 1px solid var(--crisp-border-subtle); padding: 0 var(--crisp-space-3); }
7
11
  & .crisp-datatable-search { position: relative; flex: 1; max-width: 280px; display: inline-flex; align-items: center; }
@@ -11,10 +15,7 @@
11
15
  & .crisp-datatable-search input:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
12
16
  & .crisp-datatable-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
13
17
  /* announced, never drawn */
14
- & .crisp-datatable-live {
15
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
16
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
17
- }
18
+ & .crisp-datatable-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
18
19
  /* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and-
19
20
  inert like .crisp-datatable-live above -- same clip-rect shape,
20
21
  this one just needs to actually hold DOM focus so a native
@@ -22,7 +23,8 @@
22
23
  in datatable.tsx). resize: none keeps a stray native textarea handle
23
24
  from ever being drawable, even though it's clipped to 1px anyway. */
24
25
  & .crisp-datatable-clipboard-proxy { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; resize: none; }
25
- /* single active cell (crisp/#645 follow-up -- this had no styling of its own at all before, a gap against Golden Rule 0: RecordsTable's own
26
+ /* single active cell (crisp/#645 follow-up -- this had no styling of its
27
+ own at all before, a gap against Golden Rule 0: RecordsTable's own
26
28
  measured value, .crisp-recordstable-td[data-selected], brought
27
29
  across unchanged now that a visible anchor actually matters for the
28
30
  Shift+Click range below). */
@@ -31,57 +33,91 @@
31
33
  tint formula already used twice below (tbody tr[data-selected] td)
32
34
  for the same reason a selected group of rows gets one: a selected
33
35
  GROUP of cells is the same kind of state, just cell-scoped instead of
34
- row-scoped. Not independently re-measured against Attio's own range highlight (out of scope this slice -- flagged on crisp#645). */
36
+ row-scoped. Not independently re-measured against Attio's own range
37
+ highlight (out of scope this slice -- flagged on crisp#645). */
35
38
  & td[data-in-range] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
36
- /* The header cell has no padding of its own -- the button owns the full cell so the whole header is clickable (its own inset is 12px). Mirrors the summary-row fix. `& .crisp-table th`, not bare `& th` (found while re-checking #691's
39
+ /* The header cell has no padding of its own -- the button owns the
40
+ full cell so the whole header is clickable (its own inset is 12px).
41
+ Mirrors the summary-row fix.
42
+ `& .crisp-table th`, not bare `& th` (found while re-checking #691's
37
43
  own fix live): DataTable's root nests `Table`'s own wrapper, whose
38
44
  `table.recipe.ts` carries `& th, & td { padding: 0 12px }` at the
39
45
  SAME specificity (.crisp-table th vs .crisp-datatable th, both one
40
46
  class + one element) -- a bare `& th` here only won by source
41
- order, which flipped the moment table.recipe.ts's own CSS file happened to land after datatable's in the build (alphabetical:
47
+ order, which flipped the moment table.recipe.ts's own CSS file
48
+ happened to land after datatable's in the build (alphabetical:
42
49
  datatable < table), silently reinstating 12px on every header cell
43
- in the whole product and making every interactive column's button double it to 24px. Scoping through `.crisp-table` adds a second class to the selector (0,2,1 beats 0,1,1) so this can never lose a specificity tie to source order again. `thead th`, not a bare `th` (crisp#829): #779 made the primary column's BODY cell a real `<th scope="row">` for a11y, and a bare
50
+ in the whole product and making every interactive column's button
51
+ double it to 24px. Scoping through `.crisp-table` adds a second
52
+ class to the selector (0,2,1 beats 0,1,1) so this can never lose a
53
+ specificity tie to source order again.
54
+ `thead th`, not a bare `th` (crisp#829): #779 made the primary
55
+ column's BODY cell a real `<th scope="row">` for a11y, and a bare
44
56
  `th` here matches that one too, zeroing an inset nothing else
45
57
  supplies -- the primary column's text sat flush left, one column's
46
58
  cell no longer matching its own header or any sibling `<td>`. Only
47
- the header row's own `<th>` needs zero padding (its sort BUTTON owns the inset); the body row's `<th scope="row">` has no button,
59
+ the header row's own `<th>` needs zero padding (its sort BUTTON
60
+ owns the inset); the body row's `<th scope="row">` has no button,
48
61
  and needs the SAME base 12px (table.recipe.ts, #465) every other
49
62
  cell already gets. */
50
63
  & .crisp-table thead th { padding: 0; }
51
- /* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger): Menu wraps its trigger in a flex div that sizes to its content, so a long header grew past the column instead of filling it -- with nothing left to squeeze, the label's own ellipsis CSS (below) had no properly
52
- -sized box to truncate against, and the th's own overflow hard-clipped it with no "...". Fill the wrapper, scoped to th so Menu's default
64
+ /* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
65
+ Menu wraps its trigger in a flex div that sizes to its content, so a
66
+ long header grew past the column instead of filling it -- with nothing
67
+ left to squeeze, the label's own ellipsis CSS (below) had no properly
68
+ -sized box to truncate against, and the th's own overflow hard-clipped
69
+ it with no "...". Fill the wrapper, scoped to th so Menu's default
53
70
  (correct everywhere else) is untouched. */
54
71
  & th .crisp-menu { width: 100%; height: 100%; }
55
72
  & .crisp-datatable-th-inner { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; }
56
73
  & .crisp-datatable-th-inner > svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
57
74
  /* crisp/#605 follow-up: a header with neither a Menu nor a sort button
58
- (the primary/entity column's now-inert path, or any column with no columnMenu/sortable at all) renders .crisp-datatable-th-inner as a DIRECT child of th instead of nested inside a button -- and loses the button's own 12px inset, drifting left of the td's 12px base inset. Measured live on Attio's own Companies grid: the header label
59
- and the cell's own text land at the exact same x (335px from the viewport, both 40px from the column's left edge) via two different
75
+ (the primary/entity column's now-inert path, or any column with no
76
+ columnMenu/sortable at all) renders .crisp-datatable-th-inner as a
77
+ DIRECT child of th instead of nested inside a button -- and loses
78
+ the button's own 12px inset, drifting left of the td's 12px base
79
+ inset. Measured live on Attio's own Companies grid: the header label
80
+ and the cell's own text land at the exact same x (335px from the
81
+ viewport, both 40px from the column's left edge) via two different
60
82
  padding+icon+gap combinations -- Attio does not leave the inert
61
83
  primary header flush left either. The `>` combinator targets only
62
- the button-less case: Menu's own wrapper (`.crisp-menu`) and the plain sort `button` both put >=1 more level between `th` and this span, so their own 12px (already on the button) isn't doubled. */
84
+ the button-less case: Menu's own wrapper (`.crisp-menu`) and the
85
+ plain sort `button` both put >=1 more level between `th` and this
86
+ span, so their own 12px (already on the button) isn't doubled. */
63
87
  & th > .crisp-datatable-th-inner { box-sizing: border-box; padding: 0 var(--crisp-space-3); }
64
- /* The summary cell has NO padding of its own -- the calculation trigger
88
+ /* The summary cell has NO padding of its own -- the calculation trigger
65
89
  owns the whole 246x37 area so a click anywhere in the cell opens the
66
90
  menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
67
91
  & tfoot td { padding: 0; }
68
- /* Group heading row -- a view is grouped under a subtle label with the bucket's count beside it. */
69
- & .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
92
+ /* Group heading row -- a view is grouped under a subtle label with the
93
+ bucket's count beside it. */
94
+ & .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
70
95
  & .crisp-datatable-viewsettings:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
71
96
  & .crisp-datatable-group-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
72
- & .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
97
+ & .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
73
98
  & .crisp-datatable-group-count { margin-inline-start: var(--crisp-space-1-5); color: var(--crisp-fg-muted); font-variant-numeric: tabular-nums; }
74
99
  & .crisp-datatable-th-rename { width: 100%; min-width: 0; height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-1); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); outline: none; }
75
100
  & .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
76
- & .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
77
- /* #465: 12px per the comment above (and Attio's measured header cell padding: 0 12px) -- was still c.space["2"] (8px), drifted from its own documented value. */
101
+ /* #719: overflow: clip, not hidden, on every truncation rule below --
102
+ hidden still honours a programmatic scrollLeft assignment (found via
103
+ a screenshot tool that pans anything with scrollWidth > clientWidth),
104
+ blanking or fragmenting the visible text even though real user
105
+ scrolling was already blocked. clip renders the ellipsis identically
106
+ and leaves no scroll position to move. */
107
+ & .crisp-datatable-th-label { overflow: clip; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
108
+ /* #465: 12px per the comment above (and Attio's measured header cell
109
+ padding: 0 12px) -- was still c.space["2"] (8px), drifted from its
110
+ own documented value. */
78
111
  & .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
79
112
  /* A right-aligned column's header keeps its field-icon + label packed at
80
113
  the START, matching its own values only in that the VALUES right-align
81
114
  -- Attio's own right-aligned header (measured live: "Twitter follower
82
- count") keeps the icon+label left and only the trailing column-menu icon sits pinned to the right edge, independent of the label's width
83
- (#406, reversing #393's own justify-content: flex-end here). The chevron's own margin-inline-start: auto below already pins IT to the
84
- end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
115
+ count") keeps the icon+label left and only the trailing column-menu
116
+ icon sits pinned to the right edge, independent of the label's width
117
+ (#406, reversing #393's own justify-content: flex-end here). The
118
+ chevron's own margin-inline-start: auto below already pins IT to the
119
+ end of a flex-start row with no override needed -- that's what pins
120
+ the trailing icon on Attio too. */
85
121
  & .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
86
122
  & .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
87
123
  & .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
@@ -89,17 +125,23 @@
89
125
  & th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
90
126
  /* `thead th[data-sticky]`, not a bare `th[data-sticky]` (crisp#848, same
91
127
  bare-`th` family as #829/#833/#835): this z-index:3 was written for
92
- the HEADER's own sticky corner cell (sticky column x sticky header), meant to win over everything else. Unscoped, it also matched the primary column's BODY cell -- a real `<th scope="row" data-sticky>`
93
- since #779 -- giving every scrolled body row's own sticky cell a HIGHER z-index than the sticky FOOTER's (2), so it painted over the
128
+ the HEADER's own sticky corner cell (sticky column x sticky header),
129
+ meant to win over everything else. Unscoped, it also matched the
130
+ primary column's BODY cell -- a real `<th scope="row" data-sticky>`
131
+ since #779 -- giving every scrolled body row's own sticky cell a
132
+ HIGHER z-index than the sticky FOOTER's (2), so it painted over the
94
133
  footer instead of the footer showing through. Confirmed live on a
95
- real bounded-height, 25-row table (GRC's Risk register): scrolled partway, `elementFromPoint` at the footer's own on-screen cell
134
+ real bounded-height, 25-row table (GRC's Risk register): scrolled
135
+ partway, `elementFromPoint` at the footer's own on-screen cell
96
136
  resolved to a body row's `<th>`, not the footer's `<td>`, and that
97
137
  `<th>`'s computed z-index read 3. Scoped to `thead`, a body row's
98
138
  own sticky `<th>` now falls back to the base rule above (z-index 1,
99
- correct -- the same as a plain sticky `<td>`), and the footer's own explicit z-index below is unaffected either way. */
139
+ correct -- the same as a plain sticky `<td>`), and the footer's own
140
+ explicit z-index below is unaffected either way. */
100
141
  & thead th[data-sticky] { z-index: 3; }
101
142
  /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
102
- `tfoot td { z-index: 2 }`), which `td[data-sticky]`'s own higher-specificity `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
143
+ `tfoot td { z-index: 2 }`), which `td[data-sticky]`'s own higher-specificity
144
+ `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
103
145
  & tfoot td[data-sticky] { z-index: 2; }
104
146
  & tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
105
147
  & tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
@@ -119,7 +161,8 @@
119
161
  /* crisp/#758: AI-enriched column marker, ported from RecordsTable's own
120
162
  already-measured value -- a full-bleed #9b69ff (bgPurple) wash at 4%,
121
163
  composited OVER whatever row-state background the cell already has
122
- (hover, selected), which is why it's an ::after overlay and not a plain background. */
164
+ (hover, selected), which is why it's an ::after overlay and not a
165
+ plain background. */
123
166
  & td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, var(--crisp-bg-purple) 4%, transparent); }
124
167
  & .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
125
168
  & .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
@@ -130,40 +173,56 @@
130
173
  & .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
131
174
  & .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
132
175
  /* The rejection's message, beside the cell's own content -- a sibling in
133
- the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
176
+ the TableCell, never nested inside a column's own render output, so a
177
+ hover-only wrapper (a Tooltip, say) around a custom render can't hide
134
178
  it. Block-level: the row grows to fit it rather than floating over
135
179
  the row below, so it can never end up clipped by the scroll
136
- container's overflow the way a position:fixed escape would need guarding against. */
180
+ container's overflow the way a position:fixed escape would need
181
+ guarding against. */
137
182
  & .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
138
- /* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
183
+ /* editor: "select"/"switch" -- always live, no separate edit mode, so
184
+ the cell IS the picker: same chromeless-fill treatment as
185
+ RecordsTable's own --picker cells (measured there already; DataTable
139
186
  shares RecordsTable's grid, Golden Rule 0). */
140
187
  & .crisp-datatable-td--picker { padding: 0; }
141
188
  & .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
142
189
  & .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
143
190
  & .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
144
- /* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
145
- & .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
191
+ /* reference picker: a leading 16px avatar per option, plus a trailing
192
+ "Create X" row when the consumer supplies one (#449) -- same measured
193
+ values as RecordsTable's own .crisp-recordstable-refoption*. */
194
+ & .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: clip; text-overflow: ellipsis; }
146
195
  & .crisp-datatable-refoption-avatar { flex-shrink: 0; }
147
196
  & .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
148
197
  & .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
149
198
  /* #826 -- the full-body loading state (a filter/sort re-query, or a
150
- fresh page boot), measured live on Attio's Companies grid on both triggers: icon+text centred in the row-body area, 10px gap, 14px/500 weight/20px line-height, both icon and text in the SAME muted rgba(0,0,0,0.63) -- the same color-mix figure this file already uses for other muted text (e.g. -phone-dial). Distinct from the cold in-place row load (blank, no indicator) and the warm-navigation per-cell shimmer (#599) -- neither of those swaps the whole body. */
199
+ fresh page boot), measured live on Attio's Companies grid on both
200
+ triggers: icon+text centred in the row-body area, 10px gap, 14px/500
201
+ weight/20px line-height, both icon and text in the SAME muted
202
+ rgba(0,0,0,0.63) -- the same color-mix figure this file already uses
203
+ for other muted text (e.g. -phone-dial). Distinct from the cold
204
+ in-place row load (blank, no indicator) and the warm-navigation
205
+ per-cell shimmer (#599) -- neither of those swaps the whole body. */
151
206
  & .crisp-datatable-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: var(--crisp-space-8) var(--crisp-space-3); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); line-height: 20px; }
152
207
  & .crisp-datatable-loading .crisp-spinner { border-top-color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
153
- /* #579: click-to-expand overlay for a truncated cell -- a read-only, full-value card, settled live as click (not Attio's usual
154
- hover-expand) and measured position: absolute, unlike this file's floating chrome (Menu/Select popovers) which portals and escapes the scroll container. Deliberately NOT the edit-error pattern above (grow the row, block-level, never clipped): a virtualized grid assumes a fixed row height for its scroll math (owned by the grid core, not this cell), so growing a row here would fight it. Anchored to the SAME column width the cell itself renders at (measured: Attio's own overlay came out only slightly wider than the
155
- triggering cell's clientWidth -- the overshoot is this padding), wrapping onto as many lines as the value needs; it CAN be clipped by `.crisp-table-scroll`'s own `overflow: auto` if it grows past the
208
+ /* #579: click-to-expand overlay for a truncated cell -- a read-only,
209
+ full-value card, settled live as click (not Attio's usual
210
+ hover-expand) and measured position: absolute, unlike this file's
211
+ floating chrome (Menu/Select popovers) which portals and escapes the
212
+ scroll container. Deliberately NOT the edit-error pattern above
213
+ (grow the row, block-level, never clipped): a virtualized grid
214
+ assumes a fixed row height for its scroll math (owned by the grid
215
+ core, not this cell), so growing a row here would fight it. Anchored
216
+ to the SAME column width the cell itself renders at (measured:
217
+ Attio's own overlay came out only slightly wider than the
218
+ triggering cell's clientWidth -- the overshoot is this padding),
219
+ wrapping onto as many lines as the value needs; it CAN be clipped by
220
+ `.crisp-table-scroll`'s own `overflow: auto` if it grows past the
156
221
  visible viewport, same as the edit-error comment above already
157
222
  notes for any position:absolute escape -- unmeasured against Attio
158
223
  for this exact case, flagged rather than guessed. */
159
224
  & td[data-cell-expanded] { overflow: visible; z-index: 7; }
160
- & .crisp-datatable-cell-overlay {
161
- position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
162
- padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface);
163
- border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px;
164
- font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium);
165
- color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default;
166
- }
225
+ & .crisp-datatable-cell-overlay { position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box; padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface); border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default; }
167
226
  /* fixed layout so column widths (and resize) are exact — like RecordsTable */
168
227
  & table { table-layout: fixed; }
169
228
  /* read-only/computed column marker (#596) -- sits just inside the
@@ -175,42 +234,70 @@
175
234
  & .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
176
235
  & .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
177
236
  & .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
178
- /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's own grid (the issue's own comment): the dragged header keeps full
237
+ /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
238
+ own grid (the issue's own comment): the dragged header keeps full
179
239
  opacity and the plain `pointer` cursor throughout (Attio draws no
180
240
  ghost and no "grabbing" cursor of its own), so this adds nothing to
181
241
  `[data-dragging]` itself -- the ONLY new paint is the drop-indicator
182
- line, a 3px bar in the same measured brand blue the resize handle's own hover state (above) already uses, positioned on whichever edge of the hovered header the pointer is closer to. */
242
+ line, a 3px bar in the same measured brand blue the resize handle's
243
+ own hover state (above) already uses, positioned on whichever edge of
244
+ the hovered header the pointer is closer to. */
183
245
  & th[data-dragging] { cursor: pointer; }
184
246
  & th[data-drop-before]::before, & th[data-drop-after]::after { content: ""; position: absolute; top: 0; width: 3px; height: 100%; background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none; }
185
247
  & th[data-drop-before]::before { inset-inline-start: -1.5px; }
186
248
  & th[data-drop-after]::after { inset-inline-end: -1.5px; }
187
249
  /* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
188
250
  & .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
189
- /* crisp/#601 supersession follow-up -- the same flex-row shape .crisp-datatable-entity gives its own avatar+name+actions, but structural only (no entity-specific colour/weight): lets onOpenComments/onOpenPreview compose alongside ANY column's own
251
+ /* crisp/#601 supersession follow-up -- the same flex-row shape
252
+ .crisp-datatable-entity gives its own avatar+name+actions, but
253
+ structural only (no entity-specific colour/weight): lets
254
+ onOpenComments/onOpenPreview compose alongside ANY column's own
190
255
  content (a custom render, or a non-entity built-in type), not just
191
256
  type: "entity"'s own inline rendering. */
192
257
  & .crisp-datatable-cell-actions-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; width: 100%; }
193
- /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text, plain hover vs rest) that Attio's underline is unconditional, not a
258
+ /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
259
+ plain hover vs rest) that Attio's underline is unconditional, not a
194
260
  hover reveal -- identical at rest and on hover: text-decoration-color
195
- is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text colour. The previous :hover-gated, full-contrast rule here was itself unmeasured and wrong in two ways at once. */ /* crisp#851 -- Attio's own entity/record names are measured at
196
- font-weight 500, distinct from a plain-text primary column's 400 (table.recipe.ts's own `tbody th` default). This was never its own
261
+ is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text
262
+ colour. The previous :hover-gated, full-contrast rule here was itself
263
+ unmeasured and wrong in two ways at once. */ /* crisp#851 -- Attio's own entity/record names are measured at
264
+ font-weight 500, distinct from a plain-text primary column's 400
265
+ (table.recipe.ts's own `tbody th` default). This was never its own
197
266
  explicit style -- it silently rode on a since-fixed header-rule leak
198
267
  (#833/#835/#848) that happened to apply the same 500 to every
199
268
  primary-column body cell regardless of type. Made explicit here so
200
269
  fixing that leak doesn't also (wrongly) drop entity names to 400. */
201
- & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--crisp-weight-medium); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
202
- & 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%; }
203
- /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary column, revealed on ROW hover (the issue's own title says "row
270
+ & .crisp-datatable-entity-name { overflow: clip; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--crisp-weight-medium); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
271
+ /* #893: shared by the button (onEntityClick, a different record) and
272
+ the anchor (href, the row's own record) -- was button-only, which
273
+ silently left a real <a> unstyled the day #893 added one. */
274
+ & .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%; }
275
+ /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
276
+ column, revealed on ROW hover (the issue's own title says "row
204
277
  hover", and a live check confirmed hovering well past the name --
205
- not just the icons' own hit target -- still reveals them) or keyboard focus. Measured live: border-radius 6px, icon colour rgba(0,0,0,0.55) at rest, darkening to the full text colour with a rgba(0,0,0,0.04) background on the icon's OWN hover -- both
278
+ not just the icons' own hit target -- still reveals them) or
279
+ keyboard focus. Measured live: border-radius 6px, icon colour
280
+ rgba(0,0,0,0.55) at rest, darkening to the full text colour with a
281
+ rgba(0,0,0,0.04) background on the icon's OWN hover -- both
206
282
  expressed as color-mix against fgPrimary so they stay correct in
207
283
  dark mode instead of a literal black that would invert. */
208
284
  & .crisp-datatable-entity-actions { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
209
285
  & tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
210
286
  & .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: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
211
287
  & .crisp-datatable-entity-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
212
- & .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
213
- & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
288
+ /* #892: the default shape's radius was a stray literal (radius.sm, 6px)
289
+ -- Attio's own real company/object avatar measures 30%, which is
290
+ Avatar's own already-shipped shape="rounded" value, not a new number.
291
+ shape="circle" reuses Avatar's own shape="circle" value (radius.full)
292
+ the same way, measured live on a real Attio person avatar. */
293
+ & .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-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: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
294
+ & .crisp-datatable-avatar[data-shape="circle"] { border-radius: var(--crisp-radius-full); }
295
+ & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
296
+ & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
297
+ & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
298
+ & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
299
+ & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
300
+ & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
214
301
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
215
302
  & .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
216
303
  & .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
@@ -223,13 +310,19 @@
223
310
  & .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
224
311
  & .crisp-datatable-num { font-variant-numeric: tabular-nums; }
225
312
  & .crisp-datatable-muted { color: var(--crisp-fg-muted); }
226
- /* crisp#822 -- a date/datetime cell's danger/overdue tone: same token as TasksTable's own overdue due-date (crisp#462), plain text colour, no
313
+ /* crisp#822 -- a date/datetime cell's danger/overdue tone: same token as
314
+ TasksTable's own overdue due-date (crisp#462), plain text colour, no
227
315
  fill/weight change. */
228
316
  & .crisp-datatable-danger { color: var(--crisp-fg-danger); }
229
- /* url/email cell types (#654) -- same measured values as RecordsTable's own .crisp-recordstable-link, which this mirrors: no underline at rest, underline on hover, own-colour text rather than a browser-blue link. */
230
- & .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
317
+ /* url/email cell types (#654) -- same measured values as RecordsTable's
318
+ own .crisp-recordstable-link, which this mirrors: no underline at
319
+ rest, underline on hover, own-colour text rather than a browser-blue
320
+ link. */
321
+ & .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: clip; text-overflow: ellipsis; }
231
322
  & .crisp-datatable-link:hover { text-decoration: underline; }
232
- /* phone cell type/editor (#765) -- measured live: the dial-code prefix is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text value already measured elsewhere in this file. */
323
+ /* phone cell type/editor (#765) -- measured live: the dial-code prefix
324
+ is a muted rgba(0,0,0,0.63) at 14px, matching every other muted-text
325
+ value already measured elsewhere in this file. */
233
326
  & .crisp-datatable-phone { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
234
327
  & .crisp-datatable-phone-dial { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
235
328
  & .crisp-datatable-phone-editor { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
@@ -259,7 +352,8 @@
259
352
  value rows (32px = size.menuRow, 5px 8px padding, 8px gap between
260
353
  the grip/value/actions, 16px icons) above one ALWAYS-blank trailing
261
354
  DataPhoneEditor-shaped row -- that blank row is the only "add
262
- another" affordance, there's no separate button for it. The copy/delete pair reuses .crisp-datatable-entity-action's own
355
+ another" affordance, there's no separate button for it. The
356
+ copy/delete pair reuses .crisp-datatable-entity-action's own
263
357
  hover-reveal chrome (not independently re-measured as its own hit
264
358
  target this pass, only the 16px icon glyphs were). */
265
359
  & .crisp-datatable-phone-multi-editor { display: flex; flex-direction: column; }
@@ -267,8 +361,7 @@
267
361
  & .crisp-datatable-phone-multi-row .crisp-datatable-phone-link { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
268
362
  & .crisp-datatable-phone-multi-grip { color: var(--crisp-fg-muted); flex-shrink: 0; cursor: grab; }
269
363
  & .crisp-datatable-phone-multi-actions { display: flex; align-items: center; gap: var(--crisp-space-1); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
270
- & .crisp-datatable-phone-multi-row:hover .crisp-datatable-phone-multi-actions,
271
- & .crisp-datatable-phone-multi-actions:focus-within { opacity: 1; }
364
+ & .crisp-datatable-phone-multi-row:hover .crisp-datatable-phone-multi-actions, & .crisp-datatable-phone-multi-actions:focus-within { opacity: 1; }
272
365
  & .crisp-datatable-phone-multi-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
273
366
  & .crisp-datatable-phone-multi-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
274
367
  /* datetime cell editor (#762) -- DatePickerField + TimePicker side by side. */
@@ -277,7 +370,10 @@
277
370
  18px pitch (a 2px gap), an empty star at rgba(0,0,0,0.1), a
278
371
  committed/filled star at brand600 (--crisp-bg-brand-solid, an exact
279
372
  match to the measured #266df0), and a distinct hover-preview colour
280
- -- rgb(33,91,196) -- that's close to but NOT brand700/bgBrandSolidPressed (#1f5fd6); kept as its own literal rather than forced onto that token, since the two were measured stable and different. The read-only svg and the editable button's own inner svg both carry the
373
+ -- rgb(33,91,196) -- that's close to but NOT brand700/bgBrandSolidPressed
374
+ (#1f5fd6); kept as its own literal rather than forced onto that token,
375
+ since the two were measured stable and different. The read-only svg
376
+ and the editable button's own inner svg both carry the
281
377
  crisp-datatable-rating-star class on the svg itself (not the
282
378
  button), so one set of fill rules covers both -- the button only
283
379
  adds its own reset + cursor via a separate -btn class. */
@@ -288,9 +384,13 @@
288
384
  & .crisp-datatable-rating-star[data-hovering] path { fill: #215bc4; }
289
385
  & .crisp-datatable-rating-star-btn { display: inline-flex; padding: 0; margin: 0; border: none; background: none; font-family: inherit; cursor: pointer; }
290
386
  /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
291
- a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
387
+ a leading 20px grip column, a 16px grip icon, and the row's own
388
+ hairline doubling as the drop indicator (it tints toward the brand
389
+ colour as the active drop target). The affordance + geometry are
390
+ measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
292
391
  draggable + dataTransfer, the same one column reorder already uses
293
- (RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
392
+ (RecordsTable's reorderColumn) -- not a proprietary
393
+ pointer-events implementation. */
294
394
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
295
395
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
296
396
  & .crisp-datatable-grip:active { cursor: grabbing; }