@crisp-ui-kit/crisp 0.54.2 → 0.54.4

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 (116) hide show
  1. package/dist/board.css +13 -1
  2. package/dist/board.layered.css +13 -1
  3. package/dist/chip.css +7 -1
  4. package/dist/chip.d.ts +2 -1
  5. package/dist/chip.layered.css +7 -1
  6. package/dist/cjs/components/chip/chip.js +3 -2
  7. package/dist/cjs/components/chip/chip.recipe.js +37 -1
  8. package/dist/cjs/components/datatable/datatable.js +489 -111
  9. package/dist/cjs/components/emptystate/emptystate.js +3 -2
  10. package/dist/cjs/components/emptystate/emptystate.recipe.js +26 -1
  11. package/dist/cjs/components/personchip/index.js +17 -0
  12. package/dist/cjs/components/personchip/personchip.js +50 -0
  13. package/dist/cjs/components/table/table.js +2 -2
  14. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +41 -4
  15. package/dist/cjs/components/tabs/tabs.js +2 -2
  16. package/dist/cjs/components/tabs/tabs.recipe.js +30 -3
  17. package/dist/cjs/components/text/index.js +7 -0
  18. package/dist/cjs/components/text/text.js +55 -0
  19. package/dist/cjs/components/text/text.recipe.js +34 -0
  20. package/dist/cjs/index.js +4 -0
  21. package/dist/cjs/product/board/board.js +60 -2
  22. package/dist/cjs/product/board/board.recipe.js +10 -0
  23. package/dist/cjs/product/comments/comments.js +426 -0
  24. package/dist/cjs/product/comments/comments.recipe.js +126 -0
  25. package/dist/cjs/product/comments/index.js +8 -0
  26. package/dist/cjs/product/headerbar/headerbar.recipe.js +9 -1
  27. package/dist/cjs/product/listpage/listpage.recipe.js +10 -1
  28. package/dist/cjs/product/objectsettings/index.js +16 -0
  29. package/dist/cjs/product/objectsettings/objectsettings.js +365 -0
  30. package/dist/cjs/product/objectsettings/objectsettings.recipe.js +65 -0
  31. package/dist/cjs/product/recordpage/recordpage.recipe.js +10 -1
  32. package/dist/cjs/product/recordstable/recordstable.js +89 -6
  33. package/dist/cjs/product/richtext/richtext.recipe.js +26 -1
  34. package/dist/cjs/product/taskstable/taskstable.recipe.js +10 -1
  35. package/dist/comments.css +80 -0
  36. package/dist/comments.d.ts +7 -0
  37. package/dist/comments.layered.css +82 -0
  38. package/dist/emptystate.css +15 -1
  39. package/dist/emptystate.d.ts +2 -2
  40. package/dist/emptystate.layered.css +15 -1
  41. package/dist/esm/components/chip/chip.js +3 -2
  42. package/dist/esm/components/chip/chip.recipe.js +37 -1
  43. package/dist/esm/components/datatable/datatable.js +488 -111
  44. package/dist/esm/components/emptystate/emptystate.js +4 -3
  45. package/dist/esm/components/emptystate/emptystate.recipe.js +26 -1
  46. package/dist/esm/components/emptystate/index.js +1 -1
  47. package/dist/esm/components/personchip/index.js +1 -0
  48. package/dist/esm/components/personchip/personchip.js +47 -0
  49. package/dist/esm/components/table/table.js +2 -2
  50. package/dist/esm/components/tabletoolbar/tabletoolbar.js +43 -6
  51. package/dist/esm/components/tabs/tabs.js +2 -2
  52. package/dist/esm/components/tabs/tabs.recipe.js +30 -3
  53. package/dist/esm/components/text/index.js +2 -0
  54. package/dist/esm/components/text/text.js +19 -0
  55. package/dist/esm/components/text/text.recipe.js +31 -0
  56. package/dist/esm/index.js +4 -0
  57. package/dist/esm/product/board/board.js +61 -3
  58. package/dist/esm/product/board/board.recipe.js +10 -0
  59. package/dist/esm/product/comments/comments.js +388 -0
  60. package/dist/esm/product/comments/comments.recipe.js +123 -0
  61. package/dist/esm/product/comments/index.js +2 -0
  62. package/dist/esm/product/headerbar/headerbar.recipe.js +9 -1
  63. package/dist/esm/product/listpage/listpage.recipe.js +10 -1
  64. package/dist/esm/product/objectsettings/index.js +2 -0
  65. package/dist/esm/product/objectsettings/objectsettings.js +322 -0
  66. package/dist/esm/product/objectsettings/objectsettings.recipe.js +62 -0
  67. package/dist/esm/product/recordpage/recordpage.recipe.js +10 -1
  68. package/dist/esm/product/recordstable/recordstable.js +90 -8
  69. package/dist/esm/product/richtext/richtext.recipe.js +26 -1
  70. package/dist/esm/product/taskstable/taskstable.recipe.js +10 -1
  71. package/dist/headerbar.css +1 -1
  72. package/dist/headerbar.layered.css +1 -1
  73. package/dist/listpage.css +8 -1
  74. package/dist/listpage.layered.css +8 -1
  75. package/dist/objectsettings.css +49 -0
  76. package/dist/objectsettings.d.ts +7 -0
  77. package/dist/objectsettings.layered.css +51 -0
  78. package/dist/recordpage.css +13 -2
  79. package/dist/recordpage.layered.css +13 -2
  80. package/dist/richtext.css +11 -1
  81. package/dist/richtext.layered.css +11 -1
  82. package/dist/styles.css +224 -10
  83. package/dist/styles.layered.css +224 -10
  84. package/dist/tabs.css +7 -1
  85. package/dist/tabs.d.ts +2 -2
  86. package/dist/tabs.layered.css +7 -1
  87. package/dist/taskstable.css +8 -1
  88. package/dist/taskstable.layered.css +8 -1
  89. package/dist/text.css +9 -0
  90. package/dist/text.d.ts +7 -0
  91. package/dist/text.layered.css +11 -0
  92. package/dist/types/components/chip/chip.d.ts +32 -0
  93. package/dist/types/components/chip/chip.recipe.d.ts +31 -0
  94. package/dist/types/components/datatable/datatable.d.ts +28 -4
  95. package/dist/types/components/emptystate/emptystate.d.ts +29 -4
  96. package/dist/types/components/emptystate/emptystate.recipe.d.ts +2 -0
  97. package/dist/types/components/emptystate/index.d.ts +1 -1
  98. package/dist/types/components/personchip/index.d.ts +1 -0
  99. package/dist/types/components/personchip/personchip.d.ts +37 -0
  100. package/dist/types/components/table/table.d.ts +36 -0
  101. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +8 -2
  102. package/dist/types/components/tabs/tabs.d.ts +4 -2
  103. package/dist/types/components/tabs/tabs.recipe.d.ts +18 -3
  104. package/dist/types/components/text/index.d.ts +2 -0
  105. package/dist/types/components/text/text.d.ts +32 -0
  106. package/dist/types/components/text/text.recipe.d.ts +40 -0
  107. package/dist/types/index.d.ts +4 -0
  108. package/dist/types/product/board/board.d.ts +20 -1
  109. package/dist/types/product/comments/comments.d.ts +192 -0
  110. package/dist/types/product/comments/comments.recipe.d.ts +3 -0
  111. package/dist/types/product/comments/index.d.ts +2 -0
  112. package/dist/types/product/objectsettings/index.d.ts +2 -0
  113. package/dist/types/product/objectsettings/objectsettings.d.ts +261 -0
  114. package/dist/types/product/objectsettings/objectsettings.recipe.d.ts +3 -0
  115. package/dist/types/product/recordstable/recordstable.d.ts +24 -2
  116. package/package.json +21 -1
