@crisp-ui-kit/crisp 0.54.1 → 0.54.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 (171) hide show
  1. package/dist/chip.css +7 -1
  2. package/dist/chip.d.ts +2 -1
  3. package/dist/chip.layered.css +7 -1
  4. package/dist/cjs/components/chip/chip.js +3 -2
  5. package/dist/cjs/components/chip/chip.recipe.js +37 -1
  6. package/dist/cjs/components/datatable/datatable.js +490 -111
  7. package/dist/cjs/components/datatable/datatable.recipe.js +4 -0
  8. package/dist/cjs/components/emptystate/emptystate.js +3 -2
  9. package/dist/cjs/components/emptystate/emptystate.recipe.js +26 -1
  10. package/dist/cjs/components/mentionchip/index.js +7 -0
  11. package/dist/cjs/components/mentionchip/mentionchip.js +57 -0
  12. package/dist/cjs/components/mentionchip/mentionchip.recipe.js +38 -0
  13. package/dist/cjs/components/personchip/index.js +17 -0
  14. package/dist/cjs/components/personchip/personchip.js +50 -0
  15. package/dist/cjs/components/table/table.js +2 -2
  16. package/dist/cjs/components/table/table.recipe.js +15 -3
  17. package/dist/cjs/components/tabs/tabs.js +2 -2
  18. package/dist/cjs/components/tabs/tabs.recipe.js +30 -3
  19. package/dist/cjs/components/text/index.js +7 -0
  20. package/dist/cjs/components/text/text.js +55 -0
  21. package/dist/cjs/components/text/text.recipe.js +34 -0
  22. package/dist/cjs/core/floating-stack.js +18 -0
  23. package/dist/cjs/core/index.js +2 -1
  24. package/dist/cjs/index.js +8 -0
  25. package/dist/cjs/product/formdialog/formdialog.js +171 -0
  26. package/dist/cjs/product/formdialog/formdialog.recipe.js +26 -0
  27. package/dist/cjs/product/formdialog/index.js +8 -0
  28. package/dist/cjs/product/headerbar/headerbar.recipe.js +9 -1
  29. package/dist/cjs/product/importdialog/importdialog.js +134 -0
  30. package/dist/cjs/product/importdialog/importdialog.recipe.js +92 -0
  31. package/dist/cjs/product/importdialog/index.js +18 -0
  32. package/dist/cjs/product/recordpage/recordpage.recipe.js +10 -1
  33. package/dist/cjs/product/recordstable/recordstable.js +89 -6
  34. package/dist/cjs/product/richtext/richtext.recipe.js +26 -1
  35. package/dist/cjs/product/savedviews/index.js +7 -0
  36. package/dist/cjs/product/savedviews/savedviews.js +207 -0
  37. package/dist/cjs/product/savedviews/savedviews.recipe.js +103 -0
  38. package/dist/cjs/product/taskstable/index.js +7 -0
  39. package/dist/cjs/product/taskstable/taskstable.js +27 -0
  40. package/dist/cjs/product/taskstable/taskstable.recipe.js +47 -0
  41. package/dist/cjs/product/themepicker/index.js +17 -0
  42. package/dist/cjs/product/themepicker/themepicker.js +32 -0
  43. package/dist/cjs/product/themepicker/themepicker.recipe.js +98 -0
  44. package/dist/cjs/tokens/palette.js +30 -13
  45. package/dist/cjs/tokens/semantic.js +6 -0
  46. package/dist/datatable.css +20 -21
  47. package/dist/datatable.layered.css +20 -21
  48. package/dist/emptystate.css +15 -1
  49. package/dist/emptystate.d.ts +2 -2
  50. package/dist/emptystate.layered.css +15 -1
  51. package/dist/esm/components/chip/chip.js +3 -2
  52. package/dist/esm/components/chip/chip.recipe.js +37 -1
  53. package/dist/esm/components/datatable/datatable.js +489 -111
  54. package/dist/esm/components/datatable/datatable.recipe.js +4 -0
  55. package/dist/esm/components/emptystate/emptystate.js +4 -3
  56. package/dist/esm/components/emptystate/emptystate.recipe.js +26 -1
  57. package/dist/esm/components/emptystate/index.js +1 -1
  58. package/dist/esm/components/mentionchip/index.js +2 -0
  59. package/dist/esm/components/mentionchip/mentionchip.js +21 -0
  60. package/dist/esm/components/mentionchip/mentionchip.recipe.js +35 -0
  61. package/dist/esm/components/personchip/index.js +1 -0
  62. package/dist/esm/components/personchip/personchip.js +47 -0
  63. package/dist/esm/components/table/table.js +2 -2
  64. package/dist/esm/components/table/table.recipe.js +15 -3
  65. package/dist/esm/components/tabs/tabs.js +2 -2
  66. package/dist/esm/components/tabs/tabs.recipe.js +30 -3
  67. package/dist/esm/components/text/index.js +2 -0
  68. package/dist/esm/components/text/text.js +19 -0
  69. package/dist/esm/components/text/text.recipe.js +31 -0
  70. package/dist/esm/core/floating-stack.js +17 -0
  71. package/dist/esm/core/index.js +1 -1
  72. package/dist/esm/index.js +8 -0
  73. package/dist/esm/product/formdialog/formdialog.js +134 -0
  74. package/dist/esm/product/formdialog/formdialog.recipe.js +23 -0
  75. package/dist/esm/product/formdialog/index.js +2 -0
  76. package/dist/esm/product/headerbar/headerbar.recipe.js +9 -1
  77. package/dist/esm/product/importdialog/importdialog.js +97 -0
  78. package/dist/esm/product/importdialog/importdialog.recipe.js +89 -0
  79. package/dist/esm/product/importdialog/index.js +2 -0
  80. package/dist/esm/product/recordpage/recordpage.recipe.js +10 -1
  81. package/dist/esm/product/recordstable/recordstable.js +90 -8
  82. package/dist/esm/product/richtext/richtext.recipe.js +26 -1
  83. package/dist/esm/product/savedviews/index.js +2 -0
  84. package/dist/esm/product/savedviews/savedviews.js +171 -0
  85. package/dist/esm/product/savedviews/savedviews.recipe.js +100 -0
  86. package/dist/esm/product/taskstable/index.js +2 -0
  87. package/dist/esm/product/taskstable/taskstable.js +24 -0
  88. package/dist/esm/product/taskstable/taskstable.recipe.js +44 -0
  89. package/dist/esm/product/themepicker/index.js +1 -0
  90. package/dist/esm/product/themepicker/themepicker.js +29 -0
  91. package/dist/esm/product/themepicker/themepicker.recipe.js +95 -0
  92. package/dist/esm/tokens/palette.js +30 -13
  93. package/dist/esm/tokens/semantic.js +6 -0
  94. package/dist/formdialog.css +6 -0
  95. package/dist/formdialog.d.ts +7 -0
  96. package/dist/formdialog.layered.css +8 -0
  97. package/dist/headerbar.css +1 -1
  98. package/dist/headerbar.layered.css +1 -1
  99. package/dist/importdialog.css +38 -0
  100. package/dist/importdialog.d.ts +7 -0
  101. package/dist/importdialog.layered.css +40 -0
  102. package/dist/mentionchip.css +3 -0
  103. package/dist/mentionchip.d.ts +7 -0
  104. package/dist/mentionchip.layered.css +5 -0
  105. package/dist/native/tokens.d.ts +1 -1
  106. package/dist/native/tokens.js +14 -6
  107. package/dist/recordpage.css +13 -2
  108. package/dist/recordpage.layered.css +13 -2
  109. package/dist/richtext.css +11 -1
  110. package/dist/richtext.layered.css +11 -1
  111. package/dist/savedviews.css +78 -0
  112. package/dist/savedviews.d.ts +7 -0
  113. package/dist/savedviews.layered.css +80 -0
  114. package/dist/styles.css +287 -37
  115. package/dist/styles.layered.css +287 -37
  116. package/dist/table.css +15 -6
  117. package/dist/table.layered.css +15 -6
  118. package/dist/tabs.css +7 -1
  119. package/dist/tabs.d.ts +2 -2
  120. package/dist/tabs.layered.css +7 -1
  121. package/dist/taskstable.css +23 -0
  122. package/dist/taskstable.d.ts +7 -0
  123. package/dist/taskstable.layered.css +25 -0
  124. package/dist/text.css +9 -0
  125. package/dist/text.d.ts +7 -0
  126. package/dist/text.layered.css +11 -0
  127. package/dist/themepicker.css +27 -0
  128. package/dist/themepicker.d.ts +7 -0
  129. package/dist/themepicker.layered.css +29 -0
  130. package/dist/tokens.css +7 -3
  131. package/dist/tokens.json +14 -6
  132. package/dist/types/components/chip/chip.d.ts +32 -0
  133. package/dist/types/components/chip/chip.recipe.d.ts +31 -0
  134. package/dist/types/components/datatable/datatable.d.ts +39 -4
  135. package/dist/types/components/emptystate/emptystate.d.ts +29 -4
  136. package/dist/types/components/emptystate/emptystate.recipe.d.ts +2 -0
  137. package/dist/types/components/emptystate/index.d.ts +1 -1
  138. package/dist/types/components/mentionchip/index.d.ts +2 -0
  139. package/dist/types/components/mentionchip/mentionchip.d.ts +20 -0
  140. package/dist/types/components/mentionchip/mentionchip.recipe.d.ts +18 -0
  141. package/dist/types/components/personchip/index.d.ts +1 -0
  142. package/dist/types/components/personchip/personchip.d.ts +37 -0
  143. package/dist/types/components/table/table.d.ts +36 -0
  144. package/dist/types/components/tabs/tabs.d.ts +4 -2
  145. package/dist/types/components/tabs/tabs.recipe.d.ts +18 -3
  146. package/dist/types/components/text/index.d.ts +2 -0
  147. package/dist/types/components/text/text.d.ts +32 -0
  148. package/dist/types/components/text/text.recipe.d.ts +40 -0
  149. package/dist/types/core/floating-stack.d.ts +14 -0
  150. package/dist/types/core/index.d.ts +1 -1
  151. package/dist/types/index.d.ts +8 -0
  152. package/dist/types/product/formdialog/formdialog.d.ts +57 -0
  153. package/dist/types/product/formdialog/formdialog.recipe.d.ts +3 -0
  154. package/dist/types/product/formdialog/index.d.ts +2 -0
  155. package/dist/types/product/importdialog/importdialog.d.ts +86 -0
  156. package/dist/types/product/importdialog/importdialog.recipe.d.ts +3 -0
  157. package/dist/types/product/importdialog/index.d.ts +2 -0
  158. package/dist/types/product/recordstable/recordstable.d.ts +24 -2
  159. package/dist/types/product/savedviews/index.d.ts +2 -0
  160. package/dist/types/product/savedviews/savedviews.d.ts +55 -0
  161. package/dist/types/product/savedviews/savedviews.recipe.d.ts +3 -0
  162. package/dist/types/product/taskstable/index.d.ts +2 -0
  163. package/dist/types/product/taskstable/taskstable.d.ts +54 -0
  164. package/dist/types/product/taskstable/taskstable.recipe.d.ts +3 -0
  165. package/dist/types/product/themepicker/index.d.ts +1 -0
  166. package/dist/types/product/themepicker/themepicker.d.ts +21 -0
  167. package/dist/types/product/themepicker/themepicker.recipe.d.ts +3 -0
  168. package/dist/types/tokens/contract.d.ts +2 -0
  169. package/dist/types/tokens/palette.d.ts +11 -3
  170. package/dist/types/tokens/semantic.d.ts +6 -0
  171. package/package.json +41 -1
