@crisp-ui-kit/crisp 0.33.0 → 0.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +33 -1
  2. package/dist/appshell.css +1 -4
  3. package/dist/appshell.layered.css +1 -4
  4. package/dist/authshell.css +4 -19
  5. package/dist/authshell.layered.css +4 -19
  6. package/dist/avatar.css +2 -1
  7. package/dist/avatar.layered.css +2 -1
  8. package/dist/board.css +1 -4
  9. package/dist/board.layered.css +1 -4
  10. package/dist/breadcrumb.css +0 -1
  11. package/dist/breadcrumb.layered.css +0 -1
  12. package/dist/button.css +14 -6
  13. package/dist/button.layered.css +14 -6
  14. package/dist/card.css +2 -1
  15. package/dist/card.layered.css +2 -1
  16. package/dist/checkbox.css +6 -9
  17. package/dist/checkbox.layered.css +6 -9
  18. package/dist/cjs/components/avatar/avatar.recipe.js +38 -19
  19. package/dist/cjs/components/button/button.recipe.js +109 -18
  20. package/dist/cjs/components/datatable/datatable.js +14 -2
  21. package/dist/cjs/components/datatable/datatable.recipe.js +5 -0
  22. package/dist/cjs/components/switch/switch.recipe.js +48 -18
  23. package/dist/columncalc.css +2 -6
  24. package/dist/columncalc.layered.css +2 -6
  25. package/dist/command.css +9 -11
  26. package/dist/command.layered.css +9 -11
  27. package/dist/datatable.css +11 -19
  28. package/dist/datatable.layered.css +11 -19
  29. package/dist/datepicker.css +15 -80
  30. package/dist/datepicker.layered.css +15 -80
  31. package/dist/datepickerfield.css +3 -18
  32. package/dist/datepickerfield.layered.css +3 -18
  33. package/dist/donutchart.css +1 -2
  34. package/dist/donutchart.layered.css +1 -2
  35. package/dist/entitycard.css +1 -7
  36. package/dist/entitycard.layered.css +1 -7
  37. package/dist/esm/components/avatar/avatar.recipe.js +38 -19
  38. package/dist/esm/components/button/button.recipe.js +109 -18
  39. package/dist/esm/components/datatable/datatable.js +14 -2
  40. package/dist/esm/components/datatable/datatable.recipe.js +5 -0
  41. package/dist/esm/components/switch/switch.recipe.js +48 -18
  42. package/dist/fileupload.css +10 -60
  43. package/dist/fileupload.layered.css +10 -60
  44. package/dist/inbox.css +1 -7
  45. package/dist/inbox.layered.css +1 -7
  46. package/dist/input.css +9 -20
  47. package/dist/input.layered.css +9 -20
  48. package/dist/listpage.css +2 -12
  49. package/dist/listpage.layered.css +2 -12
  50. package/dist/menu.css +3 -19
  51. package/dist/menu.layered.css +3 -19
  52. package/dist/multiselect.css +4 -25
  53. package/dist/multiselect.layered.css +4 -25
  54. package/dist/navitem.css +5 -13
  55. package/dist/navitem.layered.css +5 -13
  56. package/dist/onboarding.css +1 -8
  57. package/dist/onboarding.layered.css +1 -8
  58. package/dist/quantitypicker.css +15 -50
  59. package/dist/quantitypicker.layered.css +15 -50
  60. package/dist/radiogroup.css +1 -6
  61. package/dist/radiogroup.layered.css +1 -6
  62. package/dist/recordpage.css +2 -10
  63. package/dist/recordpage.layered.css +2 -10
  64. package/dist/recordpanel.css +1 -13
  65. package/dist/recordpanel.layered.css +1 -13
  66. package/dist/recordstable.css +2 -11
  67. package/dist/recordstable.layered.css +2 -11
  68. package/dist/resizablepanel.css +4 -15
  69. package/dist/resizablepanel.layered.css +4 -15
  70. package/dist/richtext.css +1 -5
  71. package/dist/richtext.layered.css +1 -5
  72. package/dist/select.css +7 -30
  73. package/dist/select.layered.css +7 -30
  74. package/dist/settingsshell.css +2 -7
  75. package/dist/settingsshell.layered.css +2 -7
  76. package/dist/sidebar.css +23 -28
  77. package/dist/sidebar.layered.css +23 -28
  78. package/dist/slider.css +1 -8
  79. package/dist/slider.layered.css +1 -8
  80. package/dist/statcard.css +1 -2
  81. package/dist/statcard.layered.css +1 -2
  82. package/dist/styles.css +196 -652
  83. package/dist/styles.layered.css +196 -652
  84. package/dist/switch.css +1 -5
  85. package/dist/switch.layered.css +1 -5
  86. package/dist/table.css +2 -1
  87. package/dist/table.layered.css +2 -1
  88. package/dist/tabletoolbar.css +1 -3
  89. package/dist/tabletoolbar.layered.css +1 -3
  90. package/dist/tabs.css +1 -5
  91. package/dist/tabs.layered.css +1 -5
  92. package/dist/tasklist.css +1 -8
  93. package/dist/tasklist.layered.css +1 -8
  94. package/dist/textarea.css +2 -13
  95. package/dist/textarea.layered.css +2 -13
  96. package/dist/tooltip.css +2 -1
  97. package/dist/tooltip.layered.css +2 -1
  98. package/dist/trialbanner.css +2 -11
  99. package/dist/trialbanner.layered.css +2 -11
  100. package/dist/types/components/avatar/avatar.d.ts +30 -30
  101. package/dist/types/components/avatar/avatar.recipe.d.ts +136 -49
  102. package/dist/types/components/button/button.d.ts +30 -30
  103. package/dist/types/components/button/button.recipe.d.ts +193 -45
  104. package/dist/types/components/datatable/datatable.d.ts +10 -1
  105. package/dist/types/components/switch/switch.recipe.d.ts +57 -3
  106. package/dist/viewtoggle.css +3 -9
  107. package/dist/viewtoggle.layered.css +3 -9
  108. package/dist/wheelpicker.css +14 -59
  109. package/dist/wheelpicker.layered.css +14 -59
  110. package/package.json +1 -1