@@ -448,7 +448,13 @@
448
448
  }
449
449
 
450
450
  .crisp-chip {
451
- 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
+
452
458
  }
453
459
  .crisp-chip--tone_neutral {
454
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);
@@ -914,7 +920,16 @@
914
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); }
915
921
  & .crisp-emptystate-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
916
922
  & .crisp-emptystate-desc { max-width: 320px; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
917
- & .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); }
918
933
  }
919
934
  .crisp-emptystate--variant_default {
920
935
 
@@ -929,6 +944,11 @@
929
944
  & .crisp-emptystate-desc { max-width: 266px; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); line-height: 20px; }
930
945
  & .crisp-emptystate-action { margin-top: var(--crisp-space-2); }
931
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
+ }
932
952
 
933
953
  .crisp-fileupload {
934
954
  display: flex; flex-direction: column; gap: var(--crisp-space-2); font-family: var(--crisp-font-sans); min-width: 0;
@@ -1774,9 +1794,15 @@
1774
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); }
1775
1795
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
1776
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; }
1777
- & .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); }
1778
1798
  & .crisp-tabs-panel:focus-visible { outline: none; }
1779
1799
  }
1800
+ .crisp-tabs--fill_true {
1801
+ height: 100%;
1802
+ }
1803
+ .crisp-tabs--fill_false {
1804
+
1805
+ }
1780
1806
 
1781
1807
  @media (prefers-reduced-motion: reduce) {
1782
1808
  .crisp-tabs, .crisp-tabs * {
@@ -1786,6 +1812,16 @@
1786
1812
  }
1787
1813
  }
1788
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
+
1789
1825
  .crisp-textarea {
1790
1826
  position: relative; display: block; width: 100%; font-family: var(--crisp-font-sans);
1791
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; }
@@ -1990,8 +2026,20 @@
1990
2026
  & .crisp-board-card[data-dragging] { opacity: 0.4; }
