@crisp-ui-kit/crisp 0.56.0 → 0.57.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.
Files changed (139) hide show
  1. package/dist/accentpicker.css +5 -0
  2. package/dist/accentpicker.d.ts +7 -0
  3. package/dist/accentpicker.layered.css +7 -0
  4. package/dist/avatar.css +1 -1
  5. package/dist/avatar.layered.css +1 -1
  6. package/dist/badge.css +2 -2
  7. package/dist/badge.layered.css +2 -2
  8. package/dist/button.css +3 -3
  9. package/dist/button.layered.css +3 -3
  10. package/dist/callout.css +6 -0
  11. package/dist/callout.d.ts +2 -2
  12. package/dist/callout.layered.css +6 -0
  13. package/dist/cjs/components/accentpicker/accentpicker.js +66 -0
  14. package/dist/cjs/components/accentpicker/accentpicker.recipe.js +25 -0
  15. package/dist/cjs/components/accentpicker/index.js +7 -0
  16. package/dist/cjs/components/avatar/avatar.recipe.js +4 -1
  17. package/dist/cjs/components/badge/badge.recipe.js +16 -2
  18. package/dist/cjs/components/button/button.recipe.js +16 -6
  19. package/dist/cjs/components/callout/callout.js +2 -2
  20. package/dist/cjs/components/callout/callout.recipe.js +15 -2
  21. package/dist/cjs/components/input/input.js +2 -2
  22. package/dist/cjs/components/input/input.recipe.js +14 -0
  23. package/dist/cjs/components/menu/menu.js +2 -2
  24. package/dist/cjs/components/menu/menu.recipe.js +8 -0
  25. package/dist/cjs/components/multiselect/multiselect.js +23 -10
  26. package/dist/cjs/components/multiselect/multiselect.recipe.js +104 -65
  27. package/dist/cjs/components/progress/progress.js +21 -4
  28. package/dist/cjs/components/progress/progress.recipe.js +30 -0
  29. package/dist/cjs/components/select/listbox-keyboard.js +7 -1
  30. package/dist/cjs/components/select/select.js +24 -11
  31. package/dist/cjs/components/select/select.recipe.js +177 -78
  32. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +3 -3
  33. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  34. package/dist/cjs/components/tabs/tabs.js +63 -4
  35. package/dist/cjs/components/tabs/tabs.recipe.js +13 -0
  36. package/dist/cjs/components/text/text.recipe.js +8 -7
  37. package/dist/cjs/components/textarea/textarea.js +3 -2
  38. package/dist/cjs/components/textarea/textarea.recipe.js +2 -1
  39. package/dist/cjs/components/textlink/textlink.js +2 -2
  40. package/dist/cjs/components/textlink/textlink.recipe.js +24 -3
  41. package/dist/cjs/index.js +1 -0
  42. package/dist/cjs/product/recordpage/recordpage.js +61 -7
  43. package/dist/cjs/tokens/elevation.js +7 -0
  44. package/dist/cjs/tokens/palette.js +120 -25
  45. package/dist/cjs/tokens/scale.js +9 -0
  46. package/dist/cjs/tokens/semantic.js +42 -0
  47. package/dist/esm/components/accentpicker/accentpicker.js +30 -0
  48. package/dist/esm/components/accentpicker/accentpicker.recipe.js +22 -0
  49. package/dist/esm/components/accentpicker/index.js +2 -0
  50. package/dist/esm/components/avatar/avatar.recipe.js +4 -1
  51. package/dist/esm/components/badge/badge.recipe.js +16 -2
  52. package/dist/esm/components/button/button.recipe.js +16 -6
  53. package/dist/esm/components/callout/callout.js +2 -2
  54. package/dist/esm/components/callout/callout.recipe.js +15 -2
  55. package/dist/esm/components/input/input.js +3 -3
  56. package/dist/esm/components/input/input.recipe.js +14 -0
  57. package/dist/esm/components/menu/menu.js +2 -2
  58. package/dist/esm/components/menu/menu.recipe.js +8 -0
  59. package/dist/esm/components/multiselect/multiselect.js +23 -10
  60. package/dist/esm/components/multiselect/multiselect.recipe.js +104 -65
  61. package/dist/esm/components/progress/progress.js +22 -5
  62. package/dist/esm/components/progress/progress.recipe.js +30 -0
  63. package/dist/esm/components/select/listbox-keyboard.js +7 -1
  64. package/dist/esm/components/select/select.js +24 -11
  65. package/dist/esm/components/select/select.recipe.js +177 -78
  66. package/dist/esm/components/tabletoolbar/tabletoolbar.js +3 -3
  67. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +26 -1
  68. package/dist/esm/components/tabs/tabs.js +63 -4
  69. package/dist/esm/components/tabs/tabs.recipe.js +13 -0
  70. package/dist/esm/components/text/text.recipe.js +8 -7
  71. package/dist/esm/components/textarea/textarea.js +3 -2
  72. package/dist/esm/components/textarea/textarea.recipe.js +2 -1
  73. package/dist/esm/components/textlink/textlink.js +2 -2
  74. package/dist/esm/components/textlink/textlink.recipe.js +24 -3
  75. package/dist/esm/index.js +1 -0
  76. package/dist/esm/product/recordpage/recordpage.js +62 -8
  77. package/dist/esm/tokens/elevation.js +7 -0
  78. package/dist/esm/tokens/palette.js +120 -25
  79. package/dist/esm/tokens/scale.js +9 -0
  80. package/dist/esm/tokens/semantic.js +42 -0
  81. package/dist/input.css +11 -0
  82. package/dist/input.d.ts +2 -2
  83. package/dist/input.layered.css +11 -0
  84. package/dist/menu.css +8 -0
  85. package/dist/menu.layered.css +8 -0
  86. package/dist/multiselect.css +57 -29
  87. package/dist/multiselect.d.ts +3 -1
  88. package/dist/multiselect.layered.css +57 -29
  89. package/dist/native/tokens.d.ts +1 -1
  90. package/dist/native/tokens.js +82 -28
  91. package/dist/progress.css +3 -0
  92. package/dist/progress.layered.css +3 -0
  93. package/dist/select.css +100 -42
  94. package/dist/select.d.ts +3 -2
  95. package/dist/select.layered.css +100 -42
  96. package/dist/styles.css +268 -93
  97. package/dist/styles.layered.css +268 -93
  98. package/dist/tabletoolbar.css +8 -1
  99. package/dist/tabletoolbar.layered.css +8 -1
  100. package/dist/tabs.css +6 -0
  101. package/dist/tabs.layered.css +6 -0
  102. package/dist/text.css +1 -1
  103. package/dist/text.layered.css +1 -1
  104. package/dist/textarea.css +2 -1
  105. package/dist/textarea.layered.css +2 -1
  106. package/dist/textlink.css +11 -2
  107. package/dist/textlink.d.ts +2 -1
  108. package/dist/textlink.layered.css +11 -2
  109. package/dist/tokens.css +43 -11
  110. package/dist/tokens.json +82 -28
  111. package/dist/types/components/accentpicker/accentpicker.d.ts +23 -0
  112. package/dist/types/components/accentpicker/accentpicker.recipe.d.ts +3 -0
  113. package/dist/types/components/accentpicker/index.d.ts +2 -0
  114. package/dist/types/components/button/button.recipe.d.ts +2 -2
  115. package/dist/types/components/callout/callout.d.ts +9 -1
  116. package/dist/types/components/callout/callout.recipe.d.ts +12 -2
  117. package/dist/types/components/input/input.d.ts +14 -0
  118. package/dist/types/components/input/input.recipe.d.ts +3 -0
  119. package/dist/types/components/menu/menu.d.ts +7 -1
  120. package/dist/types/components/multiselect/multiselect.d.ts +25 -6
  121. package/dist/types/components/multiselect/multiselect.recipe.d.ts +27 -0
  122. package/dist/types/components/progress/progress.d.ts +16 -4
  123. package/dist/types/components/progress/progress.recipe.d.ts +22 -0
  124. package/dist/types/components/select/listbox-keyboard.d.ts +10 -1
  125. package/dist/types/components/select/select.d.ts +29 -6
  126. package/dist/types/components/select/select.recipe.d.ts +25 -10
  127. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +14 -0
  128. package/dist/types/components/tabs/tabs.d.ts +9 -1
  129. package/dist/types/components/textarea/textarea.d.ts +16 -0
  130. package/dist/types/components/textlink/textlink.d.ts +8 -1
  131. package/dist/types/components/textlink/textlink.recipe.d.ts +10 -0
  132. package/dist/types/index.d.ts +1 -0
  133. package/dist/types/product/recordpage/recordpage.d.ts +61 -1
  134. package/dist/types/tokens/contract.d.ts +16 -3
  135. package/dist/types/tokens/elevation.d.ts +7 -0
  136. package/dist/types/tokens/palette.d.ts +55 -11
  137. package/dist/types/tokens/scale.d.ts +6 -0
  138. package/dist/types/tokens/semantic.d.ts +33 -0
  139. package/package.json +6 -1