@@ -7,6 +7,7 @@
7
7
  --crisp-bg-raised: #f4f4f5;
8
8
  --crisp-border-default: #e4e4e7;
9
9
  --crisp-border-subtle: #eeeff1;
10
+ --crisp-border-dropzone: #e6e7ea;
10
11
  --crisp-ring-neutral: transparent;
11
12
  --crisp-ring-field: rgba(0,0,0,0.06);
12
13
  --crisp-ring-switch: #d9dade;
@@ -25,6 +26,7 @@
25
26
  --crisp-fg-muted: #71717a;
26
27
  --crisp-bg-danger: #e5484d;
27
28
  --crisp-bg-danger-solid: #ba2525;
29
+ --crisp-fg-danger: #eb3b3b;
28
30
  --crisp-stroke-field-focus: #1a1c20;
29
31
  --crisp-fg-disabled: #d1d3d8;
30
32
  --crisp-bg-neutral-weak: #f3f4f5;
@@ -59,6 +61,7 @@
59
61
  --crisp-bg-raised: #101112;
60
62
  --crisp-border-default: #212224;
61
63
  --crisp-border-subtle: #1c1d1f;
64
+ --crisp-border-dropzone: #212224;
62
65
  --crisp-ring-neutral: #212224;
63
66
  --crisp-ring-field: #212224;
64
67
  --crisp-ring-switch: #212224;
@@ -77,6 +80,7 @@
77
80
  --crisp-fg-muted: #9a9aa2;
78
81
  --crisp-bg-danger: #ff5a5f;
79
82
  --crisp-bg-danger-solid: #ba2525;