1991
2027
  & .crisp-board-card:focus { outline: none; }
1992
2028
  & .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
2029
+ /* #604: a per-column calc footer, mirroring the table's own (measured
2030
+ live, Deals pipeline). Sits below the scrollable card body, not
2031
+ inside it or pinned to it -- Attio's own is position: static, not sticky, so it's a plain flex-shrink:0 sibling here too. */
2032
+ & .crisp-board-col-footer { flex-shrink: 0; padding: var(--crisp-space-1) 0; }
2033
+ & .crisp-board-col-calc { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; height: 28px; padding: var(--crisp-space-1) var(--crisp-space-2); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); cursor: pointer; }
2034
+ & .crisp-board-col-calc:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
2035
+ & .crisp-board-col-calc[data-empty] { opacity: 0.7; }
2036
+ & .crisp-board-col-calc-label { text-transform: capitalize; }
2037
+ & .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
1993
2038
  /* announced, never drawn */
1994
- & .crisp-board-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
2039
+ & .crisp-board-live {
2040
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
2041
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
2042
+ }
1995
2043
  }
1996
2044
 
1997
2045
  @media (prefers-reduced-motion: reduce) {
@@ -2043,6 +2091,87 @@
2043
2091
  }
2044
2092
  }
2045
2093
 
2094
+ .crisp-comments {
2095
+ display: flex; flex-direction: column; box-sizing: border-box; width: 320px; height: 100%; background: var(--crisp-bg-surface); border-inline-start: 1px solid var(--crisp-border-subtle); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2096
+ & * { box-sizing: border-box; }
2097
+ & .crisp-comments-head { display: flex; align-items: center; gap: var(--crisp-space-1); flex-shrink: 0; height: 49px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-4); }
2098
+ & .crisp-comments-headbtn { 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: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
2099
+ & .crisp-comments-headbtn:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2100
+ & .crisp-comments-title-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; }
2101
+ & .crisp-comments-title { font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2102
+ & .crisp-comments-breadcrumb { display: flex; align-items: center; gap: var(--crisp-space-1); font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2103
+ & .crisp-comments-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2104
+ & .crisp-comments-body--pad-top { padding-top: var(--crisp-space-3); }
2105
+ /* One comment row -- avatar 16px (Avatar size="sm"), 6px gap to the name/time line, 14px/600 name + 12px/500 63%-muted time, 14px/400 body two lines below. Hover actions fade in flush with the name line. */
2106
+ & .crisp-comments-row { position: relative; display: flex; gap: var(--crisp-space-1-5); }
2107
+ & .crisp-comments-row-main { flex: 1; min-width: 0; }
2108
+ & .crisp-comments-row-head { display: flex; align-items: baseline; gap: var(--crisp-space-1-5); height: 20px; }
2109
+ & .crisp-comments-author { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
2110
+ & .crisp-comments-time { font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); white-space: nowrap; }
2111
+ & .crisp-comments-resolved-label { display: flex; align-items: center; gap: var(--crisp-space-1); margin-bottom: var(--crisp-space-1); font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2112
+ & .crisp-comments-body-text { font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2113
+ /* Hover actions -- measured: 4 buttons, 28x28, radius 8, flush side-by-side (0 gap), transparent by default, 16px icon at 55% ink. */
2114
+ & .crisp-comments-row-actions { position: absolute; top: 0; inset-inline-end: 0; display: flex; opacity: 0; pointer-events: none; background: var(--crisp-bg-surface); }
2115
+ & .crisp-comments-row:hover .crisp-comments-row-actions, & .crisp-comments-row-actions:focus-within { opacity: 1; pointer-events: auto; }
2116
+ & .crisp-comments-action { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: transparent; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
2117
+ & .crisp-comments-action:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2118
+ & .crisp-comments-action[data-active] { color: var(--crisp-bg-brand-solid); }
2119
+ /* Reactions -- measured: 28px pill, radius 8, subtle 4% ink fill, padding 4px 8px 4px 6px, gap 4px between the emoji glyph and count. */
2120
+ & .crisp-comments-reactions { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-1-5); }
2121
+ & .crisp-comments-reaction { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: 28px; padding: 4px 8px 4px 6px; border: none; border-radius: var(--crisp-radius-md); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); font-family: inherit; font-size: 12px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); cursor: pointer; }
2122
+ & .crisp-comments-reaction[data-reacted] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 12%, transparent); color: var(--crisp-bg-brand-solid); }
2123
+ & .crisp-comments-reaction-add { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: none; border-radius: var(--crisp-radius-md); background: transparent; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; opacity: 0; transition: opacity .12s, background-color .12s; }
2124
+ & .crisp-comments-row:hover .crisp-comments-reaction-add { opacity: 1; }
2125
+ & .crisp-comments-reaction-add:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2126
+ /* Reply-in-thread view -- an indented block of replies below the root. */
2127
+ & .crisp-comments-replies { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); padding-inline-start: var(--crisp-space-4); border-inline-start: 1px solid var(--crisp-border-subtle); }
2128
+ & .crisp-comments-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--crisp-space-1); padding: var(--crisp-space-6) var(--crisp-space-4); text-align: center; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-size: var(--crisp-text-lg); }
2129
+ /* Composer -- measured: 14px/20px body text, 16px gap above the reply/mention icon row, Comment button 84x32 padding 6px 10px radius 9 (= radius.lg) brand-solid fill, 14px/500 white label, opacity .4 disabled / 1 enabled (no color/background change between the two). */
2130
+ & .crisp-comments-composer { flex-shrink: 0; padding: var(--crisp-space-3) var(--crisp-space-4) var(--crisp-space-4); border-top: 1px solid var(--crisp-border-subtle); }
2131
+ & .crisp-comments-field { min-height: 20px; max-height: 120px; overflow-y: auto; font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); outline: none; white-space: pre-wrap; word-break: break-word; }
2132
+ & .crisp-comments-field:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: 2px; }
2133
+ & .crisp-comments-field:empty::before { content: attr(data-placeholder); color: color-mix(in srgb, var(--crisp-fg-primary) 40%, transparent); }
2134
+ & .crisp-comments-composer-foot { display: flex; align-items: center; justify-content: space-between; margin-top: var(--crisp-space-4); }
2135
+ & .crisp-comments-composer-tools { display: flex; align-items: center; gap: var(--crisp-space-1); }
2136
+ & .crisp-comments-tool { 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; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); font-family: inherit; cursor: pointer; }
2137
+ & .crisp-comments-tool:hover { color: var(--crisp-fg-primary); }
2138
+ /* Same fill/shadow as Button's own intent="primary" (crisp/button.recipe.ts) --
2139
+ confirmed byte-identical against the live "Comment" button's computed box-shadow, so this reuses that literal rather than re-deriving it. 32px/radius.lg/6px-10px padding has no matching Button `size` yet (24/28/40 are the three that exist), so this stays hand-rolled instead of stretching one of those. */
2140
+ & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-lg); background: var(--crisp-bg-brand-solid); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-on-brand); cursor: pointer; opacity: 1; transition: opacity .12s, background-color .12s; }
2141
+ & .crisp-comments-post:hover:not(:disabled) { background: var(--crisp-bg-brand-solid-pressed); }
2142
+ & .crisp-comments-post:disabled { opacity: .4; cursor: default; }
2143
+ /* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding, elevation.floating -- a plain reuse of crisp's own Menu, so no rules
2144
+ live here; kept as a comment for the record. */
2145
+
2146
+ /* An inserted "@mention" chip in the composer -- same shape as
2147
+ RichTextEditor's own `.crisp-rte-mention` (crisp#428), reused here rather than re-derived since it's the same atomic-chip affordance. */
2148
+ & .crisp-comments-mention { display: inline; padding: 0 2px; border-radius: 4px; background: color-mix(in srgb, var(--crisp-bg-brand-solid) 12%, transparent); color: var(--crisp-bg-brand-solid); font-weight: var(--crisp-weight-medium); user-select: none; }
2149
+
2150
+ /* Reaction picker -- see `ReactionPicker`'s own doc comment: the exact chrome wasn't independently pixel-measured this pass (screenshot
2151
+ capture broke mid-session), so this reuses Menu's already-measured floating-popover shell rather than a new value. */
2152
+ & .crisp-comments-reactionpicker { width: 240px; padding: var(--crisp-space-2); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); 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); }
2153
+ & .crisp-comments-reactionpicker-search { box-sizing: border-box; width: 100%; height: 28px; margin-bottom: var(--crisp-space-2); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised); font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); }
2154
+ & .crisp-comments-reactionpicker-search::placeholder { color: color-mix(in srgb, var(--crisp-fg-primary) 40%, transparent); }
2155
+ & .crisp-comments-reactionpicker-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: var(--crisp-space-0-5); max-height: 160px; overflow-y: auto; }
2156
+ & .crisp-comments-reactionpicker-item { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; font-size: 16px; line-height: 1; cursor: pointer; }
2157
+ & .crisp-comments-reactionpicker-item:hover { background: var(--crisp-bg-raised); }
2158
+ /* Row badge (CommentCountBadge) -- lives in its own class family so the grid lanes can drop it into a cell without pulling in the panel's
2159
+ CSS. Persistent state: a 6px grey dot. Hover state: a 12px comment
2160
+ icon + 12px count text, both at 55% ink, replacing the dot. */
2161
+ & .crisp-comments-badge { display: inline-flex; align-items: center; gap: var(--crisp-space-1); border: none; background: none; padding: 0; margin: 0; font-family: inherit; cursor: pointer; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); }
2162
+ & .crisp-comments-badge-dot { width: 6px; height: 6px; border-radius: var(--crisp-radius-full); background: color-mix(in srgb, var(--crisp-fg-primary) 38%, transparent); }
2163
+ & .crisp-comments-badge-count { font-size: 12px; line-height: 16px; }
2164
+ [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2165
+ }
2166
+
2167
+ @media (prefers-reduced-motion: reduce) {
2168
+ .crisp-comments, .crisp-comments * {
2169
+ animation-duration: 0.01ms !important;
2170
+ animation-iteration-count: 1 !important;
2171
+ transition-duration: 0.01ms !important;
2172
+ }
2173
+ }
2174
+
2046
2175
  .crisp-entitycard {
2047
2176
  display: flex; flex-direction: column; box-sizing: border-box; width: 100%; min-width: 0; text-align: start; padding: var(--crisp-space-3) var(--crisp-space-4); margin: 0; border: none; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); color: var(--crisp-fg-primary); font-family: var(--crisp-font-sans); cursor: pointer; transition: box-shadow .12s ease, background-color .12s ease; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2048
