@crisp-ui-kit/crisp 0.12.0 → 0.13.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/board.css CHANGED
@@ -14,6 +14,13 @@
14
14
  & .board-card { display: flex; flex-direction: column; gap: var(--space-2); box-sizing: border-box; padding: var(--space-2-5) var(--space-3); border-radius: var(--radius-lg); background: var(--bg-surface); box-shadow: inset 0 0 0 1px var(--border-subtle), 0 1px 2px rgba(0,0,0,0.04); cursor: grab; }
15
15
  & .board-card:active { cursor: grabbing; }
16
16
  & .board-card[data-dragging] { opacity: 0.4; }
17
+ & .board-card:focus { outline: none; }
18
+ & .board-card:focus-visible { outline: 2px solid var(--bg-brand-solid); outline-offset: 1px; }
19
+ /* announced, never drawn */
20
+ & .board-live {
21
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
22
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
23
+ }
17
24
  }
18
25
 
19
26
  @media (prefers-reduced-motion: reduce) {
@@ -62,6 +62,13 @@ const asText = (v) => {
62
62
  return "";
63
63
  if (Array.isArray(v))
64
64
  return v.join(", ");
65
+ // Before the generic object branch, which would look for `.name`, find none
66
+ // and return "" — turning every Date into the empty string (#185). The ISO
67
+ // date is stable and locale-independent, so `contains "2026-06"` works; it
68
+ // will not match "16/06/2026", and a date-aware operator set is Attio's
69
+ // date-picker UI, which has to be measured before it can be built.
70
+ if (v instanceof Date)
71
+ return Number.isNaN(v.getTime()) ? "" : v.toISOString().slice(0, 10);
65
72
  if (typeof v === "object") {
66
73
  const name = v.name;
67
74
  return typeof name === "string" ? name : "";
@@ -104,7 +111,12 @@ function applySorts(rows, sorts, get = (row, key) => row[key]) {
104
111
  const av = get(a, s.key);
105
112
  const bv = get(b, s.key);
106
113
  let d = 0;
107
- if (typeof av === "number" && typeof bv === "number")
114
+ // Dates compare by instant, the way two numbers do. Falling through to
115
+ // text here is what made a date column sort identically ascending and
116
+ // descending — every comparison was "" against "" (#185).
117
+ if (av instanceof Date && bv instanceof Date)
118
+ d = av.getTime() - bv.getTime();
119
+ else if (typeof av === "number" && typeof bv === "number")
108
120
  d = av - bv;
109
121
  else
110
122
  d = asText(av).localeCompare(asText(bv));
@@ -43,36 +43,81 @@ const board_recipe_1 = require("./board.recipe");
43
43
  * (the parent owns the cards, so the move is a controlled state update).
44
44
  * Measured from Attio's Deals pipeline.
45
45
  */
46
- function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel = "New", className, ...rest }) {
46
+ function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel = "New", getCardLabel, announceMove = (column, card) => card ? `${card} moved to ${column}` : `Moved to ${column}`, className, ...rest }) {
47
47
  const [dragId, setDragId] = React.useState(null);
48
48
  const [overCol, setOverCol] = React.useState(null);
49
+ const [announcement, setAnnouncement] = React.useState("");
50
+ // the card a keyboard move just sent to another column; React unmounts and
51
+ // remounts it under the new parent, so focus has to be put back by hand
52
+ const [refocusId, setRefocusId] = React.useState(null);
53
+ const rootRef = React.useRef(null);
49
54
  const move = (cardId, toColumn) => {
50
55
  const card = cards.find((c) => c.id === cardId);
51
56
  if (!card || card.columnId === toColumn)
52
57
  return;
53
58
  onCardsChange?.(cards.map((c) => (c.id === cardId ? { ...c, columnId: toColumn } : c)));
54
59
  };
55
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, board_recipe_1.boardRecipe)({ className }), ...rest, children: columns.map((col) => {
56
- const colCards = cards.filter((c) => c.columnId === col.id);
57
- return ((0, jsx_runtime_1.jsxs)("div", { className: "board-col", "data-over": overCol === col.id ? "" : undefined, onDragOver: (e) => {
58
- if (!dragId)
59
- return;
60
- e.preventDefault();
61
- setOverCol(col.id);
62
- }, onDragLeave: (e) => {
63
- if (!e.currentTarget.contains(e.relatedTarget)) {
64
- setOverCol((v) => (v === col.id ? null : v));
65
- }
66
- }, onDrop: (e) => {
67
- e.preventDefault();
68
- if (dragId)
69
- move(dragId, col.id);
70
- setDragId(null);
71
- setOverCol(null);
72
- }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "board-col-header", children: [col.accent && ((0, jsx_runtime_1.jsx)("span", { className: "board-col-dot", style: { background: col.accent } })), (0, jsx_runtime_1.jsx)("span", { className: "board-col-title", children: col.title }), (0, jsx_runtime_1.jsx)("span", { className: "board-col-count", children: colCards.length }), onNewCard && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "board-col-add", "aria-label": `Add to ${col.title}`, onClick: () => onNewCard(col.id), children: "+" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "board-col-body", children: [onNewCard && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "board-newcard", onClick: () => onNewCard(col.id), children: ["+ ", newCardLabel] })), colCards.map((card) => ((0, jsx_runtime_1.jsx)("div", { className: "board-card", draggable: true, "data-dragging": dragId === card.id ? "" : undefined, onDragStart: () => setDragId(card.id), onDragEnd: () => {
73
- setDragId(null);
74
- setOverCol(null);
75
- }, children: renderCard(card) }, card.id)))] })] }, col.id));
76
- }) }));
60
+ React.useEffect(() => {
61
+ if (!refocusId)
62
+ return;
63
+ rootRef.current
64
+ ?.querySelector(`[data-card-id="${CSS.escape(refocusId)}"]`)
65
+ ?.focus();
66
+ setRefocusId(null);
67
+ }, [refocusId]);
68
+ /**
69
+ * Native HTML5 drag-and-drop is pointer-only by specification, so the board's
70
+ * primary action needs its own key model or it has none at all.
71
+ *
72
+ * Ctrl/Cmd + Left/Right rather than plain arrows: a screen reader owns the
73
+ * bare arrow keys in browse mode, and a board scrolls horizontally, so plain
74
+ * arrows on a focused card would fight both.
75
+ */
76
+ const moveByKey = (card, delta) => {
77
+ const from = columns.findIndex((c) => c.id === card.columnId);
78
+ const to = columns[from + delta];
79
+ if (from < 0 || !to)
80
+ return false;
81
+ move(card.id, to.id);
82
+ setAnnouncement(announceMove(to.title, getCardLabel?.(card)));
83
+ setRefocusId(card.id);
84
+ return true;
85
+ };
86
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, board_recipe_1.boardRecipe)({ className }), ...rest, children: [columns.map((col) => {
87
+ const colCards = cards.filter((c) => c.columnId === col.id);
88
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "board-col", "data-over": overCol === col.id ? "" : undefined, onDragOver: (e) => {
89
+ if (!dragId)
90
+ return;
91
+ e.preventDefault();
92
+ setOverCol(col.id);
93
+ }, onDragLeave: (e) => {
94
+ if (!e.currentTarget.contains(e.relatedTarget)) {
95
+ setOverCol((v) => (v === col.id ? null : v));
96
+ }
97
+ }, onDrop: (e) => {
98
+ e.preventDefault();
99
+ if (dragId)
100
+ move(dragId, col.id);
101
+ setDragId(null);
102
+ setOverCol(null);
103
+ }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "board-col-header", children: [col.accent && ((0, jsx_runtime_1.jsx)("span", { className: "board-col-dot", style: { background: col.accent } })), (0, jsx_runtime_1.jsx)("span", { className: "board-col-title", children: col.title }), (0, jsx_runtime_1.jsx)("span", { className: "board-col-count", children: colCards.length }), onNewCard && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "board-col-add", "aria-label": `Add to ${col.title}`, onClick: () => onNewCard(col.id), children: "+" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "board-col-body", role: "list", "aria-label": col.title, children: [onNewCard && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "board-newcard", onClick: () => onNewCard(col.id), children: ["+ ", newCardLabel] })), colCards.map((card) => ((0, jsx_runtime_1.jsx)("div", { className: "board-card", role: "listitem",
104
+ // biome-ignore lint/a11y/noNoninteractiveTabindex: the rule assumes a listitem cannot be operated. This one carries the board's only keyboard path to move a card, so it has to be reachable by Tab
105
+ tabIndex: 0, "aria-label": getCardLabel?.(card), "data-card-id": card.id, draggable: true, "data-dragging": dragId === card.id ? "" : undefined, onDragStart: () => setDragId(card.id), onDragEnd: () => {
106
+ setDragId(null);
107
+ setOverCol(null);
108
+ }, onKeyDown: (e) => {
109
+ if (!(e.ctrlKey || e.metaKey))
110
+ return;
111
+ const delta = e.key === "ArrowLeft"
112
+ ? -1
113
+ : e.key === "ArrowRight"
114
+ ? 1
115
+ : 0;
116
+ // only swallow the key when the move actually happens, so
117
+ // Ctrl+Right at the last column still reaches the browser
118
+ if (delta && moveByKey(card, delta))
119
+ e.preventDefault();
120
+ }, children: renderCard(card) }, card.id)))] })] }, col.id));
121
+ }), (0, jsx_runtime_1.jsx)("div", { className: "board-live", role: "status", "aria-live": "polite", children: announcement })] }));
77
122
  }
