@crisp-ui-kit/crisp 0.53.5 → 0.54.1
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.
- package/dist/activityfeed.css +12 -0
- package/dist/activityfeed.d.ts +7 -0
- package/dist/activityfeed.layered.css +14 -0
- package/dist/badge.css +3 -0
- package/dist/badge.d.ts +2 -2
- package/dist/badge.layered.css +3 -0
- package/dist/cjs/components/badge/badge.recipe.js +10 -0
- package/dist/cjs/components/bulkbar/bulkbar.js +5 -1
- package/dist/cjs/components/fileupload/fileupload.js +19 -3
- package/dist/cjs/components/formfield/formfield.js +8 -1
- package/dist/cjs/components/heatmapchart/heatmapchart.js +14 -2
- package/dist/cjs/components/skiplink/index.js +17 -0
- package/dist/cjs/components/skiplink/skiplink.js +16 -0
- package/dist/cjs/components/skiplink/skiplink.recipe.js +33 -0
- package/dist/cjs/components/textlink/index.js +7 -0
- package/dist/cjs/components/textlink/textlink.js +16 -0
- package/dist/cjs/components/textlink/textlink.recipe.js +35 -0
- package/dist/cjs/components/visuallyhidden/index.js +17 -0
- package/dist/cjs/components/visuallyhidden/visuallyhidden.js +16 -0
- package/dist/cjs/components/visuallyhidden/visuallyhidden.recipe.js +20 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/product/activityfeed/activityfeed.js +38 -0
- package/dist/cjs/product/activityfeed/activityfeed.recipe.js +35 -0
- package/dist/cjs/product/activityfeed/index.js +10 -0
- package/dist/cjs/product/board/board.js +11 -2
- package/dist/cjs/product/command/command.js +13 -1
- package/dist/cjs/product/flowcanvas/flowcanvas.js +9 -2
- package/dist/cjs/product/headerbar/headerbar.js +15 -0
- package/dist/cjs/product/headerbar/headerbar.recipe.js +35 -0
- package/dist/cjs/product/headerbar/index.js +17 -0
- package/dist/cjs/product/inbox/inbox.js +13 -1
- package/dist/cjs/product/onboarding/onboarding.js +22 -1
- package/dist/cjs/product/recordpage/index.js +2 -1
- package/dist/cjs/product/recordpage/recordpage.js +197 -123
- package/dist/cjs/product/recordpage/recordpage.recipe.js +5 -0
- package/dist/cjs/product/richtext/richtext.js +43 -0
- package/dist/cjs/product/tasklist/tasklist.js +8 -1
- package/dist/cjs/tokens/palette.js +14 -0
- package/dist/cjs/tokens/semantic.js +5 -0
- package/dist/esm/components/badge/badge.recipe.js +10 -0
- package/dist/esm/components/bulkbar/bulkbar.js +5 -1
- package/dist/esm/components/fileupload/fileupload.js +20 -4
- package/dist/esm/components/formfield/formfield.js +8 -1
- package/dist/esm/components/heatmapchart/heatmapchart.js +14 -2
- package/dist/esm/components/skiplink/index.js +1 -0
- package/dist/esm/components/skiplink/skiplink.js +13 -0
- package/dist/esm/components/skiplink/skiplink.recipe.js +30 -0
- package/dist/esm/components/textlink/index.js +2 -0
- package/dist/esm/components/textlink/textlink.js +13 -0
- package/dist/esm/components/textlink/textlink.recipe.js +32 -0
- package/dist/esm/components/visuallyhidden/index.js +1 -0
- package/dist/esm/components/visuallyhidden/visuallyhidden.js +13 -0
- package/dist/esm/components/visuallyhidden/visuallyhidden.recipe.js +17 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/product/activityfeed/activityfeed.js +32 -0
- package/dist/esm/product/activityfeed/activityfeed.recipe.js +32 -0
- package/dist/esm/product/activityfeed/index.js +2 -0
- package/dist/esm/product/board/board.js +12 -3
- package/dist/esm/product/command/command.js +14 -2
- package/dist/esm/product/flowcanvas/flowcanvas.js +9 -2
- package/dist/esm/product/headerbar/headerbar.js +12 -0
- package/dist/esm/product/headerbar/headerbar.recipe.js +32 -0
- package/dist/esm/product/headerbar/index.js +1 -0
- package/dist/esm/product/inbox/inbox.js +13 -1
- package/dist/esm/product/onboarding/onboarding.js +22 -1
- package/dist/esm/product/recordpage/index.js +1 -1
- package/dist/esm/product/recordpage/recordpage.js +197 -124
- package/dist/esm/product/recordpage/recordpage.recipe.js +5 -0
- package/dist/esm/product/richtext/richtext.js +43 -0
- package/dist/esm/product/tasklist/tasklist.js +8 -1
- package/dist/esm/tokens/palette.js +14 -0
- package/dist/esm/tokens/semantic.js +5 -0
- package/dist/headerbar.css +7 -0
- package/dist/headerbar.d.ts +7 -0
- package/dist/headerbar.layered.css +9 -0
- package/dist/native/tokens.d.ts +1 -1
- package/dist/native/tokens.js +16 -4
- package/dist/recordpage.css +17 -31
- package/dist/recordpage.layered.css +17 -31
- package/dist/skiplink.css +5 -0
- package/dist/skiplink.d.ts +7 -0
- package/dist/skiplink.layered.css +7 -0
- package/dist/styles.css +69 -31
- package/dist/styles.layered.css +69 -31
- package/dist/textlink.css +11 -0
- package/dist/textlink.d.ts +7 -0
- package/dist/textlink.layered.css +13 -0
- package/dist/tokens.css +6 -0
- package/dist/tokens.json +16 -4
- package/dist/types/components/badge/badge.d.ts +2 -0
- package/dist/types/components/badge/badge.recipe.d.ts +8 -0
- package/dist/types/components/bulkbar/bulkbar.d.ts +1 -1
- package/dist/types/components/heatmapchart/heatmapchart.d.ts +1 -1
- package/dist/types/components/skiplink/index.d.ts +1 -0
- package/dist/types/components/skiplink/skiplink.d.ts +18 -0
- package/dist/types/components/skiplink/skiplink.recipe.d.ts +3 -0
- package/dist/types/components/textlink/index.d.ts +2 -0
- package/dist/types/components/textlink/textlink.d.ts +19 -0
- package/dist/types/components/textlink/textlink.recipe.d.ts +13 -0
- package/dist/types/components/visuallyhidden/index.d.ts +1 -0
- package/dist/types/components/visuallyhidden/visuallyhidden.d.ts +15 -0
- package/dist/types/components/visuallyhidden/visuallyhidden.recipe.d.ts +3 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/product/activityfeed/activityfeed.d.ts +57 -0
- package/dist/types/product/activityfeed/activityfeed.recipe.d.ts +3 -0
- package/dist/types/product/activityfeed/index.d.ts +2 -0
- package/dist/types/product/board/board.d.ts +1 -1
- package/dist/types/product/command/command.d.ts +1 -1
- package/dist/types/product/flowcanvas/flowcanvas.d.ts +1 -1
- package/dist/types/product/headerbar/headerbar.d.ts +27 -0
- package/dist/types/product/headerbar/headerbar.recipe.d.ts +3 -0
- package/dist/types/product/headerbar/index.d.ts +1 -0
- package/dist/types/product/inbox/inbox.d.ts +1 -1
- package/dist/types/product/onboarding/onboarding.d.ts +1 -1
- package/dist/types/product/recordpage/index.d.ts +1 -1
- package/dist/types/product/recordpage/recordpage.d.ts +90 -1
- package/dist/types/product/tasklist/tasklist.d.ts +1 -1
- package/dist/types/tokens/contract.d.ts +3 -0
- package/dist/types/tokens/palette.d.ts +12 -0
- package/dist/types/tokens/semantic.d.ts +5 -0
- package/dist/visuallyhidden.css +3 -0
- package/dist/visuallyhidden.d.ts +7 -0
- package/dist/visuallyhidden.layered.css +5 -0
- package/package.json +26 -1
|
@@ -147,6 +147,14 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
|
|
|
147
147
|
// (documented React behaviour), so a plain onFocus/onBlur pair on this
|
|
148
148
|
// same wrapper sees the portal's contents correctly.
|
|
149
149
|
const mentionPickerHasFocusRef = React.useRef(false);
|
|
150
|
+
// A caret position `insertMention` just established, to be re-asserted
|
|
151
|
+
// the next time `body` regains focus (#514 fixed the picker stealing
|
|
152
|
+
// focus on OPEN; this is the same "caret resets to offset 0" mechanism
|
|
153
|
+
// on SELECT — see insertMention's own comment for why one synchronous
|
|
154
|
+
// `sel.addRange` isn't enough). One-shot: consumed and cleared by the
|
|
155
|
+
// very next focus or input, whichever comes first, so a stale pointer
|
|
156
|
+
// can never later hijack an unrelated focus.
|
|
157
|
+
const pendingCaretRef = React.useRef(null);
|
|
150
158
|
const syncEmpty = (el) => {
|
|
151
159
|
if (el)
|
|
152
160
|
el.toggleAttribute("data-empty", (el.textContent ?? "") === "");
|
|
@@ -458,6 +466,20 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
|
|
|
458
466
|
after.collapse(true);
|
|
459
467
|
sel.removeAllRanges();
|
|
460
468
|
sel.addRange(after);
|
|
469
|
+
// `select`ing a mention is usually the LAST thing the consumer's
|
|
470
|
+
// picker does before it unmounts (ActorPicker-shaped consumers call
|
|
471
|
+
// `close` right after `select`, same as this file's own doc example).
|
|
472
|
+
// A picker built on a dialog primitive (crisp's own `Command`
|
|
473
|
+
// included) commonly restores focus to whatever had it before the
|
|
474
|
+
// dialog opened as part of ITS OWN unmount cleanup — a plain
|
|
475
|
+
// `.focus()` with no Range of its own, which collapses a
|
|
476
|
+
// contentEditable's caret to offset 0. That cleanup runs after this
|
|
477
|
+
// function returns (the picker only unmounts once `closeMention`
|
|
478
|
+
// below flips `mention` to null and React commits it), so the
|
|
479
|
+
// Selection set two lines up can already be correct here and still
|
|
480
|
+
// get clobbered a tick later. Re-assert it the next time `body`
|
|
481
|
+
// actually regains focus, whenever that turns out to be.
|
|
482
|
+
pendingCaretRef.current = { node: space, offset: 1 };
|
|
461
483
|
closeMention();
|
|
462
484
|
syncEmpty(bodyRef.current);
|
|
463
485
|
emit();
|
|
@@ -522,10 +544,31 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
|
|
|
522
544
|
} })), _jsx("div", { ref: bodyRef, className: "crisp-rte-body", contentEditable: !readOnly, suppressContentEditableWarning: true, role: "textbox", tabIndex: readOnly ? -1 : 0, "aria-multiline": "true", "aria-label": bodyLabel, "aria-readonly": readOnly || undefined, "data-placeholder": placeholder, onInput: readOnly
|
|
523
545
|
? undefined
|
|
524
546
|
: () => {
|
|
547
|
+
// A real keystroke landing here means `body` already has
|
|
548
|
+
// focus on its own terms — any caret a prior mention
|
|
549
|
+
// selection was waiting to reassert (see insertMention) is
|
|
550
|
+
// moot now and must not later jump the caret backward on
|
|
551
|
+
// some unrelated future focus.
|
|
552
|
+
pendingCaretRef.current = null;
|
|
525
553
|
syncEmpty(bodyRef.current);
|
|
526
554
|
updateSlashTrigger();
|
|
527
555
|
updateMention();
|
|
528
556
|
emit();
|
|
557
|
+
}, onFocus: readOnly
|
|
558
|
+
? undefined
|
|
559
|
+
: () => {
|
|
560
|
+
const pending = pendingCaretRef.current;
|
|
561
|
+
pendingCaretRef.current = null;
|
|
562
|
+
if (!pending?.node.isConnected)
|
|
563
|
+
return;
|
|
564
|
+
const sel = window.getSelection();
|
|
565
|
+
if (!sel)
|
|
566
|
+
return;
|
|
567
|
+
const range = document.createRange();
|
|
568
|
+
range.setStart(pending.node, pending.offset);
|
|
569
|
+
range.collapse(true);
|
|
570
|
+
sel.removeAllRanges();
|
|
571
|
+
sel.addRange(range);
|
|
529
572
|
}, onMouseUp: readOnly
|
|
530
573
|
? undefined
|
|
531
574
|
: () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCrispMessage, useCrispMessages } from "../../core/index.js";
|
|
2
3
|
import { tasklistRecipe } from "./tasklist.recipe.js";
|
|
3
4
|
function Row({ task, onToggle, completeLabel, incompleteLabel, }) {
|
|
4
5
|
return (_jsxs("div", { className: "crisp-tasklist-row", "data-done": task.done ? "" : undefined, children: [_jsx("input", { type: "checkbox", className: "crisp-tasklist-check", checked: !!task.done, "aria-label": task.done ? incompleteLabel : completeLabel, onChange: () => onToggle?.(task.id, !task.done) }), _jsx("span", { className: "crisp-tasklist-title", children: task.title }), (task.due != null || task.meta != null) && (_jsxs("span", { className: "crisp-tasklist-meta", children: [task.due != null && (_jsx("span", { className: "crisp-tasklist-due", "data-overdue": task.overdue ? "" : undefined, children: task.due })), task.meta] }))] }));
|
|
@@ -8,9 +9,15 @@ function Row({ task, onToggle, completeLabel, incompleteLabel, }) {
|
|
|
8
9
|
* count, then rows of a round complete-checkbox + title + trailing meta.
|
|
9
10
|
* Completed rows strike through; overdue dates go red.
|
|
10
11
|
*/
|
|
11
|
-
export function TaskList({ tasks, groups, onToggle, emptyLabel
|
|
12
|
+
export function TaskList({ tasks, groups, onToggle, emptyLabel: emptyLabelProp, className, completeLabel: completeLabelProp, incompleteLabel: incompleteLabelProp, ...rest }) {
|
|
12
13
|
const resolved = groups ?? (tasks?.length ? [{ label: null, tasks }] : []);
|
|
13
14
|
const total = resolved.reduce((s, g) => s + g.tasks.length, 0);
|
|
15
|
+
// crisp/#471: `emptyLabel` is typed `React.ReactNode`, so it resolves by
|
|
16
|
+
// hand rather than through `useCrispMessage`, which is string-only.
|
|
17
|
+
const crispMessages = useCrispMessages();
|
|
18
|
+
const emptyLabel = emptyLabelProp ?? crispMessages["No tasks"] ?? "No tasks";
|
|
19
|
+
const completeLabel = useCrispMessage(completeLabelProp, "Mark complete");
|
|
20
|
+
const incompleteLabel = useCrispMessage(incompleteLabelProp, "Mark incomplete");
|
|
14
21
|
return (_jsx("div", { className: tasklistRecipe({ className }), ...rest, children: total === 0 ? (_jsx("div", { className: "crisp-tasklist-empty", children: emptyLabel })) : (resolved.map((g, gi) => (_jsxs("div", { children: [g.label != null && (_jsxs("div", { className: "crisp-tasklist-group", children: [g.label, _jsx("span", { className: "crisp-tasklist-group-count", children: g.tasks.length })] })), g.tasks.map((t) => (_jsx(Row, { task: t, onToggle: onToggle, completeLabel: completeLabel, incompleteLabel: incompleteLabel }, t.id)))] }, gi)))) }));
|
|
15
22
|
}
|
|
16
23
|
TaskList.displayName = "TaskList";
|
|
@@ -97,4 +97,18 @@ export const palette = {
|
|
|
97
97
|
// as-is per build/spec-check.mjs's own note), this token exists only for
|
|
98
98
|
// Badge's soft-neutral text, so darkening it doesn't move any other surface.
|
|
99
99
|
softNeutralText: { light: "#686870", dark: "#9a9aa2" },
|
|
100
|
+
// Badge's purple/role-pill tone (crisp#506) -- measured live off Attio's
|
|
101
|
+
// own role pill ("Admin", app.attio.com/<ws>/settings/workspace/members):
|
|
102
|
+
// background rgb(245,240,255), text rgb(98,56,181). The ring re-uses a
|
|
103
|
+
// second, independent measurement from the same session (a company's
|
|
104
|
+
// Categories tag pill sharing the identical background, "Broadcasting",
|
|
105
|
+
// app.attio.com/<ws>/companies) at rgb(232,221,254) -- same hue family,
|
|
106
|
+
// cross-validated rather than guessed. Dark mode was NOT measured (no
|
|
107
|
+
// live Attio session was reachable when this tone was added) -- these
|
|
108
|
+
// three light values are reused as-is for dark rather than an invented
|
|
109
|
+
// "brightened" guess (Lesson: don't add unmeasured "robustness"). Needs a
|
|
110
|
+
// real dark-mode measurement before this note can be removed.
|
|
111
|
+
purpleTint: { light: "#f5f0ff", dark: "#f5f0ff" },
|
|
112
|
+
softPurpleText: { light: "#6238b5", dark: "#6238b5" },
|
|
113
|
+
purpleRing: { light: "#e8ddfe", dark: "#e8ddfe" },
|
|
100
114
|
};
|
|
@@ -60,4 +60,9 @@ export const semantic = {
|
|
|
60
60
|
fgWarningSoft: "softWarningText",
|
|
61
61
|
fgDangerSoft: "softDangerText",
|
|
62
62
|
fgNeutralSoft: "softNeutralText",
|
|
63
|
+
/** Badge's purple/role-pill tone (crisp#506) — see palette.ts's comment
|
|
64
|
+
* on `purpleTint`/`softPurpleText`/`purpleRing` for the measurement. */
|
|
65
|
+
bgPurpleSoft: "purpleTint",
|
|
66
|
+
fgPurpleSoft: "softPurpleText",
|
|
67
|
+
ringPurpleSoft: "purpleRing",
|
|
63
68
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
.crisp-headerbar {
|
|
2
|
+
box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-2); height: var(--crisp-size-header); flex-shrink: 0; padding: 0 var(--crisp-space-3); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
3
|
+
&[data-bordered] { border-bottom: 1px solid var(--crisp-border-subtle); }
|
|
4
|
+
& .crisp-headerbar-leading { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; min-width: 0; }
|
|
5
|
+
& .crisp-headerbar-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--crisp-space-2); overflow: hidden; }
|
|
6
|
+
& .crisp-headerbar-trailing { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; }
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
@layer crisp-components {
|
|
2
|
+
.crisp-headerbar {
|
|
3
|
+
box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-2); height: var(--crisp-size-header); flex-shrink: 0; padding: 0 var(--crisp-space-3); font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
4
|
+
&[data-bordered] { border-bottom: 1px solid var(--crisp-border-subtle); }
|
|
5
|
+
& .crisp-headerbar-leading { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; min-width: 0; }
|
|
6
|
+
& .crisp-headerbar-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--crisp-space-2); overflow: hidden; }
|
|
7
|
+
& .crisp-headerbar-trailing { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; }
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/native/tokens.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// The same tokens the CSS custom properties carry, resolved per theme, with
|
|
3
3
|
// dimensions as numbers for React Native and Lynx style objects.
|
|
4
4
|
export type Theme = "light" | "dark";
|
|
5
|
-
export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "borderDefault" | "borderSubtle" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft", string>;
|
|
5
|
+
export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "borderDefault" | "borderSubtle" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft" | "bgPurpleSoft" | "fgPurpleSoft" | "ringPurpleSoft", string>;
|
|
6
6
|
export declare const tokens: {
|
|
7
7
|
color: Record<Theme, ColorTokens>;
|
|
8
8
|
palette: Record<Theme, Record<string, string>>;
|
package/dist/native/tokens.js
CHANGED
|
@@ -49,7 +49,10 @@ export const tokens = {
|
|
|
49
49
|
"fgSuccessSoft": "#186b3d",
|
|
50
50
|
"fgWarningSoft": "#80520d",
|
|
51
51
|
"fgDangerSoft": "#a33539",
|
|
52
|
-
"fgNeutralSoft": "#686870"
|
|
52
|
+
"fgNeutralSoft": "#686870",
|
|
53
|
+
"bgPurpleSoft": "#f5f0ff",
|
|
54
|
+
"fgPurpleSoft": "#6238b5",
|
|
55
|
+
"ringPurpleSoft": "#e8ddfe"
|
|
53
56
|
},
|
|
54
57
|
"dark": {
|
|
55
58
|
"bgBrandSolid": "#266df0",
|
|
@@ -97,7 +100,10 @@ export const tokens = {
|
|
|
97
100
|
"fgSuccessSoft": "#2ea968",
|
|
98
101
|
"fgWarningSoft": "#e0982a",
|
|
99
102
|
"fgDangerSoft": "#ff5a5f",
|
|
100
|
-
"fgNeutralSoft": "#9a9aa2"
|
|
103
|
+
"fgNeutralSoft": "#9a9aa2",
|
|
104
|
+
"bgPurpleSoft": "#f5f0ff",
|
|
105
|
+
"fgPurpleSoft": "#6238b5",
|
|
106
|
+
"ringPurpleSoft": "#e8ddfe"
|
|
101
107
|
}
|
|
102
108
|
},
|
|
103
109
|
"palette": {
|
|
@@ -146,7 +152,10 @@ export const tokens = {
|
|
|
146
152
|
"softSuccessText": "#186b3d",
|
|
147
153
|
"softWarningText": "#80520d",
|
|
148
154
|
"softDangerText": "#a33539",
|
|
149
|
-
"softNeutralText": "#686870"
|
|
155
|
+
"softNeutralText": "#686870",
|
|
156
|
+
"purpleTint": "#f5f0ff",
|
|
157
|
+
"softPurpleText": "#6238b5",
|
|
158
|
+
"purpleRing": "#e8ddfe"
|
|
150
159
|
},
|
|
151
160
|
"dark": {
|
|
152
161
|
"gray00": "#0a0a0a",
|
|
@@ -193,7 +202,10 @@ export const tokens = {
|
|
|
193
202
|
"softSuccessText": "#2ea968",
|
|
194
203
|
"softWarningText": "#e0982a",
|
|
195
204
|
"softDangerText": "#ff5a5f",
|
|
196
|
-
"softNeutralText": "#9a9aa2"
|
|
205
|
+
"softNeutralText": "#9a9aa2",
|
|
206
|
+
"purpleTint": "#f5f0ff",
|
|
207
|
+
"softPurpleText": "#6238b5",
|
|
208
|
+
"purpleRing": "#e8ddfe"
|
|
197
209
|
}
|
|
198
210
|
},
|
|
199
211
|
"space": {
|
package/dist/recordpage.css
CHANGED
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
& .crisp-recordpage-detailsearch { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: var(--crisp-size-chip); flex-shrink: 0; padding: 0; margin: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
48
48
|
& .crisp-recordpage-detailsearch:hover, & .crisp-recordpage-detailsearch[data-active] { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
|
|
49
49
|
& .crisp-recordpage-noresults { padding: var(--crisp-space-1-5) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
50
|
+
/* A caller-supplied `sections` grouping (#528) is not an Attio-measured
|
|
51
|
+
shape (its own Details column is flat) -- this heading reuses
|
|
52
|
+
.crisp-recordpage-section-title's own type treatment verbatim, just spaced from the group above it. */
|
|
53
|
+
& .crisp-recordpage-fieldsection-title { margin-top: var(--crisp-space-3); }
|
|
50
54
|
& .crisp-recordpage-morevalues { display: inline-flex; align-items: center; padding: 0; margin: 0; border: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); cursor: pointer; }
|
|
51
55
|
& .crisp-recordpage-morevalues:hover { color: var(--crisp-fg-primary); }
|
|
52
56
|
& .crisp-recordpage-viewall { flex-basis: 100%; margin-top: var(--crisp-space-0-5); border: none; background: none; padding: 0; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-family: inherit; font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-regular); cursor: pointer; text-align: start; }
|
|
@@ -61,31 +65,25 @@
|
|
|
61
65
|
& .crisp-recordpage-field-label-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
62
66
|
& .crisp-recordpage-field-value { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); padding-top: 2px; line-height: 20px; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
|
|
63
67
|
& .crisp-recordpage-field-value[data-editable] { cursor: pointer; }
|
|
64
|
-
/* the click-to-edit trigger is a real <button> (keyboard-operable),
|
|
65
|
-
|
|
66
|
-
own -- FieldValue can render several chips (tags), which still need
|
|
67
|
-
to wrap the way they did before this had a button around them at
|
|
68
|
-
all. It wraps only FieldValue, never the error slot beside it (see
|
|
69
|
-
the component's own comment) -- NOT `display: contents`, which drops a button's accessible semantics in some engines. */
|
|
68
|
+
/* the click-to-edit trigger is a real <button> (keyboard-operable), reset back to plain inline text and made a flex-wrap container of its own -- FieldValue can render several chips (tags), which still need to wrap the way they did before this had a button around them at all. It wraps only FieldValue, never the error slot beside it (see the component's own comment) -- NOT `display: contents`, which
|
|
69
|
+
drops a button's accessible semantics in some engines. */
|
|
70
70
|
& .crisp-recordpage-edit-trigger { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); margin: 0; padding: 0; border: none; background: none; text-align: start; font: inherit; color: inherit; cursor: pointer; }
|
|
71
71
|
& .crisp-recordpage-empty { color: var(--crisp-fg-muted); }
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
above already carries the "this is interactive" affordance; the
|
|
76
|
-
|
|
77
|
-
`bgDanger`), per #385/#430. */
|
|
72
|
+
/* #431: measured live on app.attio.com's record page Details column --
|
|
73
|
+
clicking a field's value opens a plain, borderless field exactly filling the value's own box (no border, no background, no
|
|
74
|
+
box-shadow), same font as the value it replaces (hence `font:
|
|
75
|
+
inherit`, not a restated size/weight). A field row's own hover tint above already carries the "this is interactive" affordance; the pending/error treatment isn't Attio-measured -- it reuses the same
|
|
76
|
+
tokens DataTable's identical states already use (opacity 0.5, `bgDanger`), per #385/#430. */
|
|
78
77
|
& .crisp-recordpage-edit { display: block; width: 100%; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; box-shadow: none; outline: none; font: inherit; color: inherit; }
|
|
79
|
-
/* not Attio-measured (its own field shows no focus ring either, caret
|
|
80
|
-
|
|
81
|
-
other crisp field already shows on focus (DataTable's identical .crisp-datatable-edit:focus, e.g.). */
|
|
78
|
+
/* not Attio-measured (its own field shows no focus ring either, caret only) -- kept for real keyboard users, the same brand ring every other crisp field already shows on focus (DataTable's identical
|
|
79
|
+
.crisp-datatable-edit:focus, e.g.). */
|
|
82
80
|
& .crisp-recordpage-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
83
81
|
& .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
84
82
|
& .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
|
|
85
83
|
& .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
|
-
|
|
88
|
-
classnames verbatim here would resolve to nothing. */
|
|
84
|
+
/* #457: an `editor: "select"` field's at-rest chip -- same shape as RecordsTable's own `.crisp-recordstable-status[-dot]` (already
|
|
85
|
+
measured against Attio there), but its own classes: that pair is
|
|
86
|
+
scoped under `.crisp-recordstable`'s own recipe root, so reusing the classnames verbatim here would resolve to nothing. */
|
|
89
87
|
& .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
|
|
90
88
|
& .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
|
|
91
89
|
& .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
|
|
@@ -93,23 +91,18 @@
|
|
|
93
91
|
& .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
|
|
94
92
|
& .crisp-recordpage-link:hover { text-decoration: underline; }
|
|
95
93
|
& .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
96
|
-
|
|
97
94
|
& .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
|
-
|
|
99
95
|
& .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
|
|
100
96
|
& .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
|
|
101
97
|
& .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); }
|
|
102
98
|
& .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
|
|
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. */
|
|
99
|
+
/* 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. */
|
|
106
100
|
& .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
|
|
107
101
|
& .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
|
|
108
102
|
& .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
|
|
109
103
|
& .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); }
|
|
110
104
|
& .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); }
|
|
111
105
|
& .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
|
|
112
|
-
|
|
113
106
|
& .crisp-recordpage-feed { display: flex; flex-direction: column; }
|
|
114
107
|
& .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
|
|
115
108
|
& .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); }
|
|
@@ -118,7 +111,6 @@
|
|
|
118
111
|
& .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
|
|
119
112
|
& .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; }
|
|
120
113
|
& .crisp-recordpage-feed-more:hover { text-decoration: underline; }
|
|
121
|
-
|
|
122
114
|
& .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
|
|
123
115
|
& .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
|
|
124
116
|
& .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); }
|
|
@@ -126,7 +118,6 @@
|
|
|
126
118
|
& .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; }
|
|
127
119
|
& .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
|
|
128
120
|
& .crisp-recordpage-addbtn svg { flex-shrink: 0; }
|
|
129
|
-
|
|
130
121
|
& .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); }
|
|
131
122
|
& .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); }
|
|
132
123
|
& .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
|
|
@@ -136,23 +127,18 @@
|
|
|
136
127
|
& .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
|
|
137
128
|
& .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
|
|
138
129
|
& .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
139
|
-
|
|
140
130
|
& .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); }
|
|
141
131
|
& .crisp-recordpage-note-body { flex: 1; min-width: 0; }
|
|
142
132
|
& .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
|
|
143
133
|
& .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
|
|
144
134
|
& .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
|
|
145
135
|
& .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
|
|
146
|
-
|
|
147
136
|
& .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); }
|
|
148
137
|
& .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
|
|
149
138
|
& .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
|
|
150
|
-
|
|
151
139
|
& .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; }
|
|
152
140
|
& .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
|
|
153
|
-
|
|
154
141
|
& .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
|
|
155
|
-
|
|
156
142
|
& .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); }
|
|
157
143
|
& .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)); }
|
|
158
144
|
& .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)); }
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
& .crisp-recordpage-detailsearch { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: var(--crisp-size-chip); flex-shrink: 0; padding: 0; margin: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
49
49
|
& .crisp-recordpage-detailsearch:hover, & .crisp-recordpage-detailsearch[data-active] { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
|
|
50
50
|
& .crisp-recordpage-noresults { padding: var(--crisp-space-1-5) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
51
|
+
/* A caller-supplied `sections` grouping (#528) is not an Attio-measured
|
|
52
|
+
shape (its own Details column is flat) -- this heading reuses
|
|
53
|
+
.crisp-recordpage-section-title's own type treatment verbatim, just spaced from the group above it. */
|
|
54
|
+
& .crisp-recordpage-fieldsection-title { margin-top: var(--crisp-space-3); }
|
|
51
55
|
& .crisp-recordpage-morevalues { display: inline-flex; align-items: center; padding: 0; margin: 0; border: none; background: none; font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); cursor: pointer; }
|
|
52
56
|
& .crisp-recordpage-morevalues:hover { color: var(--crisp-fg-primary); }
|
|
53
57
|
& .crisp-recordpage-viewall { flex-basis: 100%; margin-top: var(--crisp-space-0-5); border: none; background: none; padding: 0; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-family: inherit; font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-regular); cursor: pointer; text-align: start; }
|
|
@@ -62,31 +66,25 @@
|
|
|
62
66
|
& .crisp-recordpage-field-label-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
63
67
|
& .crisp-recordpage-field-value { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); padding-top: 2px; line-height: 20px; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
|
|
64
68
|
& .crisp-recordpage-field-value[data-editable] { cursor: pointer; }
|
|
65
|
-
/* the click-to-edit trigger is a real <button> (keyboard-operable),
|
|
66
|
-
|
|
67
|
-
own -- FieldValue can render several chips (tags), which still need
|
|
68
|
-
to wrap the way they did before this had a button around them at
|
|
69
|
-
all. It wraps only FieldValue, never the error slot beside it (see
|
|
70
|
-
the component's own comment) -- NOT `display: contents`, which drops a button's accessible semantics in some engines. */
|
|
69
|
+
/* the click-to-edit trigger is a real <button> (keyboard-operable), reset back to plain inline text and made a flex-wrap container of its own -- FieldValue can render several chips (tags), which still need to wrap the way they did before this had a button around them at all. It wraps only FieldValue, never the error slot beside it (see the component's own comment) -- NOT `display: contents`, which
|
|
70
|
+
drops a button's accessible semantics in some engines. */
|
|
71
71
|
& .crisp-recordpage-edit-trigger { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--crisp-space-1); margin: 0; padding: 0; border: none; background: none; text-align: start; font: inherit; color: inherit; cursor: pointer; }
|
|
72
72
|
& .crisp-recordpage-empty { color: var(--crisp-fg-muted); }
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
above already carries the "this is interactive" affordance; the
|
|
77
|
-
|
|
78
|
-
`bgDanger`), per #385/#430. */
|
|
73
|
+
/* #431: measured live on app.attio.com's record page Details column --
|
|
74
|
+
clicking a field's value opens a plain, borderless field exactly filling the value's own box (no border, no background, no
|
|
75
|
+
box-shadow), same font as the value it replaces (hence `font:
|
|
76
|
+
inherit`, not a restated size/weight). A field row's own hover tint above already carries the "this is interactive" affordance; the pending/error treatment isn't Attio-measured -- it reuses the same
|
|
77
|
+
tokens DataTable's identical states already use (opacity 0.5, `bgDanger`), per #385/#430. */
|
|
79
78
|
& .crisp-recordpage-edit { display: block; width: 100%; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; box-shadow: none; outline: none; font: inherit; color: inherit; }
|
|
80
|
-
/* not Attio-measured (its own field shows no focus ring either, caret
|
|
81
|
-
|
|
82
|
-
other crisp field already shows on focus (DataTable's identical .crisp-datatable-edit:focus, e.g.). */
|
|
79
|
+
/* not Attio-measured (its own field shows no focus ring either, caret only) -- kept for real keyboard users, the same brand ring every other crisp field already shows on focus (DataTable's identical
|
|
80
|
+
.crisp-datatable-edit:focus, e.g.). */
|
|
83
81
|
& .crisp-recordpage-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
84
82
|
& .crisp-recordpage-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
85
83
|
& .crisp-recordpage-edit-pending { flex-shrink: 0; color: var(--crisp-fg-muted); }
|
|
86
84
|
& .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
|
-
|
|
89
|
-
classnames verbatim here would resolve to nothing. */
|
|
85
|
+
/* #457: an `editor: "select"` field's at-rest chip -- same shape as RecordsTable's own `.crisp-recordstable-status[-dot]` (already
|
|
86
|
+
measured against Attio there), but its own classes: that pair is
|
|
87
|
+
scoped under `.crisp-recordstable`'s own recipe root, so reusing the classnames verbatim here would resolve to nothing. */
|
|
90
88
|
& .crisp-recordpage-status { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; }
|
|
91
89
|
& .crisp-recordpage-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: var(--crisp-radius-full); }
|
|
92
90
|
& .crisp-recordpage-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; }
|
|
@@ -94,23 +92,18 @@
|
|
|
94
92
|
& .crisp-recordpage-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
|
|
95
93
|
& .crisp-recordpage-link:hover { text-decoration: underline; }
|
|
96
94
|
& .crisp-recordpage-emptystate { padding: var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
97
|
-
|
|
98
95
|
& .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
|
-
|
|
100
96
|
& .crisp-recordpage-panel { padding-top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
|
|
101
97
|
& .crisp-recordpage-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); background: var(--crisp-bg-surface); overflow: hidden; }
|
|
102
98
|
& .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); }
|
|
103
99
|
& .crisp-recordpage-hl-card { border: 1px solid var(--crisp-border-subtle); border-radius: var(--crisp-radius-2xl); overflow: hidden; }
|
|
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. */
|
|
100
|
+
/* 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. */
|
|
107
101
|
& .crisp-recordpage-hl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1px; background: var(--crisp-border-subtle); }
|
|
108
102
|
& .crisp-recordpage-hl-cell { position: relative; min-height: 84px; padding: var(--crisp-space-2-5) var(--crisp-space-3); background: var(--crisp-bg-surface); }
|
|
109
103
|
& .crisp-recordpage-hl-icon { position: absolute; top: var(--crisp-space-2-5); inset-inline-end: var(--crisp-space-3); color: var(--crisp-fg-muted); }
|
|
110
104
|
& .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); }
|
|
111
105
|
& .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); }
|
|
112
106
|
& .crisp-recordpage-hl-value--empty { color: var(--crisp-fg-muted); font-weight: var(--crisp-weight-regular); }
|
|
113
|
-
|
|
114
107
|
& .crisp-recordpage-feed { display: flex; flex-direction: column; }
|
|
115
108
|
& .crisp-recordpage-feed-item { display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); padding: var(--crisp-space-2) 0; }
|
|
116
109
|
& .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); }
|
|
@@ -119,7 +112,6 @@
|
|
|
119
112
|
& .crisp-recordpage-feed-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); margin-top: 2px; }
|
|
120
113
|
& .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; }
|
|
121
114
|
& .crisp-recordpage-feed-more:hover { text-decoration: underline; }
|
|
122
|
-
|
|
123
115
|
& .crisp-recordpage-section-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); padding: var(--crisp-space-1); }
|
|
124
116
|
& .crisp-recordpage-section-head .crisp-recordpage-section-title { padding: 0; }
|
|
125
117
|
& .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); }
|
|
@@ -127,7 +119,6 @@
|
|
|
127
119
|
& .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; }
|
|
128
120
|
& .crisp-recordpage-addbtn:hover { background: var(--crisp-bg-raised); }
|
|
129
121
|
& .crisp-recordpage-addbtn svg { flex-shrink: 0; }
|
|
130
|
-
|
|
131
122
|
& .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); }
|
|
132
123
|
& .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); }
|
|
133
124
|
& .crisp-recordpage-composer textarea:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); border-radius: var(--crisp-radius-md); }
|
|
@@ -137,23 +128,18 @@
|
|
|
137
128
|
& .crisp-recordpage-btn:hover { background: var(--crisp-bg-raised); }
|
|
138
129
|
& .crisp-recordpage-btn--primary { box-shadow: none; background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); }
|
|
139
130
|
& .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
140
|
-
|
|
141
131
|
& .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); }
|
|
142
132
|
& .crisp-recordpage-note-body { flex: 1; min-width: 0; }
|
|
143
133
|
& .crisp-recordpage-note-head { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-bottom: var(--crisp-space-0-5); }
|
|
144
134
|
& .crisp-recordpage-note-author { font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); }
|
|
145
135
|
& .crisp-recordpage-note-time { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
|
|
146
136
|
& .crisp-recordpage-note-text { font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
|
|
147
|
-
|
|
148
137
|
& .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); }
|
|
149
138
|
& .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); padding-top: 1px; }
|
|
150
139
|
& .crisp-recordpage-task[data-done] .crisp-recordpage-task-text { text-decoration: line-through; color: var(--crisp-fg-muted); }
|
|
151
|
-
|
|
152
140
|
& .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; }
|
|
153
141
|
& .crisp-recordpage-emptycard svg { color: var(--crisp-fg-muted); opacity: 0.7; }
|
|
154
|
-
|
|
155
142
|
& .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
|
|
156
|
-
|
|
157
143
|
& .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); }
|
|
158
144
|
& .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)); }
|
|
159
145
|
& .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)); }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
.crisp-skiplink {
|
|
2
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; text-decoration: none; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary);
|
|
3
|
+
&:focus { position: fixed; top: var(--crisp-space-3); inset-inline-start: var(--crisp-space-3); z-index: 90; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; white-space: normal; padding: var(--crisp-space-2) var(--crisp-space-3); border-radius: var(--crisp-radius-md); 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); }
|
|
4
|
+
&:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
5
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
@layer crisp-components {
|
|
2
|
+
.crisp-skiplink {
|
|
3
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; text-decoration: none; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary);
|
|
4
|
+
&:focus { position: fixed; top: var(--crisp-space-3); inset-inline-start: var(--crisp-space-3); z-index: 90; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; white-space: normal; padding: var(--crisp-space-2) var(--crisp-space-3); border-radius: var(--crisp-radius-md); 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); }
|
|
5
|
+
&:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
|
|
6
|
+
}
|
|
7
|
+
}
|