2177
  &:hover { box-shadow: inset 0 0 0 1px var(--crisp-border-default), 0 1px 2px rgba(0,0,0,0.04), 0 6px 16px -6px rgba(28,40,64,0.12); }
@@ -2089,7 +2218,7 @@
2089
2218
 
2090
2219
  .crisp-headerbar {
2091
2220
  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;
2092
- &[data-bordered] { border-bottom: 1px solid var(--crisp-border-subtle); }
2221
+ &[data-bordered] { height: calc(var(--crisp-size-header) + 1px); border-bottom: 1px solid var(--crisp-border-subtle); }
2093
2222
  & .crisp-headerbar-leading { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; min-width: 0; }
2094
2223
  & .crisp-headerbar-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--crisp-space-2); overflow: hidden; }
2095
2224
  & .crisp-headerbar-trailing { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; }
@@ -2173,7 +2302,14 @@
2173
2302
  & .crisp-listpage-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-header); flex-shrink: 0; padding: 0 var(--crisp-space-3); }
2174
2303
  & .crisp-listpage-toolbar-lead { display: flex; align-items: center; gap: var(--crisp-space-2); }
2175
2304
  & .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
2176
- & .crisp-listpage-toolbar-trail { display: flex; align-items: center; gap: var(--crisp-space-2); }
2305
+ /* #641: trailing (View settings / New) has to reach the toolbar's own
2306
+ inline-end edge on its own -- the only thing pushing it there used
2307
+ to be .crisp-listpage-count's margin-inline-start: auto, which a consumer with no count badge (TasksTable, measured live: trailing sat right after leading, nowhere near the row's own right edge)
2308
+ never renders. A bare margin-inline-start: auto here is safely
2309
+ overridden by the more specific rule right below whenever count IS
2310
+ present, so the two cases don't fight -- count's own auto margin
2311
+ still does the pushing there, this one only fires when it's absent. */
2312
+ & .crisp-listpage-toolbar-trail { display: flex; align-items: center; gap: var(--crisp-space-2); margin-inline-start: auto; }
2177
2313
  & .crisp-listpage-count + .crisp-listpage-toolbar-trail { margin-inline-start: var(--crisp-space-3); }