83
+ --crisp-fg-danger: #ff5a5f;
80
84
  --crisp-stroke-field-focus: #f3f4f5;
81
85
  --crisp-fg-disabled: #5b606a;
82
86
  --crisp-bg-neutral-weak: #1d2025;
@@ -97,9 +101,9 @@
97
101
  --crisp-fg-warning-soft: #e0982a;
98
102
  --crisp-fg-danger-soft: #ff5a5f;
99
103
  --crisp-fg-neutral-soft: #9a9aa2;
100
- --crisp-bg-purple-soft: #f5f0ff;
101
- --crisp-fg-purple-soft: #6238b5;
102
- --crisp-ring-purple-soft: #e8ddfe;
104
+ --crisp-bg-purple-soft: #2f1e5a;
105
+ --crisp-fg-purple-soft: #d8c4ff;
106
+ --crisp-ring-purple-soft: #45297d;
103
107
  }
104
108
 
105
109
  :root {
@@ -444,7 +448,13 @@
444
448
  }
445
449
 
446
450
  .crisp-chip {
447
- display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding-inline: 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;
451
+ display: inline-flex; align-items: center; gap: 3px; height: var(--crisp-size-chip); min-width: 0; padding-inline: 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;
452
+ }
453
+ .crisp-chip--truncate_true {
454
+ & .crisp-chip-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
455
+ }
456
+ .crisp-chip--truncate_false {
457
+
448
458
  }
449
459
  .crisp-chip--tone_neutral {
450
460
  background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle);
@@ -602,6 +612,9 @@
602
612
  /* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
603
613
  & th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
604
614
  & th[data-sticky] { z-index: 3; }
615
+ /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
616
+ `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. */
617
+ & tfoot td[data-sticky] { z-index: 2; }
605
618
  & tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
606
619
  & tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
607
620
  & .crisp-table-scroll[data-scrolled-x] [data-sticky-edge] { clip-path: inset(0 -38px 0 0); box-shadow: 6px 0 16px 4px rgba(0,0,0,0.12); }
@@ -619,23 +632,25 @@
619
632
  & .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
620
633
  & .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; }
621
634
  & .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
622
- /* An async onEdit's in-flight state (#385) -- the field/picker dims and stops accepting input rather than disappearing, so the just-typed or just-chosen value stays visible while its commit is pending. Same treatment Input already gives its own disabled state. */
635
+ /* An async onEdit's in-flight state (#385) -- the field/picker dims and
636
+ stops accepting input rather than disappearing, so the just-typed or
637
+ just-chosen value stays visible while its commit is pending. Same
638
+ treatment Input already gives its own disabled state. */
623
639
  & .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
624
640
  & .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
625
- /* The rejection's message, beside the cell's own content -- a sibling in the TableCell, never nested inside a column's own render output, so a
626
- hover-only wrapper (a Tooltip, say) around a custom render can't hide it. Block-level: the row grows to fit it rather than floating over the row below, so it can never end up clipped by the scroll container's overflow the way a position:fixed escape would need
627
- guarding against. */
641
+ /* The rejection's message, beside the cell's own content -- a sibling in
642
+ 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
643
+ it. Block-level: the row grows to fit it rather than floating over
644
+ the row below, so it can never end up clipped by the scroll
645
+ container's overflow the way a position:fixed escape would need guarding against. */
628
646
  & .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); }
629
- /* editor: "select"/"switch" -- always live, no separate edit mode, so
630
- the cell IS the picker: same chromeless-fill treatment as
631
- RecordsTable's own --picker cells (measured there already; DataTable shares RecordsTable's grid, Golden Rule 0). */
647
+ /* 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
648
+ shares RecordsTable's grid, Golden Rule 0). */
632
649
  & .crisp-datatable-td--picker { padding: 0; }
633
650
  & .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); }
634
651
  & .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; }
635
652
  & .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
636
- /* reference picker: a leading 16px avatar per option, plus a trailing
637
- "Create X" row when the consumer supplies one (#449) -- same measured
638
- values as RecordsTable's own .crisp-recordstable-refoption*. */
653
+ /* 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*. */
639
654
  & .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
640
655
  & .crisp-datatable-refoption-avatar { flex-shrink: 0; }
641
656
  & .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
@@ -652,12 +667,7 @@
652
667
  & .crisp-datatable-entity:hover .crisp-datatable-entity-name { text-decoration: underline; }
653
668
  & 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%; }
654
669
  & .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); }
655
- & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
656
- & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
657
- & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
658
- & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
659
- & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
660
- & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
670
+ & .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); }
661
671
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
662
672
  & .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
663
673
  & .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); }
@@ -670,11 +680,10 @@
670
680
  & .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)); }
671
681
  & .crisp-datatable-num { font-variant-numeric: tabular-nums; }
672
682
  & .crisp-datatable-muted { color: var(--crisp-fg-muted); }
673
- /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures a leading 20px grip column, a 16px grip icon, and the row's own
674
- hairline doubling as the drop indicator (it tints toward the brand
675
- colour as the active drop target). The affordance + geometry are
676
- measured; the drag MECHANISM is crisp's existing house idiom -- HTML5 draggable + dataTransfer, the same one column reorder already uses (RecordsTable's reorderColumn) -- not a proprietary
677
- pointer-events implementation. */
683
+ /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
684
+ 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
685
+ draggable + dataTransfer, the same one column reorder already uses
686
+ (RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
678
687
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
679
688
  & .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; }
680
689
  & .crisp-datatable-grip:active { cursor: grabbing; }
@@ -911,7 +920,16 @@
911
920
  & .crisp-emptystate-icon { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; margin-bottom: var(--crisp-space-1); border-radius: var(--crisp-radius-xl); background: color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent); color: var(--crisp-fg-muted); }
912
921
  & .crisp-emptystate-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
913
922
  & .crisp-emptystate-desc { max-width: 320px; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
914
- & .crisp-emptystate-action { margin-top: var(--crisp-space-2); }
923
+ /* #603: measured 8px gap between "Import CSV"/"Connect data source" -- inline-flex so one action (the common case) or several lay out the same way, wrapping if the container is narrow. */
924
+ & .crisp-emptystate-action { display: inline-flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: var(--crisp-space-2); margin-top: var(--crisp-space-2); }
925
+ /* #603: Attio's zero-records "Learn more" card -- a label above a
926
+ clickable icon+title tile. Left/start-aligned, unlike the rest of
927
+ this centred panel, so it overrides the base text-align. */
928
+ & .crisp-emptystate-learnmore { display: flex; flex-direction: column; align-items: stretch; gap: var(--crisp-space-2); margin-top: var(--crisp-space-6); text-align: start; }
929
+ & .crisp-emptystate-learnmore-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 50%, transparent); }
930
+ & .crisp-emptystate-learnmore-card { display: flex; align-items: center; gap: var(--crisp-space-2); padding: var(--crisp-space-2) var(--crisp-space-3) var(--crisp-space-2) var(--crisp-space-2); margin: 0; border: 1px solid var(--crisp-border-subtle); border-radius: 18px; background: var(--crisp-bg-surface); font: inherit; text-align: start; text-decoration: none; color: inherit; cursor: pointer; }
931
+ & .crisp-emptystate-learnmore-icon { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; flex-shrink: 0; border-radius: var(--crisp-radius-xl); background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); color: var(--crisp-fg-muted); }
932
+ & .crisp-emptystate-learnmore-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
915
933
  }