78
123
  Board.displayName = "Board";
@@ -22,7 +22,14 @@ const config = (0, core_1.defineRecipe)({
22
22
  & .board-newcard:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 7%, transparent); color: ${tokens_1.contract.fgPrimary}; }
23
23
  & .board-card { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["2"]}; box-sizing: border-box; padding: ${tokens_1.contract.space["2.5"]} ${tokens_1.contract.space["3"]}; border-radius: ${tokens_1.contract.radius.lg}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}, 0 1px 2px rgba(0,0,0,0.04); cursor: grab; }
24
24
  & .board-card:active { cursor: grabbing; }
25
- & .board-card[data-dragging] { opacity: 0.4; }`,
25
+ & .board-card[data-dragging] { opacity: 0.4; }
26
+ & .board-card:focus { outline: none; }
27
+ & .board-card:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 1px; }
28
+ /* announced, never drawn */
29
+ & .board-live {
30
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
31
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
32
+ }`,
26
33
  variants: {},
27
34
  defaultVariants: {},
28
35
  compoundVariants: [],
@@ -24,6 +24,13 @@ const asText = (v) => {
24
24
  return "";
25
25
  if (Array.isArray(v))
26
26
  return v.join(", ");
27
+ // Before the generic object branch, which would look for `.name`, find none
28
+ // and return "" — turning every Date into the empty string (#185). The ISO
29
+ // date is stable and locale-independent, so `contains "2026-06"` works; it
30
+ // will not match "16/06/2026", and a date-aware operator set is Attio's
31
+ // date-picker UI, which has to be measured before it can be built.
32
+ if (v instanceof Date)
33
+ return Number.isNaN(v.getTime()) ? "" : v.toISOString().slice(0, 10);
27
34
  if (typeof v === "object") {
28
35
  const name = v.name;
29
36
  return typeof name === "string" ? name : "";
@@ -66,7 +73,12 @@ export function applySorts(rows, sorts, get = (row, key) => row[key]) {
66
73
  const av = get(a, s.key);
67
74
  const bv = get(b, s.key);
68
75
  let d = 0;
69
- if (typeof av === "number" && typeof bv === "number")
76
+ // Dates compare by instant, the way two numbers do. Falling through to
77
+ // text here is what made a date column sort identically ascending and
78
+ // descending — every comparison was "" against "" (#185).
79
+ if (av instanceof Date && bv instanceof Date)
80
+ d = av.getTime() - bv.getTime();
81
+ else if (typeof av === "number" && typeof bv === "number")
70
82
  d = av - bv;
71
83
  else
72
84
  d = asText(av).localeCompare(asText(bv));
@@ -7,36 +7,81 @@ import { boardRecipe } from "./board.recipe.js";
7
7
  * (the parent owns the cards, so the move is a controlled state update).
8
8
  * Measured from Attio's Deals pipeline.
9
9
  */
10
- export function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel = "New", className, ...rest }) {
10
+ export function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel = "New", getCardLabel, announceMove = (column, card) => card ? `${card} moved to ${column}` : `Moved to ${column}`, className, ...rest }) {
11
11
  const [dragId, setDragId] = React.useState(null);
12
12
  const [overCol, setOverCol] = React.useState(null);
13
+ const [announcement, setAnnouncement] = React.useState("");
14
+ // the card a keyboard move just sent to another column; React unmounts and
15
+ // remounts it under the new parent, so focus has to be put back by hand
16
+ const [refocusId, setRefocusId] = React.useState(null);
17
+ const rootRef = React.useRef(null);
13
18
  const move = (cardId, toColumn) => {
14
19
  const card = cards.find((c) => c.id === cardId);
15
20
  if (!card || card.columnId === toColumn)
16
21
  return;
17
22
  onCardsChange?.(cards.map((c) => (c.id === cardId ? { ...c, columnId: toColumn } : c)));
18
23
  };
19
- return (_jsx("div", { className: boardRecipe({ className }), ...rest, children: columns.map((col) => {
20
- const colCards = cards.filter((c) => c.columnId === col.id);
21
- return (_jsxs("div", { className: "board-col", "data-over": overCol === col.id ? "" : undefined, onDragOver: (e) => {
22
- if (!dragId)
23
- return;
24
- e.preventDefault();
25
- setOverCol(col.id);
26
- }, onDragLeave: (e) => {
27
- if (!e.currentTarget.contains(e.relatedTarget)) {
28
- setOverCol((v) => (v === col.id ? null : v));
29
- }
30
- }, onDrop: (e) => {
31
- e.preventDefault();
32
- if (dragId)
33
- move(dragId, col.id);
34
- setDragId(null);
35
- setOverCol(null);
36
- }, children: [_jsxs("div", { className: "board-col-header", children: [col.accent && (_jsx("span", { className: "board-col-dot", style: { background: col.accent } })), _jsx("span", { className: "board-col-title", children: col.title }), _jsx("span", { className: "board-col-count", children: colCards.length }), onNewCard && (_jsx("button", { type: "button", className: "board-col-add", "aria-label": `Add to ${col.title}`, onClick: () => onNewCard(col.id), children: "+" }))] }), _jsxs("div", { className: "board-col-body", children: [onNewCard && (_jsxs("button", { type: "button", className: "board-newcard", onClick: () => onNewCard(col.id), children: ["+ ", newCardLabel] })), colCards.map((card) => (_jsx("div", { className: "board-card", draggable: true, "data-dragging": dragId === card.id ? "" : undefined, onDragStart: () => setDragId(card.id), onDragEnd: () => {
37
- setDragId(null);
38
- setOverCol(null);
39
- }, children: renderCard(card) }, card.id)))] })] }, col.id));
40
- }) }));
24
+ React.useEffect(() => {
25
+ if (!refocusId)
26
+ return;
27
+ rootRef.current
28
+ ?.querySelector(`[data-card-id="${CSS.escape(refocusId)}"]`)
29
+ ?.focus();
30
+ setRefocusId(null);
31
+ }, [refocusId]);
32
+ /**
33
+ * Native HTML5 drag-and-drop is pointer-only by specification, so the board's
34
+ * primary action needs its own key model or it has none at all.
35
+ *
36
+ * Ctrl/Cmd + Left/Right rather than plain arrows: a screen reader owns the
37
+ * bare arrow keys in browse mode, and a board scrolls horizontally, so plain
38
+ * arrows on a focused card would fight both.
39
+ */
40
+ const moveByKey = (card, delta) => {
41
+ const from = columns.findIndex((c) => c.id === card.columnId);
42
+ const to = columns[from + delta];
43
+ if (from < 0 || !to)
44
+ return false;
45
+ move(card.id, to.id);
46
+ setAnnouncement(announceMove(to.title, getCardLabel?.(card)));
47
+ setRefocusId(card.id);
48
+ return true;
49
+ };
50
+ return (_jsxs("div", { ref: rootRef, className: boardRecipe({ className }), ...rest, children: [columns.map((col) => {
51
+ const colCards = cards.filter((c) => c.columnId === col.id);
52
+ return (_jsxs("div", { className: "board-col", "data-over": overCol === col.id ? "" : undefined, onDragOver: (e) => {
53
+ if (!dragId)
54
+ return;
55
+ e.preventDefault();
56
+ setOverCol(col.id);
57
+ }, onDragLeave: (e) => {
58
+ if (!e.currentTarget.contains(e.relatedTarget)) {
59
+ setOverCol((v) => (v === col.id ? null : v));
60
+ }
61
+ }, onDrop: (e) => {
62
+ e.preventDefault();
63
+ if (dragId)
64
+ move(dragId, col.id);
65
+ setDragId(null);
66
+ setOverCol(null);
67
+ }, children: [_jsxs("div", { className: "board-col-header", children: [col.accent && (_jsx("span", { className: "board-col-dot", style: { background: col.accent } })), _jsx("span", { className: "board-col-title", children: col.title }), _jsx("span", { className: "board-col-count", children: colCards.length }), onNewCard && (_jsx("button", { type: "button", className: "board-col-add", "aria-label": `Add to ${col.title}`, onClick: () => onNewCard(col.id), children: "+" }))] }), _jsxs("div", { className: "board-col-body", role: "list", "aria-label": col.title, children: [onNewCard && (_jsxs("button", { type: "button", className: "board-newcard", onClick: () => onNewCard(col.id), children: ["+ ", newCardLabel] })), colCards.map((card) => (_jsx("div", { className: "board-card", role: "listitem",
68
+ // biome-ignore lint/a11y/noNoninteractiveTabindex: the rule assumes a listitem cannot be operated. This one carries the board's only keyboard path to move a card, so it has to be reachable by Tab
69
+ tabIndex: 0, "aria-label": getCardLabel?.(card), "data-card-id": card.id, draggable: true, "data-dragging": dragId === card.id ? "" : undefined, onDragStart: () => setDragId(card.id), onDragEnd: () => {
70
+ setDragId(null);
71
+ setOverCol(null);
72
+ }, onKeyDown: (e) => {
73
+ if (!(e.ctrlKey || e.metaKey))
74
+ return;
75
+ const delta = e.key === "ArrowLeft"
76
+ ? -1
77
+ : e.key === "ArrowRight"
78
+ ? 1
79
+ : 0;
80
+ // only swallow the key when the move actually happens, so
81
+ // Ctrl+Right at the last column still reaches the browser
82
+ if (delta && moveByKey(card, delta))
83
+ e.preventDefault();
84
+ }, children: renderCard(card) }, card.id)))] })] }, col.id));
85
+ }), _jsx("div", { className: "board-live", role: "status", "aria-live": "polite", children: announcement })] }));
41
86
  }