2178
2314
  & .crisp-listpage-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--crisp-space-3) var(--crisp-space-3); }
2179
2315
  & .crisp-listpage-section-label { padding: 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); }
@@ -2214,6 +2350,56 @@
2214
2350
  }
2215
2351
  }
2216
2352
 
2353
+ .crisp-objectsettings {
2354
+ & .crisp-objectsettings-page { display: flex; flex-direction: column; gap: var(--crisp-space-6); }
2355
+ & .crisp-objectsettings-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--crisp-space-4); }
2356
+ & .crisp-objectsettings-heading { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); min-width: 0; }
2357
+ & .crisp-objectsettings-title { display: flex; align-items: center; gap: var(--crisp-space-2); font-size: 24px; line-height: 28px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.48px; color: var(--crisp-fg-primary); margin: 0; }
2358
+ & .crisp-objectsettings-subtitle { font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); margin: 0; }
2359
+ & .crisp-objectsettings-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); }
2360
+ & .crisp-objectsettings-search { display: flex; align-items: center; flex: 1; gap: var(--crisp-space-1-5); height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-2-5); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-default); color: var(--crisp-fg-muted); transition: box-shadow .15s; }
2361
+ & .crisp-objectsettings-search:focus-within { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
2362
+ & .crisp-objectsettings-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); }
2363
+ & .crisp-objectsettings-search input::placeholder { color: var(--crisp-fg-muted); }
2364
+ & .crisp-objectsettings-filterbtn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-2-5) 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-lg); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-default); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); cursor: pointer; white-space: nowrap; }
2365
+ & .crisp-objectsettings-filterbtn:hover { background: var(--crisp-bg-raised); }
2366
+ /* Object / attribute icon tile — Attio's own squircle (measured
2367
+ border-radius: 30%, not a scale token: it's a proportional radius tied to the tile's own size, unlike every other radius crisp uses,
2368
+ which is a fixed px). */
2369
+ & .crisp-objectsettings-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 16px; height: 16px; border-radius: 30%; color: var(--crisp-fg-on-brand); }
2370
+ & .crisp-objectsettings-icon--lg { width: 48px; height: 48px; border-radius: 16px; }
2371
+
2372
+ /* "Standard"/"Custom" type badge and the muted "No records" cell —
2373
+ plain text, not a Badge pill (measured: transparent background). */
2374
+ & .crisp-objectsettings-typebadge { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
2375
+ & .crisp-objectsettings-muted { color: var(--crisp-fg-muted); }
2376
+
2377
+ /* Attribute-type option row (icon + label + optional trailing badge),
2378
+ composed as a Select option's `label` ReactNode. */
2379
+ & .crisp-objectsettings-typeoption { display: flex; align-items: center; gap: var(--crisp-space-2); width: 100%; min-width: 0; }
2380
+ & .crisp-objectsettings-typeoption-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
2381
+ & .crisp-objectsettings-typeoption-badge { flex-shrink: 0; font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-brand-soft); }
2382
+ /* Create-attribute dialog's AI-autofill row and the submit button's ⌘↵ hint (measured off the same dialog, same shape as Dialog's own
2383
+ .crisp-dialog-submit-key, kept local rather than reaching into
2384
+ another component's scoped classes). */
2385
+ & .crisp-objectsettings-aiautofill { display: flex; flex-direction: column; gap: var(--crisp-space-1-5); }
2386
+ & .crisp-objectsettings-aiautofill-label { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
2387
+ & .crisp-objectsettings-aiautofill-row { display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-2-5); border-radius: var(--crisp-radius-md); box-shadow: inset 0 0 0 1px var(--crisp-border-default); font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); cursor: pointer; }
2388
+ & .crisp-objectsettings-aiautofill-row > svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
2389
+ & .crisp-objectsettings-aiautofill-row > span { flex: 1; }
2390
+ & .crisp-objectsettings-kbd { display: inline-flex; align-items: center; gap: var(--crisp-space-1); margin-inline-start: var(--crisp-space-1); }
2391
+ & .crisp-objectsettings-kbd kbd { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0 3px; border-radius: 4px; background: transparent; color: inherit; font-family: inherit; font-style: normal; font-size: 10px; }
2392
+ & .crisp-objectsettings-fieldrow { display: grid; grid-template-columns: 1fr 1fr; gap: var(--crisp-space-4); }
2393
+ }
2394
+
2395
+ @media (prefers-reduced-motion: reduce) {
2396
+ .crisp-objectsettings, .crisp-objectsettings * {
2397
+ animation-duration: 0.01ms !important;
2398
+ animation-iteration-count: 1 !important;
2399
+ transition-duration: 0.01ms !important;
2400
+ }
2401
+ }
2402
+
2217
2403
  .crisp-onboarding {
2218
2404
  display: flex; flex-direction: column; box-sizing: border-box; width: 272px; padding: var(--crisp-space-3); gap: var(--crisp-space-2); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent), 0 8px 28px -6px rgba(28,40,64,0.16), 0 2px 6px -2px rgba(28,40,64,0.10);
