@crisp-ui-kit/crisp 0.52.1 → 0.52.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.
@@ -125,6 +125,53 @@ exports.recordPageLabels = {
125
125
  recordCreated: (name) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("b", { children: name }), " record was created"] })),
126
126
  noEmailsWith: (name) => (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["No emails logged with ", name, " yet."] }),
127
127
  };
128
+ /** `Select` owns its own closed-by-default `open` state with no prop or
129
+ * imperative way to start it open -- the only thing that opens it is a real
130
+ * click on its trigger button. Mounting one on click (rather than rendering
131
+ * it already open) leaves the reader with a second click to make before
132
+ * they can pick anything, so drive that click ourselves the moment it
133
+ * mounts. Mirrors `DataTable`'s own `useAutoOpenSelect` (#453) -- kept as
134
+ * its own copy here rather than a shared import, the same "duplicated per
135
+ * product surface" choice already made for `EditableOption`/
136
+ * `editableInputType` above. Guarded against firing twice (StrictMode
137
+ * double-invokes effects): `Select`'s own trigger toggles, so a second
138
+ * click would close what the first one opened. */
139
+ function useAutoOpenSelect() {
140
+ const ref = React.useRef(null);
141
+ const opened = React.useRef(false);
142
+ React.useEffect(() => {
143
+ if (opened.current)
144
+ return;
145
+ opened.current = true;
146
+ ref.current
147
+ ?.querySelector(".crisp-select-trigger")
148
+ ?.click();
149
+ }, []);
150
+ return ref;
151
+ }
152
+ /** `Select` closes its own popover on Escape but has no callback telling
153
+ * the FIELD to leave editing mode -- and DOM focus doesn't reliably end up
154
+ * inside the newly-opened popover either, so a focus-dependent React
155
+ * `onKeyDown` can't be trusted to see the key at all. Mirrors `Select`'s
156
+ * own "document-level fallback for Escape" (see select.tsx) and
157
+ * `DataTable`'s identical fix for the same problem (#453). */
158
+ function useEscapeExitsEditing(onCancel) {
159
+ React.useEffect(() => {
160
+ const onKey = (e) => {
161
+ if (e.key === "Escape")
162
+ onCancel();
163
+ };
164
+ document.addEventListener("keydown", onKey);
165
+ return () => document.removeEventListener("keydown", onKey);
166
+ }, [onCancel]);
167
+ }
168
+ /** `editor: "select"`'s picker, its own component (not inline in the field
169
+ * loop) because it needs the two hooks above (#457). */
170
+ function RecordPageSelectEditor({ col, value, isPending, onCommit, onCancel, }) {
171
+ const ref = useAutoOpenSelect();
172
+ useEscapeExitsEditing(onCancel);
173
+ return ((0, jsx_runtime_1.jsxs)("span", { ref: ref, className: "crisp-recordpage-edit-picker", children: [(0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": `Edit ${col.label}`, value: value != null ? String(value) : undefined, disabled: isPending, options: col.editOptions ?? [], onValueChange: onCommit }), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }));
174
+ }
128
175
  function TagsValue({ values }) {
129
176
  const [expanded, setExpanded] = React.useState(false);
130
177
  const overflow = values.length - 1;
@@ -135,6 +182,21 @@ function FieldValue({ col, row, labels, }) {
135
182
  const v = row[col.key];
136
183
  if (valueEmpty(v))
137
184
  return (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-empty", children: labels.setAValue });
185
+ // `editor: "select"` at rest (#457) -- checked ahead of `col.type` since
186
+ // the editor is orthogonal to it. Same dot+label shape as RecordsTable's
187
+ // own measured status chip, but this component's own classes
188
+ // (`crisp-recordpage-status[-dot]`): RecordsTable's own pair is scoped
189
+ // under `.crisp-recordstable`'s recipe root, so reusing its classnames
190
+ // verbatim here would resolve to nothing. Shown when the matched option
191
+ // carries a `color`, same as every other status-shaped value in this
192
+ // codebase; a colourless option (or no match) falls back to plain text.
193
+ if (col.editor === "select") {
194
+ const opt = col.editOptions?.find((o) => o.value === v);
195
+ const text = opt?.label ?? String(v);
196
+ if (!opt?.color)
197
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: text });
198
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-status", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-status-dot", style: { background: opt.color } }), text] }));
199
+ }
138
200
  switch (col.type) {
139
201
  case "tags":
140
202
  return (0, jsx_runtime_1.jsx)(TagsValue, { values: v });
@@ -455,14 +517,31 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
455
517
  const isPending = pendingKey === col.key;
456
518
  const fieldError = error?.key === col.key ? error.message : null;
457
519
  const label = ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-label", children: [col.icon ? ((0, jsx_runtime_1.jsx)(col.icon, { size: 15, strokeWidth: 1.75 })) : null, (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-field-label-text", children: col.label })] }));