@@ -0,0 +1,5 @@
1
+ .crisp-accentpicker {
2
+ display: flex; gap: var(--crisp-space-2);
3
+ & .crisp-accentpicker-swatch { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; margin: 0; border-radius: var(--crisp-radius-full); border: 1px solid transparent; background: none; font-family: inherit; cursor: pointer; }
4
+ & .crisp-accentpicker-swatch:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 2px; }
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { RecipeFn } from "../core/types";
2
+
3
+ export interface AccentpickerVariants {
4
+
5
+ }
6
+
7
+ export declare const accentpickerRecipe: RecipeFn<{ }>;
@@ -0,0 +1,7 @@
1
+ @layer crisp-components {
2
+ .crisp-accentpicker {
3
+ display: flex; gap: var(--crisp-space-2);
4
+ & .crisp-accentpicker-swatch { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; margin: 0; border-radius: var(--crisp-radius-full); border: 1px solid transparent; background: none; font-family: inherit; cursor: pointer; }
5
+ & .crisp-accentpicker-swatch:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 2px; }
6
+ }
7
+ }
package/dist/avatar.css CHANGED
@@ -24,7 +24,7 @@
24
24
  background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-muted); box-shadow: inset 0 0 0 1px var(--crisp-border-default);
25
25
  }