916
934
  .crisp-emptystate--variant_default {
917
935
 
@@ -926,6 +944,11 @@
926
944
  & .crisp-emptystate-desc { max-width: 266px; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); line-height: 20px; }
927
945
  & .crisp-emptystate-action { margin-top: var(--crisp-space-2); }
928
946
  }
947
+ .crisp-emptystate--variant_records {
948
+ & .crisp-emptystate-icon { width: 138px; height: 138px; margin-bottom: var(--crisp-space-2); border-radius: 0; color: color-mix(in srgb, var(--crisp-fg-primary) 22%, transparent); background: radial-gradient(circle, color-mix(in srgb, var(--crisp-fg-primary) 12%, transparent) 1.2px, transparent 1.2px) 0 0 / 16px 16px; }
949
+ & .crisp-emptystate-title { font-size: 20px; line-height: 24px; }
950
+ & .crisp-emptystate-desc { max-width: 266px; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); line-height: 20px; }
951
+ }
929
952
 
930
953
  .crisp-fileupload {
931
954
  display: flex; flex-direction: column; gap: var(--crisp-space-2); font-family: var(--crisp-font-sans); min-width: 0;
@@ -1119,6 +1142,10 @@
1119
1142
  & .crisp-linechart-axis-title { margin-top: var(--crisp-space-1); padding-inline-start: var(--crisp-linechart-yaxis-width); text-align: center; font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
1120
1143
  }
1121
1144
 
1145
+ .crisp-mention-chip {
1146
+ display: inline; border-radius: var(--crisp-radius-chip); padding-inline: var(--crisp-space-1); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: color-mix(in srgb, var(--crisp-bg-brand-solid) 82%, var(--crisp-fg-primary)); font-weight: var(--crisp-weight-medium); cursor: default;
1147
+ }
1148
+
1122
1149
 
1123
1150
  /* pointer-events: auto (#375): a modal Dialog sets pointer-events: none
1124
1151
  on document.body while open (Zag re-enables it only on layers IT
@@ -1627,17 +1654,26 @@
1627
1654
  }
1628
1655
 
1629
1656
  .crisp-table {
1630
- display: block; box-sizing: border-box; background: var(--crisp-bg-surface);
1631
- & .crisp-table-scroll { overflow-x: auto; }
1657
+ display: block; box-sizing: border-box; background: var(--crisp-bg-surface); /* #554: both axes -- a consumer bounds the height via `scrollProps` (style/className on this element), and the header/footer below pin inside it exactly the way Attio's own grid does ("Both the header and
1658
+ the summary row are sticky, on the scroll container rather than the
1659
+ cells" -- docs/study/attio-table.md). Unbounded, `overflow: auto`
1660
+ shows no vertical scrollbar and nothing sticks, so this is a no-op
1661
+ for every existing consumer that never set a height. */
1662
+ & .crisp-table-scroll { overflow: auto; }
1632
1663
  & table { width: 100%; border-collapse: separate; border-spacing: 0; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
1633
- /* #465: 12px, not crisp's old 8px -- measured on a live header cell
1634
- (padding: 0 12px) and independently on a body cell's text inset (getBoundingClientRect delta from the cell edge). Matches ColumnCalc's own already-correct 12px trigger inset. */
1664
+ /* #465: 12px, not crisp's old 8px -- measured on a live header cell (padding: 0 12px) and independently on a body cell's text inset
1665
+ (getBoundingClientRect delta from the cell edge). Matches
1666
+ ColumnCalc's own already-correct 12px trigger inset. */
1635
1667
  & th, & td { position: relative; height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-3); border-bottom: 1px solid var(--crisp-border-subtle); border-inline-end: 1px solid var(--crisp-border-subtle); text-align: start; vertical-align: middle; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1636
- & th { height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); user-select: none; }
1668
+ /* #554: sticky on the vertical axis, mirroring RecordsTable's own
1669
+ `.crisp-recordstable-th`. z-index 2 sits above a plain body cell (auto)
1670
+ and below a sticky-column corner cell (DataTable's `th[data-sticky]`, z-index 3), so the corner always wins. */
1671
+ & th { position: sticky; top: 0; z-index: 2; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); user-select: none; }
1637
1672
  & td { color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); }
1638
1673
  & th:last-child, & td:last-child { border-inline-end: none; }
1639
1674
  & tbody tr:last-child td { border-bottom: none; }
1640
- & tfoot td { border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1675
+ /* #554: sticky to the bottom, mirroring `.crisp-recordstable-sum`. */
1676
+ & tfoot td { position: sticky; bottom: 0; z-index: 2; border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1641
1677
  & caption { padding: var(--crisp-space-2) var(--crisp-space-2); text-align: start; font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); caption-side: bottom; }
1642
1678
  & td[data-align="right"], & th[data-align="right"] { text-align: end; }
1643
1679
  & td[data-align="center"], & th[data-align="center"] { text-align: center; }
@@ -1758,9 +1794,15 @@
1758
1794
  & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1759
1795
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
1760
1796
  & .crisp-tabs-indicator { position: absolute; bottom: 0; left: 0; z-index: 1; height: 1px; background: var(--crisp-fg-primary); border-radius: 2px 2px 0 0; transition: transform .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); pointer-events: none; }
1761
- & .crisp-tabs-panel { flex: 1; min-height: 0; }
1797
+ & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
1762
1798
  & .crisp-tabs-panel:focus-visible { outline: none; }
1763
1799
  }
1800
+ .crisp-tabs--fill_true {
1801
+ height: 100%;
1802
+ }
1803
+ .crisp-tabs--fill_false {
1804
+
1805
+ }
1764
1806
 
1765
1807
  @media (prefers-reduced-motion: reduce) {
1766
1808
  .crisp-tabs, .crisp-tabs * {
@@ -1770,6 +1812,16 @@
1770
1812
  }
1771
1813
  }
1772
1814
 