2219
2405
  & .crisp-onboarding-head { display: flex; align-items: center; gap: var(--crisp-space-1); }
@@ -2339,17 +2525,21 @@
2339
2525
  & .crisp-recordpage-link:hover { text-decoration: underline; }
2340
2526
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2341
2527
  & .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); }
2342
- & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2528
+ /* no padding-top: Tabs' own panel supplies it now (#588) */
2529
+ & .crisp-recordpage-panel { display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2343
2530
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
2344
2531
  & .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); }
2345
2532
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
2346
- /* 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. */
2533
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
2534
+ column count, so the grid stays responsive (auto-fit) without per-cell
2535
+ nth-child border hacks that break when the column count changes. */
2347
2536
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
2348
2537
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
2349
2538
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
2350
2539
  & .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); }
2351
2540
  & .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); }
2352
2541
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
2542
+
2353
2543
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
2354
2544
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
2355
2545
  & .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); }
@@ -2358,6 +2548,7 @@
2358
2548
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
2359
2549
  & .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; }
2360
2550
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
2551
+
2361
2552
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
2362
2553
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
2363
2554
  & .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); }
@@ -2365,6 +2556,7 @@
2365
2556
  & .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; }
2366
2557
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
2367
2558
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
2559
+
2368
2560
  & .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); }
2369
2561
  & .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); }
2370
2562
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -2374,18 +2566,23 @@
2374
2566
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
2375
2567
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
2376
2568
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
2569
+
2377
2570
  & .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); }