26
26
  .crisp-avatar--tone_brand {
27
- background: var(--crisp-bg-brand-solid); color: #fff;
27
+ background: var(--crisp-bg-brand-contrast); color: #fff;
28
28
  }
29
29
  .crisp-avatar--tone_orange {
30
30
  background: var(--crisp-bg-orange); color: #fff;
@@ -25,7 +25,7 @@
25
25
  background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-muted); box-shadow: inset 0 0 0 1px var(--crisp-border-default);
26
26
  }
27
27
  .crisp-avatar--tone_brand {
28
- background: var(--crisp-bg-brand-solid); color: #fff;
28
+ background: var(--crisp-bg-brand-contrast); color: #fff;
29
29
  }
30
30
  .crisp-avatar--tone_orange {
31
31
  background: var(--crisp-bg-orange); color: #fff;
package/dist/badge.css CHANGED
@@ -36,13 +36,13 @@
36
36
 
37
37
  }
38
38
  .crisp-badge--compound_0 {
39
- background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); box-shadow: none;
39
+ background: var(--crisp-bg-brand-contrast); color: var(--crisp-fg-on-brand); box-shadow: none;
40
40
  }
41
41
  .crisp-badge--compound_1 {
42
42
  background: var(--crisp-bg-success); color: var(--crisp-fg-on-tone); box-shadow: none;
43
43
  }
44
44
  .crisp-badge--compound_2 {
45
- background: var(--crisp-bg-warning); color: var(--crisp-fg-on-tone); box-shadow: none;
45
+ background: var(--crisp-bg-warning-solid); color: var(--crisp-fg-on-tone); box-shadow: none;
46
46
  }
47
47
  .crisp-badge--compound_3 {
48
48
  background: var(--crisp-bg-danger); color: var(--crisp-fg-on-tone); box-shadow: none;
@@ -37,13 +37,13 @@
37
37
 
38
38
  }
39
39
  .crisp-badge--compound_0 {
40
- background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); box-shadow: none;
40
+ background: var(--crisp-bg-brand-contrast); color: var(--crisp-fg-on-brand); box-shadow: none;
41
41
  }
42
42
  .crisp-badge--compound_1 {
43
43
  background: var(--crisp-bg-success); color: var(--crisp-fg-on-tone); box-shadow: none;
44
44
  }
45
45
  .crisp-badge--compound_2 {
46
- background: var(--crisp-bg-warning); color: var(--crisp-fg-on-tone); box-shadow: none;
46
+ background: var(--crisp-bg-warning-solid); color: var(--crisp-fg-on-tone); box-shadow: none;
47
47
  }
48
48
  .crisp-badge--compound_3 {
49
49
  background: var(--crisp-bg-danger); color: var(--crisp-fg-on-tone); box-shadow: none;
package/dist/button.css CHANGED
@@ -3,9 +3,9 @@
3
3
  &:disabled { opacity: 0.5; cursor: not-allowed; }
4
4
  }
5
5
  .crisp-button--intent_primary {
6
- background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08);
7
- &:hover { background: var(--crisp-bg-brand-solid-pressed); }
8
- &:active { background: var(--crisp-bg-brand-solid-pressed); }
6
+ background: var(--crisp-bg-brand-contrast); color: var(--crisp-fg-on-brand); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08);
7
+ &:hover { filter: brightness(0.96); }
8
+ &:active { filter: brightness(0.92); }
9
9
  }
10
10
  .crisp-button--intent_neutral {
11
11
  background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); 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);
@@ -4,9 +4,9 @@
4
4
  &:disabled { opacity: 0.5; cursor: not-allowed; }
5
5
  }
6
6
  .crisp-button--intent_primary {
7
- background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08);
8
- &:hover { background: var(--crisp-bg-brand-solid-pressed); }
9
- &:active { background: var(--crisp-bg-brand-solid-pressed); }
7
+ background: var(--crisp-bg-brand-contrast); color: var(--crisp-fg-on-brand); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08);
8
+ &:hover { filter: brightness(0.96); }
9
+ &:active { filter: brightness(0.92); }
10
10
  }
11
11
  .crisp-button--intent_neutral {
12
12
  background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); 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);
package/dist/callout.css CHANGED
@@ -14,3 +14,9 @@
14
14
  &[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
15
15
  &[data-tone="danger"] .crisp-callout-icon { color: var(--crisp-bg-danger); }
16
16
  }
17
+ .crisp-callout--bordered_true {
18
+ border: 1px solid var(--crisp-ring-brand-soft); border-radius: var(--crisp-radius-xl); padding: var(--crisp-space-2) var(--crisp-space-3); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent);
19
+ }
20
+ .crisp-callout--bordered_false {
21
+
22
+ }
package/dist/callout.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface CalloutVariants {
4
-
4
+ bordered?: boolean;
5
5
  }