458
- // select/switch are always their own live widget -- no
459
- // click-to-open, no separate edit/view mode, the same
460
- // convention DataTable's identical `editor` columns use
461
- // (#385): choosing a value or flipping a toggle IS the
462
- // commit gesture.
463
- if (canEdit && col.editor) {
520
+ // `switch` is always its own live widget -- no
521
+ // click-to-open, no separate edit/view mode: flipping the
522
+ // toggle IS the commit gesture, and there's no
523
+ // meaningfully different "rest" display for a boolean
524
+ // (same exception `DataTable`'s own `editor` makes, #453).
525
+ if (canEdit && col.editor === "switch") {
526
+ const value = record[col.key];
527
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-field", children: [label, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-value", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-edit-picker", children: [(0, jsx_runtime_1.jsx)(switch_1.Switch, { "aria-label": `Edit ${col.label}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commit(record.id, col.key, String(checked)) }), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
528
+ }
529
+ // `select` opens crisp's own `Select` on a SINGLE click
530
+ // (#457, corrects #431/#385's original always-live
531
+ // copy of the same regression #453 fixed on `DataTable`)
532
+ // -- like every other editable type here, at rest it
533
+ // shows its value (see `FieldValue`'s own `col.editor
534
+ // === "select"` case) via the generic click-to-open
535
+ // branch below, and only mounts `Select` once
536
+ // `editingKey` matches. `DataTable`'s own double-click
537
+ // gate doesn't apply: `RecordPage`'s plain text/date
538
+ // fields already open on a single click (measured live
539
+ // for #431), so `select` matches that, not `DataTable`.
540
+ if (canEdit &&
541
+ editingKey === col.key &&
542
+ col.editor === "select") {
464
543
  const value = record[col.key];
465
- return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-field", children: [label, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-value", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-edit-picker", children: [col.editor === "select" ? ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": `Edit ${col.label}`, value: value != null ? String(value) : undefined, disabled: isPending, options: col.editOptions ?? [], onValueChange: (v) => commit(record.id, col.key, v) })) : ((0, jsx_runtime_1.jsx)(switch_1.Switch, { "aria-label": `Edit ${col.label}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commit(record.id, col.key, String(checked)) })), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
544
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-field", children: [label, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-value", children: [(0, jsx_runtime_1.jsx)(RecordPageSelectEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commit(record.id, col.key, v), onCancel: closeEditing }), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
466
545
  }
467
546
  if (canEdit && editingKey === col.key) {
468
547
  const value = record[col.key];
@@ -108,6 +108,13 @@ const config = (0, core_1.defineRecipe)({
108
108
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
109
109
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
110
110
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: ${tokens_1.contract.space["0.5"]}; font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.bgDanger}; }
111
+ /* #457: an \`editor: "select"\` field's at-rest chip -- same shape as
112
+ RecordsTable's own \`.crisp-recordstable-status[-dot]\` (already
113
+ measured against Attio there), but its own classes: that pair is
114
+ scoped under \`.crisp-recordstable\`'s own recipe root, so reusing the
115
+ classnames verbatim here would resolve to nothing. */
116
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; min-width: 0; }
117
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${tokens_1.contract.radius.full}; }
111
118
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; width: 100%; }
112
119
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
113
120
  & .crisp-recordpage-link { color: ${tokens_1.contract.fgPrimary}; text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
@@ -88,6 +88,53 @@ export const recordPageLabels = {
88
88
  recordCreated: (name) => (_jsxs(_Fragment, { children: [_jsx("b", { children: name }), " record was created"] })),
89
89
  noEmailsWith: (name) => _jsxs(_Fragment, { children: ["No emails logged with ", name, " yet."] }),
90
90
  };
91
+ /** `Select` owns its own closed-by-default `open` state with no prop or
92
+ * imperative way to start it open -- the only thing that opens it is a real
93
+ * click on its trigger button. Mounting one on click (rather than rendering
94
+ * it already open) leaves the reader with a second click to make before
95
+ * they can pick anything, so drive that click ourselves the moment it
96
+ * mounts. Mirrors `DataTable`'s own `useAutoOpenSelect` (#453) -- kept as
97
+ * its own copy here rather than a shared import, the same "duplicated per
98
+ * product surface" choice already made for `EditableOption`/
99
+ * `editableInputType` above. Guarded against firing twice (StrictMode
100
+ * double-invokes effects): `Select`'s own trigger toggles, so a second
101
+ * click would close what the first one opened. */
102
+ function useAutoOpenSelect() {
103
+ const ref = React.useRef(null);
104
+ const opened = React.useRef(false);
105
+ React.useEffect(() => {
106
+ if (opened.current)
107
+ return;
108
+ opened.current = true;
109
+ ref.current
110
+ ?.querySelector(".crisp-select-trigger")
111
+ ?.click();
112
+ }, []);
113
+ return ref;
114
+ }
115
+ /** `Select` closes its own popover on Escape but has no callback telling
116
+ * the FIELD to leave editing mode -- and DOM focus doesn't reliably end up
117
+ * inside the newly-opened popover either, so a focus-dependent React
118
+ * `onKeyDown` can't be trusted to see the key at all. Mirrors `Select`'s
119
+ * own "document-level fallback for Escape" (see select.tsx) and
120
+ * `DataTable`'s identical fix for the same problem (#453). */
121
+ function useEscapeExitsEditing(onCancel) {
122
+ React.useEffect(() => {
123
+ const onKey = (e) => {
124
+ if (e.key === "Escape")
125
+ onCancel();
126
+ };
127
+ document.addEventListener("keydown", onKey);
128
+ return () => document.removeEventListener("keydown", onKey);
129
+ }, [onCancel]);
130
+ }
131
+ /** `editor: "select"`'s picker, its own component (not inline in the field
132
+ * loop) because it needs the two hooks above (#457). */
133
+ function RecordPageSelectEditor({ col, value, isPending, onCommit, onCancel, }) {
134
+ const ref = useAutoOpenSelect();
135
+ useEscapeExitsEditing(onCancel);
136
+ return (_jsxs("span", { ref: ref, className: "crisp-recordpage-edit-picker", children: [_jsx(Select, { variant: "ghost", "aria-label": `Edit ${col.label}`, value: value != null ? String(value) : undefined, disabled: isPending, options: col.editOptions ?? [], onValueChange: onCommit }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }));
137
+ }
91
138
  function TagsValue({ values }) {
92
139
  const [expanded, setExpanded] = React.useState(false);
93
140
  const overflow = values.length - 1;
@@ -98,6 +145,21 @@ function FieldValue({ col, row, labels, }) {
98
145
  const v = row[col.key];
99
146
  if (valueEmpty(v))
100
147
  return _jsx("span", { className: "crisp-recordpage-empty", children: labels.setAValue });
148
+ // `editor: "select"` at rest (#457) -- checked ahead of `col.type` since
149
+ // the editor is orthogonal to it. Same dot+label shape as RecordsTable's
150
+ // own measured status chip, but this component's own classes
151
+ // (`crisp-recordpage-status[-dot]`): RecordsTable's own pair is scoped
152
+ // under `.crisp-recordstable`'s recipe root, so reusing its classnames
153
+ // verbatim here would resolve to nothing. Shown when the matched option
154
+ // carries a `color`, same as every other status-shaped value in this
155
+ // codebase; a colourless option (or no match) falls back to plain text.
156
+ if (col.editor === "select") {
157
+ const opt = col.editOptions?.find((o) => o.value === v);
158
+ const text = opt?.label ?? String(v);
159
+ if (!opt?.color)
160
+ return _jsx(_Fragment, { children: text });
161
+ return (_jsxs("span", { className: "crisp-recordpage-status", children: [_jsx("span", { className: "crisp-recordpage-status-dot", style: { background: opt.color } }), text] }));
162
+ }
101
163
  switch (col.type) {
102
164
  case "tags":
103
165
  return _jsx(TagsValue, { values: v });
@@ -418,14 +480,31 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
418
480
  const isPending = pendingKey === col.key;
419
481
  const fieldError = error?.key === col.key ? error.message : null;
420
482
  const label = (_jsxs("span", { className: "crisp-recordpage-field-label", children: [col.icon ? (_jsx(col.icon, { size: 15, strokeWidth: 1.75 })) : null, _jsx("span", { className: "crisp-recordpage-field-label-text", children: col.label })] }));
421
- // select/switch are always their own live widget -- no
422
- // click-to-open, no separate edit/view mode, the same
423
- // convention DataTable's identical `editor` columns use
424
- // (#385): choosing a value or flipping a toggle IS the
425
- // commit gesture.
426
- if (canEdit && col.editor) {
483
+ // `switch` is always its own live widget -- no
484
+ // click-to-open, no separate edit/view mode: flipping the
485
+ // toggle IS the commit gesture, and there's no
486
+ // meaningfully different "rest" display for a boolean
487
+ // (same exception `DataTable`'s own `editor` makes, #453).
488
+ if (canEdit && col.editor === "switch") {
489
+ const value = record[col.key];
490
+ return (_jsxs("div", { className: "crisp-recordpage-field", children: [label, _jsxs("span", { className: "crisp-recordpage-field-value", children: [_jsxs("span", { className: "crisp-recordpage-edit-picker", children: [_jsx(Switch, { "aria-label": `Edit ${col.label}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commit(record.id, col.key, String(checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }), fieldError && (_jsx("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
491
+ }
492
+ // `select` opens crisp's own `Select` on a SINGLE click
493
+ // (#457, corrects #431/#385's original always-live
494
+ // copy of the same regression #453 fixed on `DataTable`)
495
+ // -- like every other editable type here, at rest it
496
+ // shows its value (see `FieldValue`'s own `col.editor
497
+ // === "select"` case) via the generic click-to-open
498
+ // branch below, and only mounts `Select` once
499
+ // `editingKey` matches. `DataTable`'s own double-click
500
+ // gate doesn't apply: `RecordPage`'s plain text/date
501
+ // fields already open on a single click (measured live
502
+ // for #431), so `select` matches that, not `DataTable`.
503
+ if (canEdit &&
504
+ editingKey === col.key &&
505
+ col.editor === "select") {
427
506
  const value = record[col.key];
428
- return (_jsxs("div", { className: "crisp-recordpage-field", children: [label, _jsxs("span", { className: "crisp-recordpage-field-value", children: [_jsxs("span", { className: "crisp-recordpage-edit-picker", children: [col.editor === "select" ? (_jsx(Select, { variant: "ghost", "aria-label": `Edit ${col.label}`, value: value != null ? String(value) : undefined, disabled: isPending, options: col.editOptions ?? [], onValueChange: (v) => commit(record.id, col.key, v) })) : (_jsx(Switch, { "aria-label": `Edit ${col.label}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commit(record.id, col.key, String(checked)) })), isPending && (_jsx(Spinner, { size: 14, className: "crisp-recordpage-edit-pending" }))] }), fieldError && (_jsx("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
507
+ return (_jsxs("div", { className: "crisp-recordpage-field", children: [label, _jsxs("span", { className: "crisp-recordpage-field-value", children: [_jsx(RecordPageSelectEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commit(record.id, col.key, v), onCancel: closeEditing }), fieldError && (_jsx("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
429
508
  }
430
509
  if (canEdit && editingKey === col.key) {
431
510
  const value = record[col.key];
@@ -105,6 +105,13 @@ const config = defineRecipe({
105
105
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
106
106
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: ${c.fgMuted}; }
107
107
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: ${c.space["0.5"]}; font-size: ${c.text.sm}; color: ${c.bgDanger}; }
108
+ /* #457: an \`editor: "select"\` field's at-rest chip -- same shape as
109
+ RecordsTable's own \`.crisp-recordstable-status[-dot]\` (already
110
+ measured against Attio there), but its own classes: that pair is
111
+ scoped under \`.crisp-recordstable\`'s own recipe root, so reusing the
112
+ classnames verbatim here would resolve to nothing. */
113
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; }
114
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${c.radius.full}; }
108
115
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: ${c.space["1"]}; width: 100%; }
109
116
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
110
117
  & .crisp-recordpage-link { color: ${c.fgPrimary}; text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
@@ -83,23 +83,33 @@
83
83
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
84
84
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
85
85
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
86
+ /* #457: an `editor: "select"` field's at-rest chip -- same shape as
87
+ RecordsTable's own `.crisp-recordstable-status[-dot]` (already measured against Attio there), but its own classes: that pair is scoped under `.crisp-recordstable`'s own recipe root, so reusing the
88
+ classnames verbatim here would resolve to nothing. */
89
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
90
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
86
91
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
87
92
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
88
93
  & .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
89
94
  & .crisp-recordpage-link:hover { text-decoration: underline; }
90
95
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
96
+
91
97
  & .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); }
98
+
92
99
  & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
93
100
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
94
101
  & .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); }
95
102
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
96
- /* 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. */
103
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
104
+ column count, so the grid stays responsive (auto-fit) without per-cell
105
+ nth-child border hacks that break when the column count changes. */
97
106
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
98
107
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
99
108
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
100
109
  & .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); }
101
110
  & .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); }
102
111
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
112
+
103
113
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
104
114
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
105
115
  & .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); }
@@ -108,6 +118,7 @@
108
118
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
109
119
  & .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; }
110
120
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
121
+
111
122
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
112
123
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
113
124
  & .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); }
@@ -115,6 +126,7 @@
115
126
  & .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; }
116
127
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
117
128
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
129
+
118
130
  & .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); }
119
131
  & .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); }
120
132
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -124,18 +136,23 @@
124
136
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
125
137
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
126
138
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
139
+
127
140
  & .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); }
128
141
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
129
142
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
130
143
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
131
144
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
132
145
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
146
+
133
147
  & .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); }
134
148
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
135
149
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
150
+
136
151
  & .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; }
137
152
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
153
+
138
154
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
155
+
139
156
  & .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); }
140
157
  & .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)); }
141
158
  & .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)); }
@@ -84,23 +84,33 @@
84
84
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
85
85
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
86
86
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
87
+ /* #457: an `editor: "select"` field's at-rest chip -- same shape as
88
+ RecordsTable's own `.crisp-recordstable-status[-dot]` (already measured against Attio there), but its own classes: that pair is scoped under `.crisp-recordstable`'s own recipe root, so reusing the
89
+ classnames verbatim here would resolve to nothing. */
90
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
91
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
87
92
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
88
93
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
89
94
  & .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
90
95
  & .crisp-recordpage-link:hover { text-decoration: underline; }
91
96
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
97
+
92
98
  & .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); }
99
+
93
100
  & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
94
101
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
95
102
  & .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); }
96
103
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
97
- /* 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. */
104
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
105
+ column count, so the grid stays responsive (auto-fit) without per-cell
106
+ nth-child border hacks that break when the column count changes. */
98
107
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
99
108
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
100
109
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
101
110
  & .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); }
102
111
  & .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); }
103
112
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
113
+
104
114
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
105
115
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
106
116
  & .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); }
@@ -109,6 +119,7 @@
109
119
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
110
120
  & .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; }
111
121
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
122
+
112
123
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
113
124
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
114
125
  & .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); }
@@ -116,6 +127,7 @@
116
127
  & .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; }
117
128
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
118
129
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
130
+
119
131
  & .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); }
120
132
  & .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); }
121
133
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -125,18 +137,23 @@
125
137
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
126
138
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
127
139
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
140
+
128
141
  & .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); }
129
142
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
130
143
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
131
144
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
132
145
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
133
146
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
147
+
134
148
  & .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); }
135
149
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
136
150
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
151
+
137
152
  & .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; }
138
153
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
154
+
139
155
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
156
+
140
157
  & .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); }
141
158
  & .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)); }
142
159
  & .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)); }
package/dist/styles.css CHANGED
@@ -2163,23 +2163,33 @@
2163
2163
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
2164
2164
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
2165
2165
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
2166
+ /* #457: an `editor: "select"` field's at-rest chip -- same shape as
2167
+ RecordsTable's own `.crisp-recordstable-status[-dot]` (already measured against Attio there), but its own classes: that pair is scoped under `.crisp-recordstable`'s own recipe root, so reusing the
2168
+ classnames verbatim here would resolve to nothing. */
2169
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
2170
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
2166
2171
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
2167
2172
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
2168
2173
  & .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
2169
2174
  & .crisp-recordpage-link:hover { text-decoration: underline; }
2170
2175
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2176
+
2171
2177
  & .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); }
2178
+
2172
2179
  & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2173
2180
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
2174
2181
  & .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); }
2175
2182
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
2176
- /* 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. */
2183
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
2184
+ column count, so the grid stays responsive (auto-fit) without per-cell
2185
+ nth-child border hacks that break when the column count changes. */
2177
2186
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
2178
2187
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
2179
2188
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
2180
2189
  & .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); }
2181
2190
  & .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); }
2182
2191
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
2192
+
2183
2193
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
2184
2194
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
2185
2195
  & .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); }
@@ -2188,6 +2198,7 @@
2188
2198
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
2189
2199
  & .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; }
2190
2200
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
2201
+
2191
2202
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
2192
2203
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
2193
2204
  & .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); }
@@ -2195,6 +2206,7 @@
2195
2206
  & .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; }
2196
2207
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
2197
2208
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
2209
+
2198
2210
  & .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); }
2199
2211
  & .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); }
2200
2212
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -2204,18 +2216,23 @@
2204
2216
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
2205
2217
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
2206
2218
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
2219
+
2207
2220
  & .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); }
2208
2221
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
2209
2222
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
2210
2223
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2211
2224
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
2212
2225
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2226
+
2213
2227
  & .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); }
2214
2228
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
2215
2229
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
2230
+
2216
2231
  & .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; }
2217
2232
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
2233
+
2218
2234
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
2235
+
2219
2236
  & .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); }
2220
2237
  & .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)); }
2221
2238
  & .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)); }
@@ -2164,23 +2164,33 @@
2164
2164
  & .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
2165
2165
  & .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
2166
2166
  & .crisp-recordpage-edit-error { flex-basis: 100%; margin-top: var(--crisp-space-0-5); font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
2167
+ /* #457: an `editor: "select"` field's at-rest chip -- same shape as
2168
+ RecordsTable's own `.crisp-recordstable-status[-dot]` (already measured against Attio there), but its own classes: that pair is scoped under `.crisp-recordstable`'s own recipe root, so reusing the
2169
+ classnames verbatim here would resolve to nothing. */
2170
+ & .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
2171
+ & .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
2167
2172
  & .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
2168
2173
  & .crisp-recordpage-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; }
2169
2174
  & .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
2170
2175
  & .crisp-recordpage-link:hover { text-decoration: underline; }
2171
2176
  & .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2177
+
2172
2178
  & .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); }
2179
+
2173
2180
  & .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2174
2181
  & .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
2175
2182
  & .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); }
2176
2183
  & .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
2177
- /* 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. */
2184
+ /* gap-as-gridlines: a 1px gap over the border colour draws dividers at any
2185
+ column count, so the grid stays responsive (auto-fit) without per-cell
2186
+ nth-child border hacks that break when the column count changes. */
2178
2187
  & .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
2179
2188
  & .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
2180
2189
  & .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
2181
2190
  & .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); }
2182
2191
  & .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); }
2183
2192
  & .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
2193
+
2184
2194
  & .crisp-recordpage-feed { display: flex; flex-direction: column; }
2185
2195
  & .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
2186
2196
  & .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); }
@@ -2189,6 +2199,7 @@
2189
2199
  & .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
2190
2200
  & .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; }
2191
2201
  & .crisp-recordpage-feed-more:hover { text-decoration: underline; }
2202
+
2192
2203
  & .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
2193
2204
  & .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
2194
2205
  & .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); }
@@ -2196,6 +2207,7 @@
2196
2207
  & .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; }
2197
2208
  & .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
2198
2209
  & .crisp-recordpage-addbtn svg { flex-shrink: 0; }
2210
+
2199
2211
  & .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); }
2200
2212
  & .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); }
2201
2213
  & .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
@@ -2205,18 +2217,23 @@
2205
2217
  & .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
2206
2218
  & .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
2207
2219
  & .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
2220
+
2208
2221
  & .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); }
2209
2222
  & .crisp-recordpage-note-body { flex: 1; min-width: 0; }
2210
2223
  & .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
2211
2224
  & .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
2212
2225
  & .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
2213
2226
  & .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2227
+
2214
2228
  & .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); }
2215
2229
  & .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
2216
2230
  & .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
2231
+
2217
2232
  & .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; }
2218
2233
  & .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
2234
+
2219
2235
  & .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
2236
+
2220
2237
  & .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); }
2221
2238
  & .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)); }
2222
2239
  & .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)); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.52.1",
3
+ "version": "0.52.2",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",