@@ -119,7 +119,7 @@ function renderTyped(col, value, avatarTone, display) {
119
119
  }
120
120
  }
121
121
  const CHECK_W = 40;
122
- export function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel = "Select all rows", selectRowLabel = (row) => `Select row ${row.id}`, columnMenu = false, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder = "Search…", searchLabel = "Search table", onEdit, stickyFirst = true, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", empty = "No results.", showCount = true, countLabel = (shown, total) => shown === total
122
+ export function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel = "Select all rows", selectRowLabel = (row) => `Select row ${row.id}`, selectionAnnouncement = (count) => count === 0 ? "Selection cleared" : `${count} selected`, columnMenu = false, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder = "Search…", searchLabel = "Search table", onEdit, stickyFirst = true, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", empty = "No results.", showCount = true, countLabel = (shown, total) => shown === total
123
123
  ? `${total} ${total === 1 ? "row" : "rows"}`
124
124
  : `${shown} of ${total}`, toolbar, className, reorderRowLabel = "Reorder row", ...rest }) {
125
125
  const [query, setQuery] = React.useState("");
@@ -198,6 +198,18 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
198
198
  if (!selectedIds)
199
199
  setInternalSel(next);
200
200
  }, [onSelectionChange, selectedIds]);
201
+ // The selection is announced from a region that is part of the table and
202
+ // outlives every selection, because a consumer's own bulk bar (mounted only
203
+ // once something is picked) cannot announce its own arrival (#234).
204
+ // Derived by adjusting state during render rather than in an effect: it
205
+ // must not fire on mount — nobody has selected anything yet — and it has to
206
+ // cover a `selectedIds` change the consumer makes as well as a click here.
207
+ const prevSelectedCount = React.useRef(selected.size);
208
+ const [selectionText, setSelectionText] = React.useState("");
209
+ if (prevSelectedCount.current !== selected.size) {
210
+ prevSelectedCount.current = selected.size;
211
+ setSelectionText(selectionAnnouncement(selected.size));
212
+ }
201
213
  const [editing, setEditing] = React.useState(null);
202
214
  const [scrolledX, setScrolledX] = React.useState(false);
203
215
  // column widths (drag-to-resize) + fixed-layout sizing
@@ -428,7 +440,7 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
428
440
  return dir === "asc" ? (_jsx(ArrowUp, { className: "crisp-datatable-chevron", size: 13, strokeWidth: 2 })) : (_jsx(ArrowDown, { className: "crisp-datatable-chevron", size: 13, strokeWidth: 2 }));
429
441
  return (_jsx(ChevronsUpDown, { className: "crisp-datatable-chevron", size: 13, strokeWidth: 2 }));
430
442
  }