6
6
 
7
- export declare const calloutRecipe: RecipeFn<{ }>;
7
+ export declare const calloutRecipe: RecipeFn<{ bordered: { true: string; false: string } }>;
@@ -14,5 +14,11 @@
14
14
  &[data-tone="warning"] .crisp-callout-icon { color: var(--crisp-bg-warning); }
15
15
  &[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
16
16
  &[data-tone="danger"] .crisp-callout-icon { color: var(--crisp-bg-danger); }
17
+ }
18
+ .crisp-callout--bordered_true {
19
+ border: 1px solid var(--crisp-ring-brand-soft); border-radius: var(--crisp-radius-xl); padding: var(--crisp-space-2) var(--crisp-space-3); background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent);
20
+ }
21
+ .crisp-callout--bordered_false {
22
+
17
23
  }
18
24
  }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AccentPicker = AccentPicker;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const React = __importStar(require("react"));
39
+ const radiogroup_1 = require("../radiogroup");
40
+ const accentpicker_recipe_1 = require("./accentpicker.recipe");
41
+ /**
42
+ * A row of selectable colour swatches — Attio's own accent-colour picker
43
+ * (Settings > Appearance). Not a fixed palette: pass the colours (and the
44
+ * accent isn't crisp's concern to theme the app with — this only picks one).
45
+ * Same roving-focus/arrow-key model every other crisp radio-like control
46
+ * shares (`useRadioGroupKeyboard`, also used by `RadioGroup` and
47
+ * `Segmented`).
48
+ */
49
+ function AccentPicker({ colors, value, defaultValue, onValueChange, className, onKeyDown: onKeyDownProp, ...rest }) {
50
+ const [internal, setInternal] = React.useState(defaultValue);
51
+ const selected = value ?? internal;
52
+ const select = (v) => {
53
+ if (value === undefined)
54
+ setInternal(v);
55
+ onValueChange?.(v);
56
+ };
57
+ const { onKeyDown, groupRef } = (0, radiogroup_1.useRadioGroupKeyboard)({ select });
58
+ return ((0, jsx_runtime_1.jsx)("div", { ref: groupRef, role: "radiogroup", className: (0, accentpicker_recipe_1.accentpickerRecipe)({ className }), ...rest, onKeyDown: (e) => {
59
+ onKeyDown(e);
60
+ onKeyDownProp?.(e);
61
+ }, children: colors.map((color) => {
62
+ const checked = selected === color;
63
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", role: "radio", "aria-checked": checked, "aria-label": color, "data-value": color, tabIndex: checked ? 0 : -1, className: "crisp-accentpicker-swatch", style: { borderColor: checked ? color : "transparent" }, onClick: () => select(color), children: (0, jsx_runtime_1.jsx)("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("circle", { cx: "11", cy: "11", r: "9", fill: color }) }) }, color));
64
+ }) }));
65
+ }
66
+ AccentPicker.displayName = "AccentPicker";
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.accentpickerRecipe = void 0;
4
+ const core_1 = require("../../core");
5
+ const tokens_1 = require("../../tokens");
6
+ // A row of selectable colour swatches — measured live on Attio's Appearance
7
+ // settings page (crisp#635): `role="radiogroup"`, `display: flex; gap: 8px`.
8
+ // Each swatch is a 24x24 circular button, `border: 1px solid transparent`
9
+ // at rest; the CHECKED swatch's border switches to its own colour (not a
10
+ // fixed brand ring) — measured directly on the DOM (`border: 1px solid
11
+ // rgb(38,109,240)` on the checked blue swatch, `1px solid rgba(0,0,0,0)` on
12
+ // every unchecked one). That per-swatch colour is data, not a class, so it's
13
+ // set inline by the component, not here. Inside sits a 22x22 SVG with one
14
+ // solid r=9 (18px) circle -- no separate ring/dot layer, contrary to an
15
+ // earlier, less precise read of this same control.
16
+ const config = (0, core_1.defineRecipe)({
17
+ name: "accentpicker",
18
+ base: `display: flex; gap: ${tokens_1.contract.space["2"]};
19
+ & .crisp-accentpicker-swatch { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
20
+ width: 24px; height: 24px; padding: 0; margin: 0; border-radius: ${tokens_1.contract.radius.full}; border: 1px solid transparent;
21
+ background: none; font-family: inherit; cursor: pointer; }
22
+ & .crisp-accentpicker-swatch:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 2px; }`,
23
+ });
24
+ exports.accentpickerRecipe = (0, core_1.resolveRecipe)(config);
25
+ exports.default = config;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.accentpickerRecipe = exports.AccentPicker = void 0;
4
+ var accentpicker_1 = require("./accentpicker");
5
+ Object.defineProperty(exports, "AccentPicker", { enumerable: true, get: function () { return accentpicker_1.AccentPicker; } });
6
+ var accentpicker_recipe_1 = require("./accentpicker.recipe");
7
+ Object.defineProperty(exports, "accentpickerRecipe", { enumerable: true, get: function () { return accentpicker_recipe_1.accentpickerRecipe; } });
@@ -39,7 +39,10 @@ const spec = (0, core_1.defineSpec)({
39
39
  color: tokens_1.contract.fgNeutralMuted,
40
40
  boxShadow: `inset 0 0 0 1px ${tokens_1.contract.borderDefault}`,
41
41
  },
42
- brand: { background: tokens_1.contract.bgBrandSolid, color: "#fff" },
42
+ // your-moon/crisp#897: `bgBrandContrast`, the darker AA-safe sibling
43
+ // of the accent blue (`bgBrandSolid`, #0298d9, only 3.23:1 with
44
+ // white) -- keeps white text, matching every other tone here.
45
+ brand: { background: tokens_1.contract.bgBrandContrast, color: "#fff" },
43
46
  orange: { background: tokens_1.contract.bgOrange, color: "#fff" },
44
47
  cyan: { background: tokens_1.contract.bgCyan, color: "#fff" },
45
48
  purple: { background: tokens_1.contract.bgPurple, color: "#fff" },
@@ -129,8 +129,14 @@ const spec = (0, core_1.defineSpec)({
129
129
  {
130
130
  variant: "solid",
131
131
  tone: "brand",
132
+ // your-moon/crisp#897: `bgBrandContrast`, not `bgBrandSolid` — white
133
+ // text on the exact accent blue (#0298d9) only clears 3.23:1;
134
+ // `bgBrandContrast` is the darker sibling built to clear 4.5:1+ with
135
+ // white, so `fgOnBrand` (white) stays right rather than `fgOnTone`
136
+ // (ink read off-brand next to solid `success`/`warning`/`danger`,
137
+ // which really do need it).
132
138
  decls: {
133
- background: tokens_1.contract.bgBrandSolid,
139
+ background: tokens_1.contract.bgBrandContrast,
134
140
  color: tokens_1.contract.fgOnBrand,
135
141
  boxShadow: "none",
136
142
  },
@@ -143,7 +149,15 @@ const spec = (0, core_1.defineSpec)({
143
149
  {
144
150
  variant: "solid",
145
151
  tone: "warning",
146
- decls: { background: tokens_1.contract.bgWarning, color: tokens_1.contract.fgOnTone, boxShadow: "none" },
152
+ // seed-identity migration (your-moon/crisp#890): `bgWarningSolid`, not
153
+ // `bgWarning` — seed's own solid warning fill is a distinct, paler
154
+ // yellow from the accent every other tone's solid fill reuses
155
+ // directly (see tokens/palette.ts's `amberSolid` comment).
156
+ decls: {
157
+ background: tokens_1.contract.bgWarningSolid,
158
+ color: tokens_1.contract.fgOnTone,
159
+ boxShadow: "none",
160
+ },
147
161
  },
148
162
  {
149
163
  variant: "solid",
@@ -30,7 +30,13 @@ const spec = (0, core_1.defineSpec)({
30
30
  intent: {
31
31
  primary: {
32
32
  decls: {
33
- background: tokens_1.contract.bgBrandSolid,
33
+ // your-moon/crisp#897: `bgBrandContrast`, not `bgBrandSolid` —
34
+ // white text on the exact accent blue (#0298d9) only clears
35
+ // 3.23:1; `bgBrandContrast` is the darker sibling built to clear
36
+ // white at 4.5:1+, keeping `fgOnBrand` (white) rather than
37
+ // reaching for `fgOnTone` (ink read off-brand — seed's own solid
38
+ // buttons use white).
39
+ background: tokens_1.contract.bgBrandContrast,
34
40
  color: tokens_1.contract.fgOnBrand,
35
41
  // crisp#494: the inset ring is a black tint, not white -- live-
36
42
  // measured on Attio's own "New Company" button as
@@ -39,12 +45,16 @@ const spec = (0, core_1.defineSpec)({
39
45
  // exactly.
40
46
  boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08)",
41
47
  },
48
+ // your-moon/crisp#897: a brightness filter, not a second background
49
+ // swap — matches `danger`'s own pattern below. `bgBrandSolidPressed`
50
+ // remains the accent's own hover-darken for the many OTHER
51
+ // consumers that pair `bgBrandSolid` with white already (out of
52
+ // scope for this fix; tracked separately), so reusing it here would
53
+ // have made primary's rest-to-hover jump inconsistent with its own
54
+ // rest fill's new, darker starting point.
42
55
  parts: [
43
- { selector: "&:hover", decls: { background: tokens_1.contract.bgBrandSolidPressed } },
44
- {
45
- selector: "&:active",
46
- decls: { background: tokens_1.contract.bgBrandSolidPressed },
47
- },
56
+ { selector: "&:hover", decls: { filter: "brightness(0.96)" } },
57
+ { selector: "&:active", decls: { filter: "brightness(0.92)" } },
48
58
  ],
49
59
  },
50
60
  neutral: {
@@ -15,11 +15,11 @@ const DEFAULT_ICONS = {
15
15
  * An inline callout / banner — measured from info notices. `tone` tints
16
16
  * the background and icon; pass a `title` and/or body `children`.
17
17
  */
18
- function Callout({ tone = "neutral", icon, title, children, action, className, ...rest }) {
18
+ function Callout({ tone = "neutral", icon, title, children, action, bordered, className, ...rest }) {
19
19
  const Fallback = DEFAULT_ICONS[tone];
20
20
  const iconNode = icon === undefined
21
21
  ? Fallback && (0, jsx_runtime_1.jsx)(Fallback, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })
22
22
  : icon;
23
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, callout_recipe_1.calloutRecipe)({ className }), ...rest, "data-tone": tone, role: "note", children: [iconNode && (0, jsx_runtime_1.jsx)("span", { className: "crisp-callout-icon", children: iconNode }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-callout-body", children: [title != null && (0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-title", children: title }), children != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-desc", children: children }))] }), action && (0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-action", children: action })] }));
23
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, callout_recipe_1.calloutRecipe)({ bordered, className }), ...rest, "data-tone": tone, role: "note", children: [iconNode && (0, jsx_runtime_1.jsx)("span", { className: "crisp-callout-icon", children: iconNode }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-callout-body", children: [title != null && (0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-title", children: title }), children != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-desc", children: children }))] }), action && (0, jsx_runtime_1.jsx)("div", { className: "crisp-callout-action", children: action })] }));
24
24
  }
25
25
  Callout.displayName = "Callout";
@@ -21,8 +21,21 @@ const config = (0, core_1.defineRecipe)({
21
21
  &[data-tone="warning"] .crisp-callout-icon { color: ${tokens_1.contract.bgWarning}; }
22
22
  &[data-tone="danger"] { background: color-mix(in srgb, ${tokens_1.contract.bgDanger} 10%, transparent); }
23
23
  &[data-tone="danger"] .crisp-callout-icon { color: ${tokens_1.contract.bgDanger}; }`,
24
- variants: {},
25
- defaultVariants: {},
24
+ variants: {
25
+ // crisp#869: a page-level banner look, measured live on Attio's Objects
26
+ // settings page (the "Create custom objects... Upgrade" Pro-plan
27
+ // nudge) — a real 1px opaque border, 12px radius, 8px/12px padding, and
28
+ // a 14%-brand-tinted background (the same `color-mix` ratio Badge's own
29
+ // soft-brand variant already uses). Only this one brand/info-toned
30
+ // instance was found live; `bordered` doesn't attempt a per-tone border
31
+ // colour for success/warning/danger — no such Attio banner was found to
32
+ // measure one against.
33
+ bordered: {
34
+ true: `border: 1px solid ${tokens_1.contract.ringBrandSoft}; border-radius: ${tokens_1.contract.radius.xl}; padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["3"]}; background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 14%, transparent);`,
35
+ false: "",
36
+ },
37
+ },
38
+ defaultVariants: { bordered: false },
26
39
  compoundVariants: [],
27
40
  });
28
41
  exports.calloutRecipe = (0, core_1.resolveRecipe)(config);
@@ -37,11 +37,11 @@ exports.Input = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const React = __importStar(require("react"));
39
39
  const input_recipe_1 = require("./input.recipe");
40
- exports.Input = React.forwardRef(({ size, invalid, className, style, ...props }, ref) => {
40
+ exports.Input = React.forwardRef(({ size, invalid, className, style, suffix, ...props }, ref) => {
41
41
  // The wrapper exists only to host the ::after focus ring — an <input> is a
42
42
  // replaced element and cannot have one. Everything the consumer passes
43
43
  // still lands on the <input> itself, so `id`, `name`, `onChange`, form
44
44
  // association and every native attribute behave exactly as before.
45
- return ((0, jsx_runtime_1.jsx)("span", { className: (0, input_recipe_1.inputRecipe)({ size, invalid, className }), style: style, children: (0, jsx_runtime_1.jsx)("input", { ref: ref, className: "crisp-input-value", "aria-invalid": invalid === true || undefined, ...props }) }));
45
+ return ((0, jsx_runtime_1.jsxs)("span", { className: (0, input_recipe_1.inputRecipe)({ size, invalid, className }), style: style, "data-readonly": props.readOnly ? "" : undefined, children: [(0, jsx_runtime_1.jsx)("input", { ref: ref, className: "crisp-input-value", "aria-invalid": invalid === true || undefined, ...props }), suffix != null && (0, jsx_runtime_1.jsx)("span", { className: "crisp-input-suffix", children: suffix })] }));
46
46
  });
47
47
  exports.Input.displayName = "Input";
@@ -41,9 +41,23 @@ const config = (0, core_1.defineRecipe)({
41
41
  & .crisp-input-value::placeholder { color: ${tokens_1.contract.fgMuted}; }
42
42
  & .crisp-input-value:disabled { opacity: 0.5; cursor: not-allowed; }
43
43
  &:has(.crisp-input-value:disabled) { opacity: 1; }
44
+
45
+ /* crisp#634: measured live on Attio's own Settings > Profile read-only
46
+ primary-email field -- a tinted field, not just muted text, driven off
47
+ the native \`readOnly\` attribute. \`ringField\` (rgba(0,0,0,.06)) is an
48
+ exact match for the measured ring; the 2%-fgPrimary tint reuses the
49
+ same color-mix idiom every other soft crisp surface already uses. */
50
+ &[data-readonly] { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 2%, transparent); box-shadow: inset 0 0 0 1px ${tokens_1.contract.ringField}; }
51
+ & .crisp-input-suffix { flex-shrink: 0; margin-inline-start: ${tokens_1.contract.space["1"]}; }
44
52
  `,
45
53
  variants: {
46
54
  size: {
55
+ // crisp#878: not a fresh click-measurement -- reuses `TableToolbar`'s
56
+ // own compact search box shape (`tabletoolbar.chrome.ts`'s `-search`
57
+ // rule), already corroborated against Attio, at the same height as
58
+ // `c.size.row` ("nav item · search · control height"). Same radius and
59
+ // padding `medium` already uses, just 4px shorter.
60
+ sm: `height: ${tokens_1.contract.size.row}; border-radius: ${tokens_1.contract.radius.md}; & .crisp-input-value { padding-inline: ${tokens_1.contract.space["2.5"]}; }`,
47
61
  medium: `height: 32px; border-radius: ${tokens_1.contract.radius.md}; & .crisp-input-value { padding-inline: ${tokens_1.contract.space["2.5"]}; }`,
48
62
  large: `height: 36px; border-radius: 10px; & .crisp-input-value { padding-inline: ${tokens_1.contract.space["3"]}; }`,
49
63
  },
@@ -168,13 +168,13 @@ function Menu({ trigger, children, align = "start", open: openProp, defaultOpen,
168
168
  : "crisp-menu-content", role: "menu", id: surfaceId, "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : triggerId, "data-align": align, "data-up": up ? "" : undefined, style: pos, onKeyDown: onContentKeyDown, children: (0, jsx_runtime_1.jsx)(MenuContext.Provider, { value: { close }, children: children }) }) }))] }));
169
169
  }
170
170
  Menu.displayName = "Menu";
171
- function MenuItem({ children, icon, shortcut, danger, disabled, onSelect, closeOnSelect = true, }) {
171
+ function MenuItem({ children, icon, shortcut, count, danger, disabled, onSelect, closeOnSelect = true, }) {
172
172
  const ctx = React.useContext(MenuContext);
173
173
  return ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", tabIndex: -1, disabled: disabled, "data-disabled": disabled ? "" : undefined, className: `crisp-menu-item${danger ? " crisp-menu-item--danger" : ""}`, onClick: () => {
174
174
  onSelect?.();
175
175
  if (closeOnSelect)
176
176
  ctx?.close();
177
- }, children: [icon, (0, jsx_runtime_1.jsx)("span", { className: "crisp-menu-item-label", children: children }), shortcut && (0, jsx_runtime_1.jsx)("span", { className: "crisp-menu-item-shortcut", children: shortcut })] }));
177
+ }, children: [icon, (0, jsx_runtime_1.jsx)("span", { className: "crisp-menu-item-label", children: children }), count != null && (0, jsx_runtime_1.jsx)("span", { className: "crisp-menu-item-count", children: count }), shortcut && (0, jsx_runtime_1.jsx)("span", { className: "crisp-menu-item-shortcut", children: shortcut })] }));
178
178
  }