1815
+ .crisp-text {
1816
+ display: inline;
1817
+ }
1818
+ .crisp-text--tone_default {
1819
+ color: var(--crisp-fg-primary);
1820
+ }
1821
+ .crisp-text--tone_muted {
1822
+ color: var(--crisp-fg-neutral-muted);
1823
+ }
1824
+
1773
1825
  .crisp-textarea {
1774
1826
  position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
1775
1827
  & .crisp-textarea-field { box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical; margin: 0; padding: var(--crisp-space-2-5) 14px; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-field); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); outline: none; transition: box-shadow .15s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
@@ -2064,14 +2116,60 @@
2064
2116
  & .crisp-flowcanvas-tool-sep { width: 1px; height: 18px; background: var(--crisp-border-subtle); }
2065
2117
  }
2066
2118
 
2119
+ .crisp-formdialog {
2120
+ & .crisp-formdialog-createfoot { display: flex; align-items: center; gap: var(--crisp-space-4); }
2121
+ & .crisp-formdialog-more-label { font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); cursor: pointer; }
2122
+ & .crisp-formdialog-error { font-size: var(--crisp-text-md); color: var(--crisp-fg-danger); margin: 0; }
2123
+ & .crisp-formdialog-form { display: grid; gap: var(--crisp-space-3); }
2124
+ }
2125
+
2067
2126
  .crisp-headerbar {
2068
2127
  box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-2); height: var(--crisp-size-header); flex-shrink: 0; padding: 0 var(--crisp-space-3); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2069
- &[data-bordered] { border-bottom: 1px solid var(--crisp-border-subtle); }
2128
+ &[data-bordered] { height: calc(var(--crisp-size-header) + 1px); border-bottom: 1px solid var(--crisp-border-subtle); }
2070
2129
  & .crisp-headerbar-leading { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; min-width: 0; }
2071
2130
  & .crisp-headerbar-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--crisp-space-2); overflow: hidden; }
2072
2131
  & .crisp-headerbar-trailing { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; }
2073
2132
  }
2074
2133
 
2134
+ .crisp-importdialog {
2135
+ display: contents; font-family: var(--crisp-font-sans);
2136
+ & .crisp-importdialog-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }
2137
+ & .crisp-importdialog-positioner { position: fixed; inset: 0; z-index: 61; display: flex; }
2138
+ & .crisp-importdialog-content { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 100%; height: 100%; border-radius: 0; background: var(--crisp-bg-surface); box-shadow: none; transform: none; opacity: 1; transition: opacity 0.2s ease; @starting-style { opacity: 0; } }
2139
+ & .crisp-importdialog-header { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; height: 52px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-6); border-bottom: 1px solid var(--crisp-border-subtle); }
2140
+ & .crisp-importdialog-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); margin: 0; }
2141
+ & .crisp-importdialog-close { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
2142
+ & .crisp-importdialog-close:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
2143
+ & .crisp-importdialog-steps { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; padding: var(--crisp-space-4) var(--crisp-space-6) 0; }
2144
+ & .crisp-importdialog-step { display: inline-flex; align-items: center; height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); 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); cursor: default; transition: background-color .12s, color .12s; }
2145
+ & button.crisp-importdialog-step { cursor: pointer; }
2146
+ & .crisp-importdialog-step[data-active] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: color-mix(in srgb, var(--crisp-bg-brand-solid) 82%, var(--crisp-fg-primary)); }
2147
+ & .crisp-importdialog-step:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: 1px; }
2148
+ & .crisp-importdialog-body { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; padding: var(--crisp-space-6); }
2149
+ & .crisp-importdialog-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-3); flex-shrink: 0; min-height: 56px; padding: 0 var(--crisp-space-6); border-top: 1px solid var(--crisp-border-subtle); }
2150
+ & .crisp-importdialog-dropzone { box-sizing: border-box; position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; border: 1px dashed var(--crisp-border-dropzone); border-radius: 20px; background: var(--crisp-bg-subtle); text-align: center; transition: background-color .12s, border-color .12s; }
2151
+ & .crisp-importdialog-dropzone[data-dragging-over] { border-color: var(--crisp-bg-brand-solid); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 6%, transparent); }
2152
+ & .crisp-importdialog-dropzone[data-invalid] { border-color: var(--crisp-bg-danger); }
2153
+ & .crisp-importdialog-dropzone-title { color: var(--crisp-fg-primary); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); }
2154
+ & .crisp-importdialog-dropzone-or { color: var(--crisp-fg-muted); font-size: var(--crisp-text-md); line-height: 18px; }
2155
+ & .crisp-importdialog-dropzone-choose { border: none; background: none; padding: 0; margin: 0; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-bg-brand-solid); cursor: pointer; }
2156
+ & .crisp-importdialog-dropzone-choose:hover { color: var(--crisp-bg-brand-solid-pressed); }
2157
+ & .crisp-importdialog-dropzone-choose:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: 2px; border-radius: var(--crisp-radius-sm); }
2158
+ & .crisp-importdialog-dropzone-learnmore { color: var(--crisp-fg-muted); font-size: var(--crisp-text-md); text-decoration: underline; }
2159
+ & .crisp-importdialog-dropzone-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
2160
+ & .crisp-importdialog-dropzone-errors { margin: 0; padding: 0; list-style: none; }
2161
+ & .crisp-importdialog-dropzone-errors:empty { position: absolute; }
2162
+ & .crisp-importdialog-dropzone-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
2163
+ }
2164
+
2165
+ @media (prefers-reduced-motion: reduce) {
2166
+ .crisp-importdialog, .crisp-importdialog * {
2167
+ animation-duration: 0.01ms !important;
2168
+ animation-iteration-count: 1 !important;
2169
+ transition-duration: 0.01ms !important;
2170
+ }
2171
+ }
2172
+
2075
2173
  .crisp-inbox {
2076
2174
  display: flex; flex-direction: column; box-sizing: border-box; width: 380px; max-height: 588px; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); overflow: hidden; box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2077
2175
  & .crisp-inbox-head { padding: var(--crisp-space-3) var(--crisp-space-3) 0; }
@@ -2277,17 +2375,21 @@
2277
2375
  & .crisp-recordpage-link:hover { text-decoration: underline; }
2278
2376
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2279
2377
  & .crisp-recordpage-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; overflow-y: auto; padding: var(--crisp-space-3) var(--crisp-space-5); }
2280
- & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2378
+ /* no padding-top: Tabs' own panel supplies it now (#588) */
2379
+ & .crisp-recordpage-panel { display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2281
2380
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
2282
2381
  & .crisp-recordpage-card-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); letter-spacing: -0.14px; color: var(--crisp-fg-primary); padding: var(--crisp-space-3) var(--crisp-space-3) var(--crisp-space-2); }