2378
2571
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
2379
2572
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
2380
2573
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2381
2574
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
2382
2575
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2576
+
2383
2577
  & .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); }
2384
2578
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
2385
2579
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
2580
+
2386
2581
  & .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; }
2387
2582
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
2583
+
2388
2584
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
2585
+
2389
2586
  & .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); }
2390
2587
  & .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)); }
2391
2588
  & .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)); }
@@ -2723,7 +2920,17 @@
2723
2920
  & .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); }
2724
2921
  & .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); }
2725
2922
  & .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); }
2726
- & .crisp-rte-body ul, & .crisp-rte-body ol { padding-inline-start: var(--crisp-space-5); }
2923
+ /* #575: a consumer's CSS reset (e.g. Tailwind preflight's un-scoped ul, ol { list-style: none }
2924
+ , 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,
2925
+ ~36.1353px), not the 20px placeholder #575 shipped with no real
2926
+ 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
2927
+ rather than being rounded to an existing token or added to the
2928
+ shared scale. Marker colour (currentColor via the already-measured
2929
+ 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
2930
+ marker type for a nested list either (nested <ul> is disc too,
2931
+ not circle), which this already doesn't attempt. */
2932
+ & .crisp-rte-body ul { list-style-type: disc; padding-inline-start: 36px; }
2933
+ & .crisp-rte-body ol { list-style-type: decimal; padding-inline-start: 36px; }
2727
2934
  & .crisp-rte-body li { margin-top: var(--crisp-space-1); }
2728
2935
  & .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); }
2729
2936
  & .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); }
@@ -2977,7 +3184,14 @@
2977
3184
  & .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); }
2978
3185
  & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
2979
3186
  & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
2980
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; }
3187
+ /* #642: measured live -- title, due date and assignee name are ALL 14px/500 on Attio's own Tasks page. Only -title/-due set their own
3188
+ font-size (and only -title its own weight), so an unstyled consumer
3189
+ slot (a plain PersonChip/RecordChip name span with no font-size of
3190
+ its own) fell through to the browser/body default, 16px/400. Set
3191
+ the ambient font here so it cascades to any slot content that
3192
+ doesn't override it -- -title/-due's own declarations still apply,
3193
+ unchanged, they're just now redundant with this baseline rather than the only source of it. */
3194
+ & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); }
2981
3195
  & .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; }
2982
3196
  & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
2983
3197
  & .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"); }
package/dist/tabs.css CHANGED
@@ -11,9 +11,15 @@
11
11
  & .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); }
12
12
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
13
13
  & .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; }
14
- & .crisp-tabs-panel { flex: 1; min-height: 0; }
14
+ & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
15
15
  & .crisp-tabs-panel:focus-visible { outline: none; }
16
16
  }
17
+ .crisp-tabs--fill_true {
18
+ height: 100%;
19
+ }
20
+ .crisp-tabs--fill_false {
21
+
22
+ }
17
23
 
18
24
  @media (prefers-reduced-motion: reduce) {
19
25
  .crisp-tabs, .crisp-tabs * {
package/dist/tabs.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface TabsVariants {
4
-
4
+ fill?: boolean;
5
5
  }
6
6
 
7
- export declare const tabsRecipe: RecipeFn<{ }>;
7
+ export declare const tabsRecipe: RecipeFn<{ fill: { true: string; false: string } }>;
@@ -12,9 +12,15 @@
12
12
  & .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); }
13
13
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
14
14
  & .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; }
15
- & .crisp-tabs-panel { flex: 1; min-height: 0; }
15
+ & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
16
16
  & .crisp-tabs-panel:focus-visible { outline: none; }
17
17
  }
18
+ .crisp-tabs--fill_true {
19
+ height: 100%;
20
+ }
21
+ .crisp-tabs--fill_false {
22
+
23
+ }
18
24
 
19
25
  @media (prefers-reduced-motion: reduce) {
20
26
  .crisp-tabs, .crisp-tabs * {
@@ -3,7 +3,14 @@
3
3
  & .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); }
4
4
  & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
5
5
  & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
6
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; }
6
+ /* #642: measured live -- title, due date and assignee name are ALL 14px/500 on Attio's own Tasks page. Only -title/-due set their own
7
+ font-size (and only -title its own weight), so an unstyled consumer
8
+ slot (a plain PersonChip/RecordChip name span with no font-size of
9
+ its own) fell through to the browser/body default, 16px/400. Set
10
+ the ambient font here so it cascades to any slot content that
11
+ doesn't override it -- -title/-due's own declarations still apply,
12
+ unchanged, they're just now redundant with this baseline rather than the only source of it. */
13
+ & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); }
7
14
  & .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; }
8
15
  & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
9
16
  & .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"); }
@@ -4,7 +4,14 @@
4
4
  & .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); }
5
5
  & .crisp-taskstable-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
6
6
  & .crisp-taskstable-row { display: flex; align-items: center; gap: var(--crisp-space-1-5); }