179
179
  MenuItem.displayName = "MenuItem";
180
180
  function MenuCheckboxItem({ children, checked, onCheckedChange, disabled, }) {
@@ -57,6 +57,14 @@ const config = (0, core_1.defineRecipe)({
57
57
  .crisp-menu-item--danger svg { color: ${tokens_1.contract.bgDanger}; }
58
58
  .crisp-menu-item--danger:hover, .crisp-menu-item--danger:focus-visible, .crisp-menu-item--danger[data-highlighted] { background: color-mix(in srgb, ${tokens_1.contract.bgDanger} 10%, transparent); }
59
59
  .crisp-menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
60
+ /* crisp#875: same measured neutral chip TableToolbar's own ToolbarTool
61
+ count prop uses (re-confirmed on two live Attio surfaces this round:
62
+ the Sort tool's own "+N" overflow, a Kanban board's stage-column
63
+ count) -- a small badge, not bare text. */
64
+ .crisp-menu-item-count { flex-shrink: 0; margin-inline-start: ${tokens_1.contract.space["1.5"]}; display: inline-flex; align-items: center; height: 16px; box-sizing: border-box;
65
+ padding: 0 ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius.sm}; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent);
66
+ box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgNeutralSoft};
67
+ font-variant-numeric: tabular-nums; }
60
68
  .crisp-menu-item-shortcut { flex-shrink: 0; margin-inline-start: ${tokens_1.contract.space["3"]}; font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.fgMuted}; letter-spacing: 0.02em; }