2283
2382
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
2284
- /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any column count, so the grid stays responsive (auto-fit) without per-cell nth-child border hacks that break when the column count changes. */
2383
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
2384
+ column count, so the grid stays responsive (auto-fit) without per-cell
2385
+ nth-child border hacks that break when the column count changes. */
2285
2386
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
2286
2387
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
2287
2388
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
2288
2389
  & .crisp-recordpage-hl-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); margin-bottom: var(--crisp-space-2-5); padding-inline-end: var(--crisp-space-4); }
2289
2390
  & .crisp-recordpage-hl-value { display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2290
2391
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
2392
+
2291
2393
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
2292
2394
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
2293
2395
  & .crisp-recordpage-feed-dot { position: relative; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex-shrink: 0; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-raised); color: var(--crisp-fg-muted); }
@@ -2296,6 +2398,7 @@
2296
2398
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
2297
2399
  & .crisp-recordpage-feed-more { align-self: flex-start; margin-top: var(--crisp-space-1); border: none; background: none; padding: var(--crisp-space-1) 0; color: var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; }
2298
2400
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
2401
+
2299
2402
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
2300
2403
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
2301
2404
  & .crisp-recordpage-section-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
@@ -2303,6 +2406,7 @@
2303
2406
  & .crisp-recordpage-addbtn { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: 26px; padding: 0 var(--crisp-space-1-5); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; }
2304
2407
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
2305
2408
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
2409
+
2306
2410
  & .crisp-recordpage-composer { display: flex; flex-direction: column; gap: var(--crisp-space-2); padding: var(--crisp-space-2); border: 1px solid var(--crisp-border-default); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); margin-bottom: var(--crisp-space-2); }
2307
2411
  & .crisp-recordpage-composer textarea { width: 100%; min-height: 56px; resize: vertical; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
2308
2412
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -2312,18 +2416,23 @@
2312
2416
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
2313
2417
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
2314
2418
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
2419
+
2315
2420
  & .crisp-recordpage-note { display: flex; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2-5) 0; border-top: 1px solid var(--crisp-border-subtle); }
2316
2421
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
2317
2422
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
2318
2423
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2319
2424
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
2320
2425
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2426
+
2321
2427
  & .crisp-recordpage-task { display: flex; align-items: flex-start; gap: var(--crisp-space-2); padding: var(--crisp-space-1-5) 0; border-top: 1px solid var(--crisp-border-subtle); }
2322
2428
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
2323
2429
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
2430
+
2324
2431
  & .crisp-recordpage-emptycard { display: flex; flex-direction: column; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-6) var(--crisp-space-3); border: 1px dashed var(--crisp-border-default); border-radius: var(--crisp-radius-xl); color: var(--crisp-fg-muted); font-size: var(--crisp-text-md); text-align: center; }
2325
2432
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
2433
+
2326
2434
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
2435
+
2327
2436
  & .crisp-recordstable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
2328
2437
  & .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: color-mix(in srgb, var(--crisp-bg-brand-solid) 82%, var(--crisp-fg-primary)); }
2329
2438
  & .crisp-recordstable-tag[data-tone="success"] { background: color-mix(in srgb, var(--crisp-bg-success) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-success) 78%, var(--crisp-fg-primary)); }
@@ -2661,7 +2770,17 @@
2661
2770
  & .crisp-rte-body h1 { font-size: var(--crisp-text-xl); line-height: 28px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.3px; margin-top: var(--crisp-space-4); }
2662
2771
  & .crisp-rte-body h2 { font-size: 17px; line-height: 24px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.2px; margin-top: var(--crisp-space-3); }
2663
2772
  & .crisp-rte-body h3 { font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-semibold); margin-top: var(--crisp-space-2-5); }
2664
- & .crisp-rte-body ul, & .crisp-rte-body ol { padding-inline-start: var(--crisp-space-5); }
2773
+ /* #575: a consumer's CSS reset (e.g. Tailwind preflight's un-scoped ul, ol { list-style: none }
2774
+ , specificity 0,0,1) was winning every list marker outright -- this rule set padding but never declared list-style-type, so there was no higher-specificity value (this selector is 0,1,1) to beat the reset with. Split into two single-selector rules, each declaring its own marker type, rather than the old single comma-joined rule ( .crisp-rte-body ul, & .crisp-rte-body ol ) the issue flagged as malformed. #611: padding-inline-start re-measured live once CRUD on the measurement workspace was allowed (a "zz-parity" note with a bulleted list, a nested bulleted list, and a numbered list) -- Attio's own <ul>/<ol> measures 36px (getComputedStyle,
2775
+ ~36.1353px), not the 20px placeholder #575 shipped with no real
2776
+ content to check against. 36px isn't in tokens/scale.ts (steps stop at 24px, jump to 32px) and is a single component-intrinsic value, so it stays a raw px per AUTHORING.md's golden rule 4b
2777
+ rather than being rounded to an existing token or added to the
2778
+ shared scale. Marker colour (currentColor via the already-measured
2779
+ fgPrimary, confirmed to equal Attio's measured rgb(16,17,18) == #101112 exactly) and the ~4px li-to-li gap (the existing li margin-top rule below) were re-checked at the same time and already matched -- only the padding was wrong. Attio doesn't vary
2780
+ marker type for a nested list either (nested <ul> is disc too,
2781
+ not circle), which this already doesn't attempt. */
2782
+ & .crisp-rte-body ul { list-style-type: disc; padding-inline-start: 36px; }
2783
+ & .crisp-rte-body ol { list-style-type: decimal; padding-inline-start: 36px; }
2665
2784
  & .crisp-rte-body li { margin-top: var(--crisp-space-1); }
2666
2785
  & .crisp-rte-body blockquote { padding-inline-start: var(--crisp-space-3); border-inline-start: 2px solid var(--crisp-border-default); color: var(--crisp-fg-muted); }
2667
2786
  & .crisp-rte-body code { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; font-size: var(--crisp-text-md); padding: 1px var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); }
@@ -2709,6 +2828,85 @@
2709
2828
  }
2710
2829
  }
2711
2830
 