7
- & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; }
7
+ /* #642: measured live -- title, due date and assignee name are ALL 14px/500 on Attio's own Tasks page. Only -title/-due set their own
8
+ font-size (and only -title its own weight), so an unstyled consumer
9
+ slot (a plain PersonChip/RecordChip name span with no font-size of
10
+ its own) fell through to the browser/body default, 16px/400. Set
11
+ the ambient font here so it cascades to any slot content that
12
+ doesn't override it -- -title/-due's own declarations still apply,
13
+ unchanged, they're just now redundant with this baseline rather than the only source of it. */
14
+ & .crisp-taskstable-cell { display: flex; align-items: center; min-width: 0; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); }
8
15
  & .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; }
9
16
  & .crisp-taskstable-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 30%, transparent); }
10
17
  & .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"); }
package/dist/text.css ADDED
@@ -0,0 +1,9 @@
1
+ .crisp-text {
2
+ display: inline;
3
+ }
4
+ .crisp-text--tone_default {
5
+ color: var(--crisp-fg-primary);
6
+ }
7
+ .crisp-text--tone_muted {
8
+ color: var(--crisp-fg-neutral-muted);
9
+ }
package/dist/text.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { RecipeFn } from "../core/types";
2
+
3
+ export interface TextVariants {
4
+ tone?: "default" | "muted";
5
+ }
6
+
7
+ export declare const textRecipe: RecipeFn<{ tone: { default: string; muted: string } }>;
@@ -0,0 +1,11 @@
1
+ @layer crisp-components {
2
+ .crisp-text {
3
+ display: inline;
4
+ }
5
+ .crisp-text--tone_default {
6
+ color: var(--crisp-fg-primary);
7
+ }
8
+ .crisp-text--tone_muted {
9
+ color: var(--crisp-fg-neutral-muted);
10
+ }
11
+ }
@@ -3,6 +3,18 @@ import type { VariantProps } from "../../core";
3
3
  import { chipRecipe } from "./chip.recipe";
4
4
  export type ChipProps = React.ComponentPropsWithoutRef<"span"> & VariantProps<typeof chipRecipe> & {
5
5
  asChild?: boolean;
6
+ /**
7
+ * Ellipsis-truncates the label when it no longer fits. Only meaningful
8
+ * when `children` is a plain string — anything else (an avatar, a
9
+ * multi-node label) is rendered as given, untruncated.
10
+ *
11
+ * Ellipsis only, no `Tooltip` on hover (dropped, #623 — a truncated
12
+ * value in Attio is revealed by the cell's own click-to-expand, #579,
13
+ * not by a chip tooltip; there was no Attio reference for one, which
14
+ * is exactly the "never add polish the reference lacks" case). Not
15
+ * Attio's "+N" overflow badge either — see `chip.recipe.ts` for why.
16
+ */
17
+ truncate?: boolean;
6
18
  };
7
19
  /**
8
20
  * A small pill — the same measured chip RecordsTable, RecordPanel and
@@ -19,6 +31,10 @@ export type ChipProps = React.ComponentPropsWithoutRef<"span"> & VariantProps<ty
19
31
  * spacing from the label.
20
32
  */
21
33
  export declare const Chip: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & VariantProps<import("../..").RecipeFn<{
34
+ readonly truncate: {
35
+ readonly true: string;
36
+ readonly false: string;
37
+ };
22
38
  readonly tone: {
23
39
  readonly neutral: string;
24
40
  readonly brand: string;
@@ -31,6 +47,10 @@ export declare const Chip: React.ForwardRefExoticComponent<Omit<React.DetailedHT
31
47
  };
32
48
  }> & {
33
49
  config: import("../..").RecipeConfig<{
50
+ readonly truncate: {
51
+ readonly true: string;
52
+ readonly false: string;
53
+ };
34
54
  readonly tone: {
35
55
  readonly neutral: string;
36
56
  readonly brand: string;
@@ -44,4 +64,16 @@ export declare const Chip: React.ForwardRefExoticComponent<Omit<React.DetailedHT
44
64
  }>;
45
65
  }> & {
46
66
  asChild?: boolean;
67
+ /**
68
+ * Ellipsis-truncates the label when it no longer fits. Only meaningful
69
+ * when `children` is a plain string — anything else (an avatar, a
70
+ * multi-node label) is rendered as given, untruncated.
71
+ *
72
+ * Ellipsis only, no `Tooltip` on hover (dropped, #623 — a truncated
73
+ * value in Attio is revealed by the cell's own click-to-expand, #579,
74
+ * not by a chip tooltip; there was no Attio reference for one, which
75
+ * is exactly the "never add polish the reference lacks" case). Not
76
+ * Attio's "+N" overflow badge either — see `chip.recipe.ts` for why.
77
+ */
78
+ truncate?: boolean;
47
79
  } & React.RefAttributes<HTMLSpanElement>>;