61
69
  .crisp-menu-item-check { flex-shrink: 0; color: ${tokens_1.contract.bgBrandSolid}; }
62
70
  .crisp-menu-item-check--placeholder { width: 15px; }
@@ -60,16 +60,18 @@ function Chip({ opt }) {
60
60
  return ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-tag", "data-tone": opt.tone ?? toneFor(opt.value), children: opt.label }));
61
61
  }
62
62
  /**
63
- * A multi-select / tag editor — measured from a cell tag picker. The
64
- * outlined trigger shows the selected values as coloured chips; it opens a
65
- * popover with a search field over rows of "[chip] … [checkbox]" that toggle on
66
- * click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
63
+ * A multi-select / tag editor, its trigger/listbox/item chrome built to the
64
+ * same seed-design parity as `Select` (crisp/#882, following #867/#879) —
65
+ * its own chip/checkbox picker behaviour is unchanged. The outlined trigger
66
+ * shows the selected values as coloured chips; it opens a popover with a
67
+ * search field over rows of "[chip] … [checkbox]" that toggle on click (the
68
+ * menu stays open). Keyboard: ↑/↓ move, Enter/Space toggle, Esc closes.
67
69
  */
68
70
  function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, name, required,
69
71
  // The id belongs on the trigger, not the wrapper: `<label for>` only names a