431
- return (_jsxs("div", { ref: gridRef, className: datatableRecipe({ className }), tabIndex: -1, onKeyDown: onGridKeyDown, ...rest, children: [(filterable || showCount || toolbar || columnMenu) && (_jsxs("div", { className: "crisp-datatable-toolbar", children: [columnMenu && (_jsxs(Menu, { align: "start", trigger: _jsxs("button", { type: "button", className: "crisp-datatable-viewsettings", children: [_jsx(SlidersHorizontal, { size: 14, strokeWidth: 1.75 }), "View settings"] }), children: [_jsx("div", { className: "crisp-datatable-group-label", children: "Columns" }), allColumns.map((c) => (_jsxs(MenuItem, { onSelect: () => c.hidden ? showColumn(c.key) : hideColumn(c.key), children: [_jsx(Check, { size: 14, strokeWidth: 2, style: { opacity: c.hidden ? 0 : 1 } }), typeof c.header === "string" ? c.header : c.key] }, c.key)))] })), filterable && (_jsxs("span", { className: "crisp-datatable-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), _jsx("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchLabel })] })), showCount && (_jsx("span", { className: "crisp-datatable-count", children: countLabel(rows.length, data.length) })), toolbar] })), _jsxs(Table, { interactive: interactive, surface: surface, style: { minWidth }, scrollProps: {
443
+ return (_jsxs("div", { ref: gridRef, className: datatableRecipe({ className }), tabIndex: -1, onKeyDown: onGridKeyDown, ...rest, children: [_jsx("div", { className: "crisp-datatable-live", role: "status", "aria-live": "polite", children: selectionText }), (filterable || showCount || toolbar || columnMenu) && (_jsxs("div", { className: "crisp-datatable-toolbar", children: [columnMenu && (_jsxs(Menu, { align: "start", trigger: _jsxs("button", { type: "button", className: "crisp-datatable-viewsettings", children: [_jsx(SlidersHorizontal, { size: 14, strokeWidth: 1.75 }), "View settings"] }), children: [_jsx("div", { className: "crisp-datatable-group-label", children: "Columns" }), allColumns.map((c) => (_jsxs(MenuItem, { onSelect: () => c.hidden ? showColumn(c.key) : hideColumn(c.key), children: [_jsx(Check, { size: 14, strokeWidth: 2, style: { opacity: c.hidden ? 0 : 1 } }), typeof c.header === "string" ? c.header : c.key] }, c.key)))] })), filterable && (_jsxs("span", { className: "crisp-datatable-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), _jsx("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchLabel })] })), showCount && (_jsx("span", { className: "crisp-datatable-count", children: countLabel(rows.length, data.length) })), toolbar] })), _jsxs(Table, { interactive: interactive, surface: surface, style: { minWidth }, scrollProps: {
432
444
  onScroll: (e) => setScrolledX(e.currentTarget.scrollLeft > 0),
433
445
  ...(scrolledX ? { "data-scrolled-x": "" } : {}),
434
446
  }, children: [_jsxs("colgroup", { children: [showGrip && _jsx("col", { style: { width: GRIP_W } }), selectable && _jsx("col", { style: { width: CHECK_W } }), columns.map((col) => (_jsx("col", { style: { width: colWidth(col) } }, col.key)))] }), _jsx(TableHeader, { children: _jsxs(TableRow, { children: [showGrip && (_jsx(TableHead, { className: "crisp-datatable-col-grip", ...stickyCol(-2) })), selectable && (_jsx(TableHead, { className: "crisp-datatable-col-check", ...stickyCol(-1), children: _jsx("span", { className: "crisp-datatable-check", children: _jsx(Checkbox, { "aria-label": selectAllLabel, checked: allSelected, indeterminate: someSelected, onChange: toggleAll }) }) })), columns.map((col, i) => {
@@ -15,6 +15,11 @@ const config = defineRecipe({
15
15
  & .crisp-datatable-search input:focus { box-shadow: inset 0 0 0 1px ${c.bgBrandSolid}; }
16
16
  & .crisp-datatable-count { margin-inline-start: auto; font-size: ${c.text.md}; color: ${c.fgMuted}; }
17
17
  /* The header cell has no padding of its own -- the button owns the
18
+ /* announced, never drawn */
19
+ & .crisp-datatable-live {
20
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
21
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
22
+ }
18
23
  full cell so the whole header is clickable (its own inset is 12px).
19
24
  Mirrors the summary-row fix. */
20
25
  & th { padding: 0; }
@@ -1,25 +1,55 @@
1
- import { defineRecipe, resolveRecipe } from "../../core/index.js";
1
+ import { defineSpec, resolveRecipe, specToRecipe } from "../../core/index.js";
2
2
  import { contract as c } from "../../tokens/index.js";
3
3
  // A settings toggle, measured from the live app: a 24×16 pill track that
4
4
  // flips from a muted grey to brand blue (#266df0), with a 12px white thumb
5
5
  // (2px inset) that slides 8px across. background + transform both animate .2s.
6
- const config = defineRecipe({
6
+ const spec = defineSpec({
7
7
  name: "switch",
8
- base: `
9
- display: inline-flex; align-items: center; flex-shrink: 0; width: 24px; height: 16px; padding: 0; margin: 0;
10
- border: none; border-radius: ${c.radius.full}; background: color-mix(in srgb, ${c.fgPrimary} 9%, transparent);
11
- cursor: pointer; transition: background-color .2s ease; -webkit-tap-highlight-color: transparent;
12
- & .crisp-switch-thumb { width: 12px; height: 12px; margin: 0 2px; border-radius: ${c.radius.full}; background: #fff; transition: transform .2s ease; }
13
- &[data-checked] { background: ${c.bgBrandSolid}; }
14
- &[data-checked] .crisp-switch-thumb { transform: translateX(8px); }
15
- &:focus-visible { outline: 2px solid ${c.bgBrandSolid}; outline-offset: 2px; }
16
- &:disabled { opacity: 0.5; cursor: not-allowed; }
17
- `,
18
- variants: {},
19
- defaultVariants: {},
20
- compoundVariants: [],
21
- });
22
- export const switchRecipe = Object.assign(resolveRecipe(config), {
23
- config,
8
+ base: {
9
+ display: "inline-flex",
10
+ alignItems: "center",
11
+ flexShrink: 0,
12
+ width: "24px",
13
+ height: "16px",
14
+ padding: 0,
15
+ margin: 0,
16
+ border: "none",
17
+ borderRadius: c.radius.full,
18
+ background: `color-mix(in srgb, ${c.fgPrimary} 9%, transparent)`,
19
+ cursor: "pointer",
20
+ transition: "background-color .2s ease",
21
+ WebkitTapHighlightColor: "transparent",
22
+ },
23
+ parts: [
24
+ {
25
+ slot: "thumb",
26
+ selector: "& .crisp-switch-thumb",
27
+ decls: {
28
+ width: "12px",
29
+ height: "12px",
30
+ margin: "0 2px",
31
+ borderRadius: c.radius.full,
32
+ background: "#fff",
33
+ transition: "transform .2s ease",
34
+ },
35
+ },
36
+ { selector: "&[data-checked]", decls: { background: c.bgBrandSolid } },
37
+ {
38
+ slot: "thumb",
39
+ selector: "&[data-checked] .crisp-switch-thumb",
40
+ decls: { transform: "translateX(8px)" },
41
+ },
42
+ {
43
+ selector: "&:focus-visible",
44
+ decls: {
45
+ outline: `2px solid ${c.bgBrandSolid}`,
46
+ outlineOffset: "2px",
47
+ },
48
+ },
49
+ { selector: "&:disabled", decls: { opacity: 0.5, cursor: "not-allowed" } },
50
+ ],
24
51
  });
52
+ const config = specToRecipe(spec);
53
+ export const switchRecipe = Object.assign(resolveRecipe(config), { config });
54
+ export { spec };
25
55
  export default config;
@@ -1,83 +1,33 @@
1
1
  .crisp-fileupload {
2
-
3
- display: flex; flex-direction: column; gap: var(--crisp-space-2);
4
- font-family: var(--crisp-font-sans); min-width: 0;
5
-
6
- & .crisp-fileupload-zone {
7
- box-sizing: border-box; position: relative;
8
- display: flex; flex-direction: column; align-items: center;
9
- justify-content: center; gap: var(--crisp-space-1);
10
- width: 100%; padding: var(--crisp-space-6) var(--crisp-space-4);
11
- border: 1px dashed var(--crisp-border-default); border-radius: var(--crisp-radius-md);
12
- background: transparent; cursor: pointer; text-align: center;
13
- font-family: inherit;
14
- transition: background-color .12s, border-color .12s;
15
- outline: 2px solid transparent; outline-offset: -2px;
16
- }
2
+ display: flex; flex-direction: column; gap: var(--crisp-space-2); font-family: var(--crisp-font-sans); min-width: 0;
3
+ & .crisp-fileupload-zone { box-sizing: border-box; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; padding: var(--crisp-space-6) var(--crisp-space-4); border: 1px dashed var(--crisp-border-default); border-radius: var(--crisp-radius-md); background: transparent; cursor: pointer; text-align: center; font-family: inherit; transition: background-color .12s, border-color .12s; outline: 2px solid transparent; outline-offset: -2px; }
17
4
  & .crisp-fileupload-zone:hover { background: var(--crisp-bg-raised); }
18
5
  & .crisp-fileupload-zone:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
19
- & .crisp-fileupload-zone[data-dragging] {
20
- border-color: var(--crisp-bg-brand-solid);
21
- background: color-mix(in srgb, var(--crisp-bg-brand-solid) 6%, transparent);
22
- }
6
+ & .crisp-fileupload-zone[data-dragging] { border-color: var(--crisp-bg-brand-solid); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 6%, transparent); }
23
7
  & .crisp-fileupload-zone[data-disabled] { opacity: .5; cursor: not-allowed; }
24
8
  & .crisp-fileupload-zone[data-invalid] { border-color: var(--crisp-bg-danger); }
25
-
26
9
  /* "No files" 16/20 weight 600 */
27
- & .crisp-fileupload-title {
28
- color: var(--crisp-fg-primary); font-size: var(--crisp-text-xl); line-height: 20px;
29
- font-weight: var(--crisp-weight-semibold);
30
- }
10
+ & .crisp-fileupload-title { color: var(--crisp-fg-primary); font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); }
31
11
  /* The hint line, 14/20 weight 500 at 63% of the foreground */
32
- & .crisp-fileupload-hint {
33
- color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent);
34
- font-size: var(--crisp-text-lg); line-height: 20px;
35
- font-weight: var(--crisp-weight-medium);
36
- }
37
-
38
- & .crisp-fileupload-input {
39
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
40
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
41
- }
42
-
12
+ & .crisp-fileupload-hint { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); }
13
+ & .crisp-fileupload-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
43
14
  & .crisp-fileupload-list { display: flex; flex-direction: column; gap: var(--crisp-space-1); margin: 0; padding: 0; list-style: none; }
44
15
  /* the row height crisp measured for a data-grid row */
45
- & .crisp-fileupload-item {
46
- display: flex; align-items: center; gap: var(--crisp-space-2);
47
- min-height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2);
48
- border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised);
49
- font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary);
50
- }
16
+ & .crisp-fileupload-item { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); }
51
17
  & .crisp-fileupload-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
52
18
  & .crisp-fileupload-item-size { flex-shrink: 0; color: var(--crisp-fg-muted); font-size: var(--crisp-text-sm); font-variant-numeric: tabular-nums; }
53
19
  & .crisp-fileupload-item[data-status="error"] { color: var(--crisp-bg-danger); }
54
20
  & .crisp-fileupload-item[data-status="error"] .crisp-fileupload-item-size { color: var(--crisp-bg-danger); }
55
-
56
21
  /* progress track, reusing the measured Progress geometry */
57
- & .crisp-fileupload-progress {
58
- flex-shrink: 0; width: 64px; height: 4px; border-radius: var(--crisp-radius-full);
59
- background: var(--crisp-bg-neutral-weak); overflow: hidden;
60
- }
22
+ & .crisp-fileupload-progress { flex-shrink: 0; width: 64px; height: 4px; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-neutral-weak); overflow: hidden; }
61
23
  & .crisp-fileupload-progress-fill { height: 100%; background: var(--crisp-bg-brand-solid); transition: width .2s ease; }
62
-
63
- & .crisp-fileupload-remove {
64
- display: inline-flex; align-items: center; justify-content: center;
65
- flex-shrink: 0; width: var(--crisp-size-icon); height: var(--crisp-size-icon);
66
- padding: 0; border: none; border-radius: var(--crisp-radius-sm);
67
- background: transparent; color: var(--crisp-fg-muted); cursor: pointer;
68
- font-family: inherit;
69
- outline: 2px solid transparent; outline-offset: -2px;
70
- }
24
+ & .crisp-fileupload-remove { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: var(--crisp-size-icon); height: var(--crisp-size-icon); padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: transparent; color: var(--crisp-fg-muted); cursor: pointer; font-family: inherit; outline: 2px solid transparent; outline-offset: -2px; }
71
25
  & .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
72
26
  & .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
73
-
74
27
  & .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
75
- /* out of flow while empty, so the always-mounted live region costs no
76
- layout: display:none would take it out of the accessibility tree, which
77
- is the one thing it exists to be in */
28
+ /* out of flow while empty, so the always-mounted live region costs no layout: display:none would take it out of the accessibility tree, which is the one thing it exists to be in */
78
29
  & .crisp-fileupload-errors:empty { position: absolute; }
79
30
  & .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
80
-
81
31
  }
82
32
 
83
33
  @media (prefers-reduced-motion: reduce) {
@@ -1,84 +1,34 @@
1
1
  @layer crisp-components {
2
2
  .crisp-fileupload {
3
-
4
- display: flex; flex-direction: column; gap: var(--crisp-space-2);
5
- font-family: var(--crisp-font-sans); min-width: 0;
6
-
7
- & .crisp-fileupload-zone {
8
- box-sizing: border-box; position: relative;
9
- display: flex; flex-direction: column; align-items: center;
10
- justify-content: center; gap: var(--crisp-space-1);
11
- width: 100%; padding: var(--crisp-space-6) var(--crisp-space-4);
12
- border: 1px dashed var(--crisp-border-default); border-radius: var(--crisp-radius-md);
13
- background: transparent; cursor: pointer; text-align: center;
14
- font-family: inherit;
15
- transition: background-color .12s, border-color .12s;
16
- outline: 2px solid transparent; outline-offset: -2px;
17
- }
3
+ display: flex; flex-direction: column; gap: var(--crisp-space-2); font-family: var(--crisp-font-sans); min-width: 0;
4
+ & .crisp-fileupload-zone { box-sizing: border-box; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--crisp-space-1); width: 100%; padding: var(--crisp-space-6) var(--crisp-space-4); border: 1px dashed var(--crisp-border-default); border-radius: var(--crisp-radius-md); background: transparent; cursor: pointer; text-align: center; font-family: inherit; transition: background-color .12s, border-color .12s; outline: 2px solid transparent; outline-offset: -2px; }
18
5
  & .crisp-fileupload-zone:hover { background: var(--crisp-bg-raised); }
19
6
  & .crisp-fileupload-zone:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
20
- & .crisp-fileupload-zone[data-dragging] {
21
- border-color: var(--crisp-bg-brand-solid);
22
- background: color-mix(in srgb, var(--crisp-bg-brand-solid) 6%, transparent);
23
- }
7
+ & .crisp-fileupload-zone[data-dragging] { border-color: var(--crisp-bg-brand-solid); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 6%, transparent); }
24
8
  & .crisp-fileupload-zone[data-disabled] { opacity: .5; cursor: not-allowed; }
25
9
  & .crisp-fileupload-zone[data-invalid] { border-color: var(--crisp-bg-danger); }
26
-
27
10
  /* "No files" 16/20 weight 600 */
28
- & .crisp-fileupload-title {
29
- color: var(--crisp-fg-primary); font-size: var(--crisp-text-xl); line-height: 20px;
30
- font-weight: var(--crisp-weight-semibold);
31
- }
11
+ & .crisp-fileupload-title { color: var(--crisp-fg-primary); font-size: var(--crisp-text-xl); line-height: 20px; font-weight: var(--crisp-weight-semibold); }
32
12
  /* The hint line, 14/20 weight 500 at 63% of the foreground */
33
- & .crisp-fileupload-hint {
34
- color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent);
35
- font-size: var(--crisp-text-lg); line-height: 20px;
36
- font-weight: var(--crisp-weight-medium);
37
- }
38
-
39
- & .crisp-fileupload-input {
40
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
41
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
42
- }
43
-
13
+ & .crisp-fileupload-hint { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); }
14
+ & .crisp-fileupload-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
44
15
  & .crisp-fileupload-list { display: flex; flex-direction: column; gap: var(--crisp-space-1); margin: 0; padding: 0; list-style: none; }
45
16
  /* the row height crisp measured for a data-grid row */
46
- & .crisp-fileupload-item {
47
- display: flex; align-items: center; gap: var(--crisp-space-2);
48
- min-height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2);
49
- border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised);
50
- font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary);
51
- }
17
+ & .crisp-fileupload-item { display: flex; align-items: center; gap: var(--crisp-space-2); min-height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-raised); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); }
52
18
  & .crisp-fileupload-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
53
19
  & .crisp-fileupload-item-size { flex-shrink: 0; color: var(--crisp-fg-muted); font-size: var(--crisp-text-sm); font-variant-numeric: tabular-nums; }
54
20
  & .crisp-fileupload-item[data-status="error"] { color: var(--crisp-bg-danger); }
55
21
  & .crisp-fileupload-item[data-status="error"] .crisp-fileupload-item-size { color: var(--crisp-bg-danger); }
56
-
57
22
  /* progress track, reusing the measured Progress geometry */
58
- & .crisp-fileupload-progress {
59
- flex-shrink: 0; width: 64px; height: 4px; border-radius: var(--crisp-radius-full);
60
- background: var(--crisp-bg-neutral-weak); overflow: hidden;
61
- }
23
+ & .crisp-fileupload-progress { flex-shrink: 0; width: 64px; height: 4px; border-radius: var(--crisp-radius-full); background: var(--crisp-bg-neutral-weak); overflow: hidden; }
62
24
  & .crisp-fileupload-progress-fill { height: 100%; background: var(--crisp-bg-brand-solid); transition: width .2s ease; }
63
-
64
- & .crisp-fileupload-remove {
65
- display: inline-flex; align-items: center; justify-content: center;
66
- flex-shrink: 0; width: var(--crisp-size-icon); height: var(--crisp-size-icon);
67
- padding: 0; border: none; border-radius: var(--crisp-radius-sm);
68
- background: transparent; color: var(--crisp-fg-muted); cursor: pointer;
69
- font-family: inherit;
70
- outline: 2px solid transparent; outline-offset: -2px;
71
- }
25
+ & .crisp-fileupload-remove { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: var(--crisp-size-icon); height: var(--crisp-size-icon); padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: transparent; color: var(--crisp-fg-muted); cursor: pointer; font-family: inherit; outline: 2px solid transparent; outline-offset: -2px; }
72
26
  & .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
73
27
  & .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
74
-
75
28
  & .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
76
- /* out of flow while empty, so the always-mounted live region costs no
77
- layout: display:none would take it out of the accessibility tree, which
78
- is the one thing it exists to be in */
29
+ /* out of flow while empty, so the always-mounted live region costs no layout: display:none would take it out of the accessibility tree, which is the one thing it exists to be in */
79
30
  & .crisp-fileupload-errors:empty { position: absolute; }
80
31
  & .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
81
-
82
32
  }
83
33
 
84
34
  @media (prefers-reduced-motion: reduce) {
package/dist/inbox.css CHANGED
@@ -1,9 +1,5 @@
1
1
  .crisp-inbox {
2
- display: flex; flex-direction: column; box-sizing: border-box; width: 380px; max-height: 588px;
3
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); overflow: hidden;
4
- 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);
5
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
6
-
2
+ 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 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); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
7
3
  & .crisp-inbox-head { padding: var(--crisp-space-3) var(--crisp-space-3) 0; }
8
4
  & .crisp-inbox-title { font-size: var(--crisp-text-xl); line-height: 28px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.3px; color: var(--crisp-fg-primary); }
9
5
  & .crisp-inbox-tabs { display: flex; gap: var(--crisp-space-3); margin-top: var(--crisp-space-2); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); }
@@ -12,7 +8,6 @@
12
8
  & .crisp-inbox-tab[data-active] { color: var(--crisp-bg-brand-solid); box-shadow: inset 0 -2px 0 var(--crisp-bg-brand-solid); }
13
9
  & .crisp-inbox-tab-count { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
14
10
  & .crisp-inbox-tab[data-active] .crisp-inbox-tab-count { color: var(--crisp-bg-brand-solid); }
15
-
16
11
  & .crisp-inbox-list { flex: 1; min-height: 0; overflow-y: auto; padding: var(--crisp-space-1); display: flex; flex-direction: column; gap: 1px; }
17
12
  & .crisp-inbox-row { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) var(--crisp-space-2); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; font-family: inherit; text-align: start; cursor: pointer; transition: background-color .12s; }
18
13
  & .crisp-inbox-row:hover { background: var(--crisp-bg-raised); }
@@ -22,7 +17,6 @@
22
17
  & .crisp-inbox-row-msg b { font-weight: var(--crisp-weight-semibold); }
23
18
  & .crisp-inbox-row-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
24
19
  & .crisp-inbox-row-unread { flex-shrink: 0; width: 8px; height: 8px; margin-top: var(--crisp-space-1); border-radius: var(--crisp-radius-full); background: var(--crisp-bg-brand-solid); }
25
-
26
20
  & .crisp-inbox-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; }