2831
+
2832
+ /* pointer-events: auto (#375) — see Menu/MultiSelect's matching comment:
2833
+ a modal Dialog sets pointer-events: none on document.body, and a
2834
+ portaled layer outside Zag's knowledge inherits that lock. */
2835
+ .crisp-savedviews-panel {
2836
+ font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2837
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
2838
+ min-width: 200px; max-height: 360px; overflow-y: auto; padding: var(--crisp-space-1);
2839
+ border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
2840
+ box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3);
2841
+ transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
2842
+ }
2843
+ .crisp-savedviews-panel[data-up] { transform-origin: bottom left; }
2844
+ .crisp-savedviews-panel { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
2845
+
2846
+ .crisp-savedviews-row {
2847
+ box-sizing: border-box; position: relative; display: flex; align-items: center; gap: var(--crisp-space-1-5);
2848
+ height: var(--crisp-size-menuRow); flex-shrink: 0; padding: var(--crisp-space-1-5) var(--crisp-space-2);
2849
+ border-radius: var(--crisp-radius-md); cursor: pointer; -webkit-user-select: none; user-select: none;
2850
+ }
2851
+ .crisp-savedviews-row:hover, .crisp-savedviews-row[data-highlighted] { background: var(--crisp-bg-raised); }
2852
+ .crisp-savedviews-row-icon { flex-shrink: 0; display: inline-flex; width: var(--crisp-size-icon); height: var(--crisp-size-icon); color: var(--crisp-fg-muted); }
2853
+ .crisp-savedviews-row-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2854
+ .crisp-savedviews-row[aria-selected="true"] .crisp-savedviews-row-label { font-weight: var(--crisp-weight-semibold); }
2855
+ .crisp-savedviews-row-shared { flex-shrink: 0; display: inline-flex; color: var(--crisp-fg-muted); }
2856
+
2857
+ /* The kebab's own <Menu> is wrapped in a slot div (savedviews.tsx) that
2858
+ stops a click from bubbling — through the REACT tree, which a portal
2859
+ does not leave, even though the Menu's content is a DOM sibling under
2860
+ document.body — into the row's own onClick. The slot, not the kebab
2861
+ button itself, is what the row's flex layout pushes to the trailing
2862
+ edge. */
2863
+ .crisp-savedviews-kebab-slot { flex-shrink: 0; margin-inline-start: auto; }
2864
+ /* 20px / 4px padding is a component-intrinsic size (AUTHORING.md §4b —
2865
+ "a badge's 20px" is the named example), used once here: raw, not a
2866
+ scale step. */
2867
+ .crisp-savedviews-kebab {
2868
+ box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
2869
+ width: 20px; height: 20px; padding: var(--crisp-space-1); margin: 0;
2870
+ border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); cursor: pointer;
2871
+ opacity: 0; transition: opacity .1s ease, background-color .12s ease, color .12s ease;
2872
+ }
2873
+ .crisp-savedviews-row:hover .crisp-savedviews-kebab,
2874
+ .crisp-savedviews-row:focus-within .crisp-savedviews-kebab,
2875
+ .crisp-savedviews-kebab[data-open] { opacity: 1; }
2876
+ .crisp-savedviews-kebab:hover, .crisp-savedviews-kebab[data-open] { background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); }
2877
+
2878
+ .crisp-savedviews {
2879
+ position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; /* Trigger fill measured rgb(246,247,247); bgRaised's light value is
2880
+ #f4f4f5 (244,244,245) — a 2-unit-per-channel difference, inside
2881
+ screenshot/anti-aliasing noise, not a second real colour. Reusing the
2882
+ token (Lesson 3 — tokens, not raw px/hex) gets a correct,
2883
+ already-established dark-mode fill for free instead of a light-only
2884
+ literal; AUTHORING.md Lesson 29 is the case where that shortcut went
2885
+ wrong (a flat colour that stayed visible where the reference had
2886
+ none) — this is the opposite risk (a token that already flips per
2887
+ theme, applied to the closest measured match). */
2888
+ & .crisp-savedviews-trigger {
2889
+ box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--crisp-space-1-5);
2890
+ height: var(--crisp-size-row); margin: 0; padding: var(--crisp-space-1) var(--crisp-space-1-5);
2891
+ border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised);
2892
+ font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary);
2893
+ cursor: pointer; white-space: nowrap; transition: background-color .12s ease;
2894
+ }
2895
+ & .crisp-savedviews-trigger:hover,
2896
+ & .crisp-savedviews-trigger[data-open] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-raised)); }
2897
+ & .crisp-savedviews-trigger-icon { flex-shrink: 0; display: inline-flex; width: var(--crisp-size-icon); height: var(--crisp-size-icon); }
2898
+ & .crisp-savedviews-trigger-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
2899
+ & .crisp-savedviews-trigger-chevron { flex-shrink: 0; color: var(--crisp-fg-muted); }
2900
+ }
2901
+
2902
+ @media (prefers-reduced-motion: reduce) {
2903
+ .crisp-savedviews, .crisp-savedviews * {
2904
+ animation-duration: 0.01ms !important;
2905
+ animation-iteration-count: 1 !important;
2906
+ transition-duration: 0.01ms !important;
2907
+ }
2908
+ }
2909
+
2712
2910
  .crisp-settingssection {
2713
2911
  display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans);
2714
2912
  & .crisp-settingssection-head { display: flex; align-items: flex-start; gap: var(--crisp-space-2); }
@@ -2831,6 +3029,58 @@
2831
3029
  }
2832
3030
  }
2833
3031
 