70
72
  // LABELABLE element, so an id left on the root div meant a FormField label
71
73
  // above this control named nothing (#247). Select already did this.
72
- id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPlaceholderProp, emptyLabel: emptyLabelProp, disabled, invalid, className, "aria-label": ariaLabel, ...rest }) {
74
+ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPlaceholderProp, emptyLabel: emptyLabelProp, disabled, readOnly, invalid, size = "small", variant = "default", className, "aria-label": ariaLabel, ...rest }) {
73
75
  // crisp/#471: same resolution order as Select's own.
74
76
  const placeholder = (0, core_1.useCrispMessage)(placeholderProp, "Select…");
75
77
  const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search…");
@@ -106,8 +108,12 @@ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPl
106
108
  const filtered = searchable && q
107
109
  ? options.filter((o) => o.label.toLowerCase().includes(q))
108
110
  : options;
111
+ // seed-design's `interactive` (crisp/#882, matching Select): disabled AND
112
+ // readOnly both refuse to open, but only `disabled` greys the text and
113
+ // removes the button from the tab order.
114
+ const interactive = !disabled && !readOnly;
109
115
  const openMenu = () => {
110
- if (disabled)
116
+ if (!interactive)
111
117
  return;
112
118
  const rect = triggerRef.current?.getBoundingClientRect();
113
119
  if (rect) {
@@ -116,7 +122,7 @@ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPl
116
122
  // direction that is (#210)
117
123
  const { style, up: flipUp } = (0, core_1.placeFloating)({
118
124
  rect,
119
- gap: 4,
125
+ gap: 8, // seed-design's gutter (crisp/#882, matching Select; was 4)
120
126
  flipBelow: 300,
121
127
  anchor: triggerRef.current,
122
128
  });
@@ -161,6 +167,10 @@ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPl
161
167
  close,
162
168
  // Tab is already moving focus out; `close` would drag it back to the trigger
163
169
  dismiss: () => setOpen(false),
170
+ // Space also toggles the highlighted option (crisp/#882, matching
171
+ // Select's Enter/Space parity) when the trigger itself owns navigation.
172
+ // Never when searchable -- see Select's own comment on this option.
173
+ commitOnSpace: !searchable,
164
174
  });
165
175
  // Registers the (now-portaled) listbox on the shared floating-layer stack
166
176
  // (#339) so this doesn't get closed out from under itself when nested
@@ -198,11 +208,14 @@ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPl
198
208
  document.addEventListener("keydown", onKey);
199
209
  return () => document.removeEventListener("keydown", onKey);
200
210
  }, [open, close]);