27
21
  & .crisp-inbox-empty-icon { display: inline-flex; margin-bottom: var(--crisp-space-2); color: color-mix(in srgb, var(--crisp-fg-primary) 22%, transparent); }
28
22
  & .crisp-inbox-empty-title { font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
@@ -1,10 +1,6 @@
1
1
  @layer crisp-components {
2
2
  .crisp-inbox {
3
- display: flex; flex-direction: column; box-sizing: border-box; width: 380px; max-height: 588px;
4
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); overflow: hidden;
5
- 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);
6
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
7
-
3
+ 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 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); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
8
4
  & .crisp-inbox-head { padding: var(--crisp-space-3) var(--crisp-space-3) 0; }
9
5
  & .crisp-inbox-title { font-size: var(--crisp-text-xl); line-height: 28px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.3px; color: var(--crisp-fg-primary); }
10
6
  & .crisp-inbox-tabs { display: flex; gap: var(--crisp-space-3); margin-top: var(--crisp-space-2); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); }
@@ -13,7 +9,6 @@
13
9
  & .crisp-inbox-tab[data-active] { color: var(--crisp-bg-brand-solid); box-shadow: inset 0 -2px 0 var(--crisp-bg-brand-solid); }
14
10
  & .crisp-inbox-tab-count { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
15
11
  & .crisp-inbox-tab[data-active] .crisp-inbox-tab-count { color: var(--crisp-bg-brand-solid); }
16
-
17
12
  & .crisp-inbox-list { flex: 1; min-height: 0; overflow-y: auto; padding: var(--crisp-space-1); display: flex; flex-direction: column; gap: 1px; }
18
13
  & .crisp-inbox-row { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) var(--crisp-space-2); margin: 0; border: none; border-radius: var(--crisp-radius-md); background: none; font-family: inherit; text-align: start; cursor: pointer; transition: background-color .12s; }