3032
+ .crisp-taskstable {
3033
+ display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
3034
+ & .crisp-taskstable-colhead { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-height: 49px; max-height: 49px; padding: var(--crisp-space-2-5) var(--crisp-space-3); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
3035
+ & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
3036
+ & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
3037
+ & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; }
3038
+ & .crisp-taskstable-check { appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); cursor: pointer; transition: background-color .12s, box-shadow .12s; }
3039
+ & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
3040
+ & .crisp-taskstable-check:checked { background: var(--crisp-bg-brand-solid) center / 10px no-repeat; box-shadow: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
3041
+ & .crisp-taskstable-check:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
3042
+ & .crisp-taskstable-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
3043
+ & .crisp-taskstable-due { font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); white-space: nowrap; }
3044
+ & .crisp-taskstable-due[data-overdue] { color: var(--crisp-fg-danger); }
3045
+ & .crisp-taskstable-empty { padding: var(--crisp-space-8) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
3046
+ }
3047
+
3048
+ @media (prefers-reduced-motion: reduce) {
3049
+ .crisp-taskstable, .crisp-taskstable * {
3050
+ animation-duration: 0.01ms !important;
3051
+ animation-iteration-count: 1 !important;
3052
+ transition-duration: 0.01ms !important;
3053
+ }
3054
+ }
3055
+
3056
+ .crisp-themepicker {
3057
+ display: flex; gap: var(--crisp-space-2); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
3058
+ & .crisp-themepicker-option { box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: var(--crisp-space-1-5); width: 224px; padding: var(--crisp-space-1); border: 1px solid var(--crisp-border-subtle); border-radius: 16px; background: var(--crisp-bg-surface); cursor: pointer; font-family: inherit; }
3059
+ & .crisp-themepicker-option[data-selected] { border-color: var(--crisp-bg-brand-solid); }
3060
+ & .crisp-themepicker-preview { box-sizing: border-box; display: flex; width: 100%; height: 114px; padding: var(--crisp-space-2); gap: var(--crisp-space-1-5); border-radius: var(--crisp-radius-xl); overflow: hidden; }
3061
+ & .crisp-themepicker-preview-side { flex-shrink: 0; width: 28%; display: flex; flex-direction: column; gap: var(--crisp-space-1); }
3062
+ & .crisp-themepicker-preview-bar { height: 4px; border-radius: var(--crisp-radius-full); }
3063
+ & .crisp-themepicker-preview-bar:nth-child(1) { width: 70%; background: var(--crisp-bg-brand-solid); }
3064
+ & .crisp-themepicker-preview-bar:nth-child(2) { width: 90%; }
3065
+ & .crisp-themepicker-preview-bar:nth-child(3) { width: 60%; }
3066
+ & .crisp-themepicker-preview-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1-5); border-radius: var(--crisp-radius-md); }
3067
+ /* Light / System (default) swatch colours. */
3068
+ & .crisp-themepicker-preview[data-swatch="light"], & .crisp-themepicker-preview[data-swatch="system"] { border: 1px solid #eeeff1; background: #fbfbfb; }
3069
+ & [data-swatch="light"] .crisp-themepicker-preview-bar:not(:nth-child(1)), & [data-swatch="system"] .crisp-themepicker-preview-bar:not(:nth-child(1)) { background: #eeeff1; }
3070
+ & [data-swatch="light"] .crisp-themepicker-preview-main, & [data-swatch="system"] .crisp-themepicker-preview-main { background: #ffffff; }
3071
+ & [data-swatch="light"] .crisp-themepicker-preview-main .crisp-themepicker-preview-bar, & [data-swatch="system"] .crisp-themepicker-preview-main .crisp-themepicker-preview-bar { background: #eeeff1; }
3072
+ /* Dark swatch colours -- always for "dark", and for "system" only when the OS itself reports dark. */
3073
+ & .crisp-themepicker-preview[data-swatch="dark"] { border: 1px solid #1c1d1f; background: #0e0f10; }
3074
+ & [data-swatch="dark"] .crisp-themepicker-preview-bar:not(:nth-child(1)) { background: #1c1d1f; }
3075
+ & [data-swatch="dark"] .crisp-themepicker-preview-main { background: #0a0a0a; }
3076
+ & [data-swatch="dark"] .crisp-themepicker-preview-main .crisp-themepicker-preview-bar { background: #1c1d1f; }
3077
+ @media (prefers-color-scheme: dark) { & .crisp-themepicker-preview[data-swatch="system"] { border: 1px solid #1c1d1f; background: #0e0f10; } & [data-swatch="system"] .crisp-themepicker-preview-bar:not(:nth-child(1)) { background: #1c1d1f; } & [data-swatch="system"] .crisp-themepicker-preview-main { background: #0a0a0a; } & [data-swatch="system"] .crisp-themepicker-preview-main .crisp-themepicker-preview-bar { background: #1c1d1f; } }
3078
+ & .crisp-themepicker-label { display: flex; align-items: center; gap: var(--crisp-space-1); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
3079
+ & .crisp-themepicker-option[data-selected] .crisp-themepicker-label { color: var(--crisp-fg-primary); }
3080
+ & .crisp-themepicker-radio { flex-shrink: 0; width: 14px; height: 14px; border-radius: var(--crisp-radius-full); border: 1px solid var(--crisp-border-default); box-sizing: border-box; }
3081
+ & .crisp-themepicker-option[data-selected] .crisp-themepicker-radio { border: 4px solid var(--crisp-bg-brand-solid); }
3082
+ }
3083
+
2834
3084
  .crisp-trialbanner {
2835
3085
  display: flex; align-items: center; gap: var(--crisp-space-1-5); box-sizing: border-box; min-height: 28px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2836
3086
  & .crisp-trialbanner-icon { display: inline-flex; flex-shrink: 0; color: var(--crisp-fg-muted); }
package/dist/table.css CHANGED
@@ -1,15 +1,24 @@
1
1
  .crisp-table {
2
- display: block; box-sizing: border-box; background: var(--crisp-bg-surface);
3
- & .crisp-table-scroll { overflow-x: auto; }
2
+ display: block; box-sizing: border-box; background: var(--crisp-bg-surface); /* #554: both axes -- a consumer bounds the height via `scrollProps` (style/className on this element), and the header/footer below pin inside it exactly the way Attio's own grid does ("Both the header and
3
+ the summary row are sticky, on the scroll container rather than the
4
+ cells" -- docs/study/attio-table.md). Unbounded, `overflow: auto`
5
+ shows no vertical scrollbar and nothing sticks, so this is a no-op
6
+ for every existing consumer that never set a height. */
7
+ & .crisp-table-scroll { overflow: auto; }
4
8
  & table { width: 100%; border-collapse: separate; border-spacing: 0; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
5
- /* #465: 12px, not crisp's old 8px -- measured on a live header cell
6
- (padding: 0 12px) and independently on a body cell's text inset (getBoundingClientRect delta from the cell edge). Matches ColumnCalc's own already-correct 12px trigger inset. */
9
+ /* #465: 12px, not crisp's old 8px -- measured on a live header cell (padding: 0 12px) and independently on a body cell's text inset
10
+ (getBoundingClientRect delta from the cell edge). Matches
11
+ ColumnCalc's own already-correct 12px trigger inset. */
7
12
  & th, & td { position: relative; height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-3); border-bottom: 1px solid var(--crisp-border-subtle); border-inline-end: 1px solid var(--crisp-border-subtle); text-align: start; vertical-align: middle; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
8
- & th { height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); user-select: none; }
13
+ /* #554: sticky on the vertical axis, mirroring RecordsTable's own
14
+ `.crisp-recordstable-th`. z-index 2 sits above a plain body cell (auto)
15
+ and below a sticky-column corner cell (DataTable's `th[data-sticky]`, z-index 3), so the corner always wins. */
16
+ & th { position: sticky; top: 0; z-index: 2; height: var(--crisp-size-tableHeader); background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); user-select: none; }
9
17
  & td { color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); }
10
18
  & th:last-child, & td:last-child { border-inline-end: none; }
11
19
  & tbody tr:last-child td { border-bottom: none; }
12
- & tfoot td { border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
20
+ /* #554: sticky to the bottom, mirroring `.crisp-recordstable-sum`. */
21
+ & tfoot td { position: sticky; bottom: 0; z-index: 2; border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
13
22
  & caption { padding: var(--crisp-space-2) var(--crisp-space-2); text-align: start; font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); caption-side: bottom; }
14
23
  & td[data-align="right"], & th[data-align="right"] { text-align: end; }
15
24
  & td[data-align="center"], & th[data-align="center"] { text-align: center; }