42
87
  Board.displayName = "Board";
@@ -19,7 +19,14 @@ const config = defineRecipe({
19
19
  & .board-newcard:hover { background: color-mix(in srgb, ${c.fgPrimary} 7%, transparent); color: ${c.fgPrimary}; }
20
20
  & .board-card { display: flex; flex-direction: column; gap: ${c.space["2"]}; box-sizing: border-box; padding: ${c.space["2.5"]} ${c.space["3"]}; border-radius: ${c.radius.lg}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px ${c.borderSubtle}, 0 1px 2px rgba(0,0,0,0.04); cursor: grab; }
21
21
  & .board-card:active { cursor: grabbing; }
22
- & .board-card[data-dragging] { opacity: 0.4; }`,
22
+ & .board-card[data-dragging] { opacity: 0.4; }
23
+ & .board-card:focus { outline: none; }
24
+ & .board-card:focus-visible { outline: 2px solid ${c.bgBrandSolid}; outline-offset: 1px; }
25
+ /* announced, never drawn */
26
+ & .board-live {
27
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
28
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
29
+ }`,
23
30
  variants: {},
24
31
  defaultVariants: {},
25
32
  compoundVariants: [],
package/dist/styles.css CHANGED
@@ -1950,6 +1950,13 @@
1950
1950
  & .board-card { display: flex; flex-direction: column; gap: var(--space-2); box-sizing: border-box; padding: var(--space-2-5) var(--space-3); border-radius: var(--radius-lg); background: var(--bg-surface); box-shadow: inset 0 0 0 1px var(--border-subtle), 0 1px 2px rgba(0,0,0,0.04); cursor: grab; }
1951
1951
  & .board-card:active { cursor: grabbing; }
1952
1952
  & .board-card[data-dragging] { opacity: 0.4; }
1953
+ & .board-card:focus { outline: none; }
1954
+ & .board-card:focus-visible { outline: 2px solid var(--bg-brand-solid); outline-offset: 1px; }
1955
+ /* announced, never drawn */
1956
+ & .board-live {
1957
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
1958
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
1959
+ }
1953
1960
  }
1954
1961
 
1955
1962
  @media (prefers-reduced-motion: reduce) {
@@ -18,6 +18,14 @@ export type BoardProps<T extends BoardCard> = Omit<React.ComponentPropsWithoutRe
18
18
  /** When set, each column shows a "+ {newCardLabel}" button and its header +. */
19
19
  onNewCard?: (columnId: string) => void;
20
20
  newCardLabel?: string;
21
+ /**
22
+ * Accessible name for a card. Also used in the move announcement. Without it
23
+ * a card is announced by whatever `renderCard` puts in it, which is usually
24
+ * right but is not something the component can guarantee.
25
+ */
26
+ getCardLabel?: (card: T) => string;
27
+ /** What a screen reader hears after a keyboard move. */
28
+ announceMove?: (columnTitle: string, cardLabel?: string) => string;
21
29
  className?: string;
22
30
  };
23
31
  /**
@@ -26,7 +34,7 @@ export type BoardProps<T extends BoardCard> = Omit<React.ComponentPropsWithoutRe
26
34
  * (the parent owns the cards, so the move is a controlled state update).
27
35
  * Measured from Attio's Deals pipeline.
28
36
  */
29
- export declare function Board<T extends BoardCard>({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel, className, ...rest }: BoardProps<T>): React.JSX.Element;
37
+ export declare function Board<T extends BoardCard>({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel, getCardLabel, announceMove, className, ...rest }: BoardProps<T>): React.JSX.Element;
30
38
  export declare namespace Board {
31
39
  var displayName: string;
32
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",