19
14
  & .crisp-inbox-row:hover { background: var(--crisp-bg-raised); }
@@ -23,7 +18,6 @@
23
18
  & .crisp-inbox-row-msg b { font-weight: var(--crisp-weight-semibold); }
24
19
  & .crisp-inbox-row-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
25
20
  & .crisp-inbox-row-unread { flex-shrink: 0; width: 8px; height: 8px; margin-top: var(--crisp-space-1); border-radius: var(--crisp-radius-full); background: var(--crisp-bg-brand-solid); }
26
-
27
21
  & .crisp-inbox-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; }
28
22
  & .crisp-inbox-empty-icon { display: inline-flex; margin-bottom: var(--crisp-space-2); color: color-mix(in srgb, var(--crisp-fg-primary) 22%, transparent); }
29
23
  & .crisp-inbox-empty-title { font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
package/dist/input.css CHANGED
@@ -1,34 +1,23 @@
1
1
  .crisp-input {
2
-
3
- position: relative; box-sizing: border-box; display: inline-flex; align-items: center;
4
- width: 100%; 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);
5
- font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
6
-
7
- &::after {
8
- content: ""; position: absolute; inset: 0; border-radius: inherit;
9
- border: 2px solid transparent; transition: border-color .1s ease;
10
- pointer-events: none;
11
- }
2
+ position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
3
+ &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
12
4
  &:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
13
-
14
- & .crisp-input-value {
15
- box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%;
16
- align-self: stretch; margin: 0; border: none; background: none; outline: none;
17
- font: inherit; color: var(--crisp-fg-primary);
18
- }
5
+ & .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
19
6
  & .crisp-input-value::placeholder { color: var(--crisp-fg-muted); }
20
7
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
21
8
  &:has(.crisp-input-value:disabled) { opacity: 1; }
22
-
23
9
  }