201
- return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, multiselect_recipe_1.multiselectRecipe)({ invalid, className }), ...rest, children: [name &&
202
- (value.length > 0 ? value : required ? [""] : []).map((v) => ((0, jsx_runtime_1.jsx)("input", { className: "crisp-formvalue", style: core_1.formValueStyle, name: name, value: v, required: required, tabIndex: -1, "aria-hidden": "true", onChange: () => { } }, v))), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, id: id, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-multiselect-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: (e) => {
211
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, multiselect_recipe_1.multiselectRecipe)({ size, variant, invalid, className }), ...rest, children: [name &&
212
+ (value.length > 0 ? value : required ? [""] : []).map((v) => ((0, jsx_runtime_1.jsx)("input", { className: "crisp-formvalue", style: core_1.formValueStyle, name: name, value: v, required: required, tabIndex: -1, "aria-hidden": "true", onChange: () => { } }, v))), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, id: id, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, "aria-readonly": readOnly ? "true" : undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-multiselect-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: (e) => {
213
+ if (!interactive)
214
+ return;
203
215
  if (open && !searchable)
204
216
  onNavKeys(e);
205
- else if (!open && (e.key === "ArrowDown" || e.key === "Enter")) {
217
+ else if (!open &&
218
+ (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ")) {
206
219
  e.preventDefault();
207
220
  openMenu();
208
221
  }