24
10
  .crisp-input--size_medium {
25
- height: 32px; border-radius: var(--crisp-radius-md); & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
11
+ height: 32px; border-radius: var(--crisp-radius-md);
12
+ & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
26
13
  }
27
14
  .crisp-input--size_large {
28
- height: 36px; border-radius: 10px; & .crisp-input-value { padding-inline: var(--crisp-space-3); }
15
+ height: 36px; border-radius: 10px;
16
+ & .crisp-input-value { padding-inline: var(--crisp-space-3); }
29
17
  }
30
18
  .crisp-input--invalid_true {
31
- &::after { border-color: var(--crisp-bg-danger); } &:focus-within::after { border-color: var(--crisp-bg-danger); }
19
+ &::after { border-color: var(--crisp-bg-danger); }
20
+ &:focus-within::after { border-color: var(--crisp-bg-danger); }
32
21
  }
33
22
  .crisp-input--invalid_false {
34
23
 
@@ -1,35 +1,24 @@
1
1
  @layer crisp-components {
2
2
  .crisp-input {
3
-
4
- position: relative; box-sizing: border-box; display: inline-flex; align-items: center;
5
- width: 100%; 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);
6
- font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
7
-
8
- &::after {
9
- content: ""; position: absolute; inset: 0; border-radius: inherit;
10
- border: 2px solid transparent; transition: border-color .1s ease;
11
- pointer-events: none;
12
- }
3
+ position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
4
+ &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
13
5
  &:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
14
-
15
- & .crisp-input-value {
16
- box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%;
17
- align-self: stretch; margin: 0; border: none; background: none; outline: none;
18
- font: inherit; color: var(--crisp-fg-primary);
19
- }
6
+ & .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
20
7
  & .crisp-input-value::placeholder { color: var(--crisp-fg-muted); }
21
8
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
22
9
  &:has(.crisp-input-value:disabled) { opacity: 1; }
23
-
24
10
  }
25
11
  .crisp-input--size_medium {
26
- height: 32px; border-radius: var(--crisp-radius-md); & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
12
+ height: 32px; border-radius: var(--crisp-radius-md);
13
+ & .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
27
14
  }
28
15
  .crisp-input--size_large {
29
- height: 36px; border-radius: 10px; & .crisp-input-value { padding-inline: var(--crisp-space-3); }
16
+ height: 36px; border-radius: 10px;
17
+ & .crisp-input-value { padding-inline: var(--crisp-space-3); }
30
18
  }
31
19
  .crisp-input--invalid_true {
32
- &::after { border-color: var(--crisp-bg-danger); } &:focus-within::after { border-color: var(--crisp-bg-danger); }
20
+ &::after { border-color: var(--crisp-bg-danger); }
21
+ &:focus-within::after { border-color: var(--crisp-bg-danger); }
33
22
  }
34
23
  .crisp-input--invalid_false {
35
24
 
package/dist/listpage.css CHANGED
@@ -1,32 +1,22 @@
1
1
  .crisp-listpage {
2
- display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary);
3
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
4
-
2
+ 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;
5
3
  & .crisp-listpage-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); height: 52px; flex-shrink: 0; padding: 0 var(--crisp-space-3); }
6
4
  & .crisp-listpage-toolbar-lead { display: flex; align-items: center; gap: var(--crisp-space-2); }
7
5
  & .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
8
6
  & .crisp-listpage-toolbar-trail { display: flex; align-items: center; gap: var(--crisp-space-2); }
9
7
  & .crisp-listpage-count + .crisp-listpage-toolbar-trail { margin-inline-start: var(--crisp-space-3); }
10
-
11
8
  & .crisp-listpage-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--crisp-space-3) var(--crisp-space-3); }
12
-
13
9
  & .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); }
14
- & .crisp-listpage-favorites { display: flex; align-items: center; justify-content: center; min-height: 200px; padding: var(--crisp-space-6); border-radius: var(--crisp-radius-xl);
15
- background: repeating-linear-gradient(0deg, transparent, transparent) padding-box,
16
- border-box; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
10
+ & .crisp-listpage-favorites { display: flex; align-items: center; justify-content: center; min-height: 200px; padding: var(--crisp-space-6); border-radius: var(--crisp-radius-xl); background: repeating-linear-gradient(0deg, transparent, transparent) padding-box, border-box; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
17
11
  & .crisp-listpage-favorites[data-dashed] { box-shadow: none; border: 1px dashed color-mix(in srgb, var(--crisp-fg-primary) 14%, transparent); }
18
-
19
12
  & .crisp-listpage-colhead { display: flex; align-items: center; gap: var(--crisp-space-2-5); height: 40px; padding: 0 var(--crisp-space-2); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
20
13
  & .crisp-listpage-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); }
21
-
22
14
  & .crisp-listpage-group { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 36px; padding: 0 var(--crisp-space-2); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
23
15
  & .crisp-listpage-group-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); color: var(--crisp-fg-muted); }
24
-
25
16
  & .crisp-listpage-row { display: flex; align-items: center; gap: var(--crisp-space-2-5); width: 100%; height: 52px; padding: 0 var(--crisp-space-2); margin: 0; border: none; border-top: 1px solid var(--crisp-border-subtle); background: none; font-family: inherit; color: inherit; text-align: start; cursor: pointer; transition: background-color .12s; }
26
17
  & .crisp-listpage-row:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; }
27
18
  & .crisp-listpage-row:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); }
28
19
  & .crisp-listpage-row-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
29
-
30
20
  & .crisp-listpage-empty { padding: var(--crisp-space-8) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
31
21
  }
32
22
 
@@ -1,33 +1,23 @@
1
1
  @layer crisp-components {
2
2
  .crisp-listpage {
3
- display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: var(--crisp-font-sans); color: var(--crisp-fg-primary);
4
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
5
-
3
+ 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;
6
4
  & .crisp-listpage-toolbar { display: flex; align-items: center; gap: var(--crisp-space-2); height: 52px; flex-shrink: 0; padding: 0 var(--crisp-space-3); }
7
5
  & .crisp-listpage-toolbar-lead { display: flex; align-items: center; gap: var(--crisp-space-2); }
8
6
  & .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
9
7
  & .crisp-listpage-toolbar-trail { display: flex; align-items: center; gap: var(--crisp-space-2); }
10
8
  & .crisp-listpage-count + .crisp-listpage-toolbar-trail { margin-inline-start: var(--crisp-space-3); }
11
-
12
9
  & .crisp-listpage-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--crisp-space-3) var(--crisp-space-3); }
13
-
14
10
  & .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); }
15
- & .crisp-listpage-favorites { display: flex; align-items: center; justify-content: center; min-height: 200px; padding: var(--crisp-space-6); border-radius: var(--crisp-radius-xl);
16
- background: repeating-linear-gradient(0deg, transparent, transparent) padding-box,
17
- border-box; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
11
+ & .crisp-listpage-favorites { display: flex; align-items: center; justify-content: center; min-height: 200px; padding: var(--crisp-space-6); border-radius: var(--crisp-radius-xl); background: repeating-linear-gradient(0deg, transparent, transparent) padding-box, border-box; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
18
12
  & .crisp-listpage-favorites[data-dashed] { box-shadow: none; border: 1px dashed color-mix(in srgb, var(--crisp-fg-primary) 14%, transparent); }
19
-
20
13
  & .crisp-listpage-colhead { display: flex; align-items: center; gap: var(--crisp-space-2-5); height: 40px; padding: 0 var(--crisp-space-2); box-shadow: inset 0 -1px 0 var(--crisp-border-subtle); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
21
14
  & .crisp-listpage-colhead-cell { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); }
22
-
23
15
  & .crisp-listpage-group { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 36px; padding: 0 var(--crisp-space-2); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
24
16
  & .crisp-listpage-group-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); color: var(--crisp-fg-muted); }
25
-
26
17
  & .crisp-listpage-row { display: flex; align-items: center; gap: var(--crisp-space-2-5); width: 100%; height: 52px; padding: 0 var(--crisp-space-2); margin: 0; border: none; border-top: 1px solid var(--crisp-border-subtle); background: none; font-family: inherit; color: inherit; text-align: start; cursor: pointer; transition: background-color .12s; }
27
18
  & .crisp-listpage-row:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; }
28
19
  & .crisp-listpage-row:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); }
29
20
  & .crisp-listpage-row-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
30
-
31
21
  & .crisp-listpage-empty { padding: var(--crisp-space-8) var(--crisp-space-4); text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
32
22
  }
33
23
 
package/dist/menu.css CHANGED
@@ -1,25 +1,11 @@
1
1
  .crisp-menu {
2
-
3
- position: relative; display: inline-flex; font-family: var(--crisp-font-sans);
4
- -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
5
-
6
- & .crisp-menu-content {
7
- position: fixed; z-index: 60; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px);
8
- min-width: 200px; max-height: 360px; overflow-y: auto; padding: var(--crisp-space-1);
9
- border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover);
10
- 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);
11
- transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
12
- }
2
+ position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
3
+ & .crisp-menu-content { position: fixed; z-index: 60; box-sizing: border-box; display: flex; flex-direction: column; gap: var(--crisp-space-px); min-width: 200px; max-height: 360px; overflow-y: auto; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover); 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); transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease; }
13
4
  & .crisp-menu-content[data-align="end"] { transform-origin: top right; }
14
5
  & .crisp-menu-content[data-up] { transform-origin: bottom left; }
15
6
  & .crisp-menu-content[data-up][data-align="end"] { transform-origin: bottom right; }
16
7
  & .crisp-menu-content { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
17
-
18
- & .crisp-menu-item {
19
- box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); flex-shrink: 0;
20
- margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md);
21
- font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); text-align: start; cursor: pointer; white-space: nowrap;
22
- }
8
+ & .crisp-menu-item { box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); flex-shrink: 0; margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); text-align: start; cursor: pointer; white-space: nowrap; }
23
9
  & .crisp-menu-item svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
24
10
  & .crisp-menu-item:focus { outline: none; }
25
11
  & .crisp-menu-item:hover:not([data-disabled]), & .crisp-menu-item:focus-visible:not([data-disabled]), & .crisp-menu-item[data-highlighted]:not([data-disabled]) { background: var(--crisp-bg-raised); }
@@ -31,10 +17,8 @@
31
17
  & .crisp-menu-item-shortcut { flex-shrink: 0; margin-inline-start: var(--crisp-space-3); font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); letter-spacing: 0.02em; }
32
18
  & .crisp-menu-item-check { flex-shrink: 0; color: var(--crisp-bg-brand-solid); }
33
19
  & .crisp-menu-item-check--placeholder { width: 15px; }
34
-
35
20
  & .crisp-menu-sep { height: 1px; flex-shrink: 0; border: none; margin: var(--crisp-space-1) calc(-1 * var(--crisp-space-1)); background: var(--crisp-border-subtle); }
36
21
  & .crisp-menu-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
37
-
38
22
  }
39
23
 
40
24
  @media (prefers-reduced-motion: reduce) {