@crisp-ui-kit/crisp 0.32.1 → 0.33.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 (124) hide show
  1. package/README.md +33 -1
  2. package/dist/appshell.css +1 -4
  3. package/dist/appshell.layered.css +1 -4
  4. package/dist/authshell.css +4 -19
  5. package/dist/authshell.layered.css +4 -19
  6. package/dist/avatar.css +2 -1
  7. package/dist/avatar.layered.css +2 -1
  8. package/dist/board.css +1 -4
  9. package/dist/board.layered.css +1 -4
  10. package/dist/breadcrumb.css +0 -1
  11. package/dist/breadcrumb.layered.css +0 -1
  12. package/dist/button.css +14 -6
  13. package/dist/button.layered.css +14 -6
  14. package/dist/card.css +2 -1
  15. package/dist/card.layered.css +2 -1
  16. package/dist/checkbox.css +6 -9
  17. package/dist/checkbox.layered.css +6 -9
  18. package/dist/cjs/components/avatar/avatar.recipe.js +38 -19
  19. package/dist/cjs/components/badge/badge.recipe.js +83 -24
  20. package/dist/cjs/components/button/button.recipe.js +109 -18
  21. package/dist/cjs/components/divider/divider.recipe.js +20 -8
  22. package/dist/cjs/components/progress/progress.recipe.js +50 -10
  23. package/dist/cjs/components/spinner/spinner.recipe.js +15 -7
  24. package/dist/cjs/components/switch/switch.recipe.js +48 -18
  25. package/dist/cjs/core/index.js +4 -1
  26. package/dist/cjs/core/spec.js +71 -0
  27. package/dist/columncalc.css +2 -6
  28. package/dist/columncalc.layered.css +2 -6
  29. package/dist/command.css +9 -11
  30. package/dist/command.layered.css +9 -11
  31. package/dist/datatable.css +9 -19
  32. package/dist/datatable.layered.css +9 -19
  33. package/dist/datepicker.css +15 -80
  34. package/dist/datepicker.layered.css +15 -80
  35. package/dist/datepickerfield.css +3 -18
  36. package/dist/datepickerfield.layered.css +3 -18
  37. package/dist/donutchart.css +1 -2
  38. package/dist/donutchart.layered.css +1 -2
  39. package/dist/entitycard.css +1 -7
  40. package/dist/entitycard.layered.css +1 -7
  41. package/dist/esm/components/avatar/avatar.recipe.js +38 -19
  42. package/dist/esm/components/badge/badge.recipe.js +83 -24
  43. package/dist/esm/components/button/button.recipe.js +109 -18
  44. package/dist/esm/components/divider/divider.recipe.js +20 -8
  45. package/dist/esm/components/progress/progress.recipe.js +50 -10
  46. package/dist/esm/components/spinner/spinner.recipe.js +15 -7
  47. package/dist/esm/components/switch/switch.recipe.js +48 -18
  48. package/dist/esm/core/index.js +1 -0
  49. package/dist/esm/core/spec.js +66 -0
  50. package/dist/fileupload.css +10 -60
  51. package/dist/fileupload.layered.css +10 -60
  52. package/dist/inbox.css +1 -7
  53. package/dist/inbox.layered.css +1 -7
  54. package/dist/input.css +9 -20
  55. package/dist/input.layered.css +9 -20
  56. package/dist/listpage.css +2 -12
  57. package/dist/listpage.layered.css +2 -12
  58. package/dist/menu.css +3 -19
  59. package/dist/menu.layered.css +3 -19
  60. package/dist/multiselect.css +4 -25
  61. package/dist/multiselect.layered.css +4 -25
  62. package/dist/navitem.css +5 -13
  63. package/dist/navitem.layered.css +5 -13
  64. package/dist/onboarding.css +1 -8
  65. package/dist/onboarding.layered.css +1 -8
  66. package/dist/quantitypicker.css +15 -50
  67. package/dist/quantitypicker.layered.css +15 -50
  68. package/dist/radiogroup.css +1 -6
  69. package/dist/radiogroup.layered.css +1 -6
  70. package/dist/recordpage.css +2 -10
  71. package/dist/recordpage.layered.css +2 -10
  72. package/dist/recordpanel.css +1 -13
  73. package/dist/recordpanel.layered.css +1 -13
  74. package/dist/recordstable.css +2 -11
  75. package/dist/recordstable.layered.css +2 -11
  76. package/dist/resizablepanel.css +4 -15
  77. package/dist/resizablepanel.layered.css +4 -15
  78. package/dist/richtext.css +1 -5
  79. package/dist/richtext.layered.css +1 -5
  80. package/dist/select.css +7 -30
  81. package/dist/select.layered.css +7 -30
  82. package/dist/settingsshell.css +2 -7
  83. package/dist/settingsshell.layered.css +2 -7
  84. package/dist/sidebar.css +23 -28
  85. package/dist/sidebar.layered.css +23 -28
  86. package/dist/slider.css +1 -8
  87. package/dist/slider.layered.css +1 -8
  88. package/dist/statcard.css +1 -2
  89. package/dist/statcard.layered.css +1 -2
  90. package/dist/styles.css +194 -652
  91. package/dist/styles.layered.css +194 -652
  92. package/dist/switch.css +1 -5
  93. package/dist/switch.layered.css +1 -5
  94. package/dist/table.css +2 -1
  95. package/dist/table.layered.css +2 -1
  96. package/dist/tabletoolbar.css +1 -3
  97. package/dist/tabletoolbar.layered.css +1 -3
  98. package/dist/tabs.css +1 -5
  99. package/dist/tabs.layered.css +1 -5
  100. package/dist/tasklist.css +1 -8
  101. package/dist/tasklist.layered.css +1 -8
  102. package/dist/textarea.css +2 -13
  103. package/dist/textarea.layered.css +2 -13
  104. package/dist/tooltip.css +2 -1
  105. package/dist/tooltip.layered.css +2 -1
  106. package/dist/trialbanner.css +2 -11
  107. package/dist/trialbanner.layered.css +2 -11
  108. package/dist/types/components/avatar/avatar.d.ts +30 -30
  109. package/dist/types/components/avatar/avatar.recipe.d.ts +136 -49
  110. package/dist/types/components/badge/badge.d.ts +26 -26
  111. package/dist/types/components/badge/badge.recipe.d.ts +169 -45
  112. package/dist/types/components/button/button.d.ts +30 -30
  113. package/dist/types/components/button/button.recipe.d.ts +193 -45
  114. package/dist/types/components/divider/divider.recipe.d.ts +26 -2
  115. package/dist/types/components/progress/progress.recipe.d.ts +49 -2
  116. package/dist/types/components/spinner/spinner.recipe.d.ts +17 -2
  117. package/dist/types/components/switch/switch.recipe.d.ts +57 -3
  118. package/dist/types/core/index.d.ts +1 -0
  119. package/dist/types/core/spec.d.ts +83 -0
  120. package/dist/viewtoggle.css +3 -9
  121. package/dist/viewtoggle.layered.css +3 -9
  122. package/dist/wheelpicker.css +14 -59
  123. package/dist/wheelpicker.layered.css +14 -59
  124. package/package.json +4 -1
@@ -1,60 +1,119 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.badgeRecipe = void 0;
3
+ exports.spec = exports.badgeRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
- const config = (0, core_1.defineRecipe)({
6
+ const ring = (v, pct) => `inset 0 0 0 1px color-mix(in srgb, ${v} ${pct}%, transparent)`;
7
+ const soft = (v, pct) => `color-mix(in srgb, ${v} ${pct}%, transparent)`;
8
+ const spec = (0, core_1.defineSpec)({
7
9
  name: "badge",
8
- base: `display: inline-flex; align-items: center; justify-content: center; gap: ${tokens_1.contract.space["1"]}; font-family: ${tokens_1.contract.font.sans}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.1px; white-space: nowrap;
9
- & .crisp-badge-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: ${tokens_1.contract.radius.full}; background: currentColor; }`,
10
+ base: {
11
+ display: "inline-flex",
12
+ alignItems: "center",
13
+ justifyContent: "center",
14
+ gap: tokens_1.contract.space["1"],
15
+ fontFamily: tokens_1.contract.font.sans,
16
+ fontWeight: tokens_1.contract.weight.medium,
17
+ letterSpacing: "-0.1px",
18
+ whiteSpace: "nowrap",
19
+ },
20
+ parts: [
21
+ {
22
+ slot: "dot",
23
+ selector: "& .crisp-badge-dot",
24
+ decls: {
25
+ width: "6px",
26
+ height: "6px",
27
+ flexShrink: 0,
28
+ borderRadius: tokens_1.contract.radius.full,
29
+ background: "currentColor",
30
+ },
31
+ },
32
+ ],
10
33
  variants: {
11
34
  tone: {
12
- neutral: `background: ${tokens_1.contract.bgRaised}; color: ${tokens_1.contract.fgMuted}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault};`,
13
- brand: `background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 14%, transparent); color: ${tokens_1.contract.bgBrandSolid}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 30%, transparent);`,
14
- success: `background: color-mix(in srgb, ${tokens_1.contract.bgSuccess} 14%, transparent); color: ${tokens_1.contract.bgSuccess}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgSuccess} 30%, transparent);`,
15
- warning: `background: color-mix(in srgb, ${tokens_1.contract.bgWarning} 16%, transparent); color: ${tokens_1.contract.bgWarning}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgWarning} 32%, transparent);`,
16
- danger: `background: color-mix(in srgb, ${tokens_1.contract.bgDanger} 14%, transparent); color: ${tokens_1.contract.bgDanger}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgDanger} 30%, transparent);`,
35
+ neutral: {
36
+ background: tokens_1.contract.bgRaised,
37
+ color: tokens_1.contract.fgMuted,
38
+ boxShadow: `inset 0 0 0 1px ${tokens_1.contract.borderDefault}`,
39
+ },
40
+ brand: {
41
+ background: soft(tokens_1.contract.bgBrandSolid, 14),
42
+ color: tokens_1.contract.bgBrandSolid,
43
+ boxShadow: ring(tokens_1.contract.bgBrandSolid, 30),
44
+ },
45
+ success: {
46
+ background: soft(tokens_1.contract.bgSuccess, 14),
47
+ color: tokens_1.contract.bgSuccess,
48
+ boxShadow: ring(tokens_1.contract.bgSuccess, 30),
49
+ },
50
+ warning: {
51
+ background: soft(tokens_1.contract.bgWarning, 16),
52
+ color: tokens_1.contract.bgWarning,
53
+ boxShadow: ring(tokens_1.contract.bgWarning, 32),
54
+ },
55
+ danger: {
56
+ background: soft(tokens_1.contract.bgDanger, 14),
57
+ color: tokens_1.contract.bgDanger,
58
+ boxShadow: ring(tokens_1.contract.bgDanger, 30),
59
+ },
17
60
  },
18
61
  size: {
19
- small: `height: 16px; padding-inline: ${tokens_1.contract.space["1.5"]}; font-size: ${tokens_1.contract.text.xs}; border-radius: ${tokens_1.contract.radius.sm};`,
20
- medium: `height: 20px; padding-inline: ${tokens_1.contract.space["2"]}; font-size: ${tokens_1.contract.text.sm}; border-radius: ${tokens_1.contract.radius.sm};`,
21
- large: `height: ${tokens_1.contract.size.chip}; padding-inline: 9px; font-size: ${tokens_1.contract.text.lg}; border-radius: ${tokens_1.contract.radius.chip};`,
22
- },
23
- variant: {
24
- soft: ``,
25
- solid: ``,
62
+ small: {
63
+ height: "16px",
64
+ paddingInline: tokens_1.contract.space["1.5"],
65
+ fontSize: tokens_1.contract.text.xs,
66
+ borderRadius: tokens_1.contract.radius.sm,
67
+ },
68
+ medium: {
69
+ height: "20px",
70
+ paddingInline: tokens_1.contract.space["2"],
71
+ fontSize: tokens_1.contract.text.sm,
72
+ borderRadius: tokens_1.contract.radius.sm,
73
+ },
74
+ large: {
75
+ height: tokens_1.contract.size.chip,
76
+ paddingInline: "9px",
77
+ fontSize: tokens_1.contract.text.lg,
78
+ borderRadius: tokens_1.contract.radius.chip,
79
+ },
26
80
  },
81
+ variant: { soft: {}, solid: {} },
27
82
  },
28
83
  defaultVariants: { tone: "neutral", size: "medium", variant: "soft" },
29
84
  compoundVariants: [
30
85
  {
31
86
  variant: "solid",
32
87
  tone: "brand",
33
- css: `background: ${tokens_1.contract.bgBrandSolid}; color: ${tokens_1.contract.fgOnBrand}; box-shadow: none;`,
88
+ decls: {
89
+ background: tokens_1.contract.bgBrandSolid,
90
+ color: tokens_1.contract.fgOnBrand,
91
+ boxShadow: "none",
92
+ },
34
93
  },
35
94
  {
36
95
  variant: "solid",
37
96
  tone: "success",
38
- css: `background: ${tokens_1.contract.bgSuccess}; color: ${tokens_1.contract.fgOnTone}; box-shadow: none;`,
97
+ decls: { background: tokens_1.contract.bgSuccess, color: tokens_1.contract.fgOnTone, boxShadow: "none" },
39
98
  },
40
99
  {
41
100
  variant: "solid",
42
101
  tone: "warning",
43
- css: `background: ${tokens_1.contract.bgWarning}; color: ${tokens_1.contract.fgOnTone}; box-shadow: none;`,
102
+ decls: { background: tokens_1.contract.bgWarning, color: tokens_1.contract.fgOnTone, boxShadow: "none" },
44
103
  },
45
104
  {
46
105
  variant: "solid",
47
106
  tone: "danger",
48
- css: `background: ${tokens_1.contract.bgDanger}; color: ${tokens_1.contract.fgOnTone}; box-shadow: none;`,
107
+ decls: { background: tokens_1.contract.bgDanger, color: tokens_1.contract.fgOnTone, boxShadow: "none" },
49
108
  },
50
109
  {
51
110
  variant: "solid",
52
111
  tone: "neutral",
53
- css: `background: ${tokens_1.contract.fgMuted}; color: ${tokens_1.contract.bgSurface}; box-shadow: none;`,
112
+ decls: { background: tokens_1.contract.fgMuted, color: tokens_1.contract.bgSurface, boxShadow: "none" },
54
113
  },
55
114
  ],
56
115
  });
57
- exports.badgeRecipe = Object.assign((0, core_1.resolveRecipe)(config), {
58
- config,
59
- });
116
+ exports.spec = spec;
117
+ const config = (0, core_1.specToRecipe)(spec);
118
+ exports.badgeRecipe = Object.assign((0, core_1.resolveRecipe)(config), { config });
60
119
  exports.default = config;
@@ -1,26 +1,99 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buttonRecipe = void 0;
3
+ exports.spec = exports.buttonRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
- const config = (0, core_1.defineRecipe)({
6
+ const spec = (0, core_1.defineSpec)({
7
7
  name: "button",
8
- base: `display: inline-flex; align-items: center; justify-content: center; gap: ${tokens_1.contract.space["1.5"]}; font-family: ${tokens_1.contract.font.sans}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.14px; line-height: 20px; white-space: nowrap; border: none; cursor: pointer; transition: background-color .2s, box-shadow .2s, color .2s; &:disabled { opacity: 0.5; cursor: not-allowed; }`,
8
+ base: {
9
+ display: "inline-flex",
10
+ alignItems: "center",
11
+ justifyContent: "center",
12
+ gap: tokens_1.contract.space["1.5"],
13
+ fontFamily: tokens_1.contract.font.sans,
14
+ fontWeight: tokens_1.contract.weight.medium,
15
+ letterSpacing: "-0.14px",
16
+ lineHeight: "20px",
17
+ whiteSpace: "nowrap",
18
+ border: "none",
19
+ cursor: "pointer",
20
+ transition: "background-color .2s, box-shadow .2s, color .2s",
21
+ },
22
+ parts: [
23
+ { selector: "&:disabled", decls: { opacity: 0.5, cursor: "not-allowed" } },
24
+ ],
9
25
  variants: {
10
26
  intent: {
11
- primary: `background: ${tokens_1.contract.bgBrandSolid}; color: ${tokens_1.contract.fgOnBrand}; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.07), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); &:hover { background: ${tokens_1.contract.bgBrandSolidPressed}; } &:active { background: ${tokens_1.contract.bgBrandSolidPressed}; }`,
12
- neutral: `background: ${tokens_1.contract.bgSurface}; color: ${tokens_1.contract.fgPrimary}; box-shadow: ${tokens_1.elevation.field}; &:hover { background: ${tokens_1.contract.bgRaised}; }`,
13
- danger: `background: ${tokens_1.contract.bgDangerSolid}; color: ${tokens_1.contract.fgOnBrand}; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.07), 0 2px 4px -2px rgba(186,37,37,0.20), 0 3px 6px -2px rgba(186,37,37,0.12); &:hover { filter: brightness(0.96); } &:active { filter: brightness(0.92); }`,
14
- ghost: `background: transparent; color: ${tokens_1.contract.fgMuted}; box-shadow: none; &:hover { background: ${tokens_1.contract.bgRaised}; color: ${tokens_1.contract.fgPrimary}; }`,
27
+ primary: {
28
+ decls: {
29
+ background: tokens_1.contract.bgBrandSolid,
30
+ color: tokens_1.contract.fgOnBrand,
31
+ boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.07), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08)",
32
+ },
33
+ parts: [
34
+ { selector: "&:hover", decls: { background: tokens_1.contract.bgBrandSolidPressed } },
35
+ {
36
+ selector: "&:active",
37
+ decls: { background: tokens_1.contract.bgBrandSolidPressed },
38
+ },
39
+ ],
40
+ },
41
+ neutral: {
42
+ decls: {
43
+ background: tokens_1.contract.bgSurface,
44
+ color: tokens_1.contract.fgPrimary,
45
+ boxShadow: tokens_1.elevation.field,
46
+ },
47
+ parts: [{ selector: "&:hover", decls: { background: tokens_1.contract.bgRaised } }],
48
+ },
49
+ danger: {
50
+ decls: {
51
+ background: tokens_1.contract.bgDangerSolid,
52
+ color: tokens_1.contract.fgOnBrand,
53
+ boxShadow: "inset 0 0 0 1px rgba(255,255,255,0.07), 0 2px 4px -2px rgba(186,37,37,0.20), 0 3px 6px -2px rgba(186,37,37,0.12)",
54
+ },
55
+ parts: [
56
+ { selector: "&:hover", decls: { filter: "brightness(0.96)" } },
57
+ { selector: "&:active", decls: { filter: "brightness(0.92)" } },
58
+ ],
59
+ },
60
+ ghost: {
61
+ decls: {
62
+ background: "transparent",
63
+ color: tokens_1.contract.fgMuted,
64
+ boxShadow: "none",
65
+ },
66
+ parts: [
67
+ {
68
+ selector: "&:hover",
69
+ decls: { background: tokens_1.contract.bgRaised, color: tokens_1.contract.fgPrimary },
70
+ },
71
+ ],
72
+ },
15
73
  },
16
74
  size: {
17
- sm: `height: 24px; padding-inline: ${tokens_1.contract.space["1.5"]}; font-size: ${tokens_1.contract.text.md}; border-radius: ${tokens_1.contract.radius.md};`,
18
- md: `height: 28px; padding-inline: ${tokens_1.contract.space["2"]}; font-size: ${tokens_1.contract.text.lg}; border-radius: ${tokens_1.contract.radius.md};`,
19
- lg: `height: 40px; padding-inline: 14px; font-size: ${tokens_1.contract.text.lg}; border-radius: 10px;`,
75
+ sm: {
76
+ height: "24px",
77
+ paddingInline: tokens_1.contract.space["1.5"],
78
+ fontSize: tokens_1.contract.text.md,
79
+ borderRadius: tokens_1.contract.radius.md,
80
+ },
81
+ md: {
82
+ height: "28px",
83
+ paddingInline: tokens_1.contract.space["2"],
84
+ fontSize: tokens_1.contract.text.lg,
85
+ borderRadius: tokens_1.contract.radius.md,
86
+ },
87
+ lg: {
88
+ height: "40px",
89
+ paddingInline: "14px",
90
+ fontSize: tokens_1.contract.text.lg,
91
+ borderRadius: "10px",
92
+ },
20
93
  },
21
94
  shape: {
22
- text: ``,
23
- icon: `padding-inline: 0; aspect-ratio: 1 / 1;`,
95
+ text: {},
96
+ icon: { paddingInline: 0, aspectRatio: "1 / 1" },
24
97
  },
25
98
  // Opt-in hit-area expansion (crisp/#62): none of sm/md/lg reaches the
26
99
  // 44x44 touch-target minimum (Apple HIG / Material), and enlarging the
@@ -30,8 +103,27 @@ const config = (0, core_1.defineRecipe)({
30
103
  // Opt-in rather than always-on: an always-on overlay would swallow
31
104
  // clicks meant for a neighbour in a toolbar packed tighter than 44px.
32
105
  touchTarget: {
33
- true: `position: relative; &::after { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; height: 100%; min-width: 44px; min-height: 44px; }`,
34
- false: ``,
106
+ true: {
107
+ decls: { position: "relative" },
108
+ parts: [
109
+ {
110
+ slot: "hitArea",
111
+ selector: "&::after",
112
+ decls: {
113
+ content: '""',
114
+ position: "absolute",
115
+ top: "50%",
116
+ left: "50%",
117
+ transform: "translate(-50%, -50%)",
118
+ width: "100%",
119
+ height: "100%",
120
+ minWidth: "44px",
121
+ minHeight: "44px",
122
+ },
123
+ },
124
+ ],
125
+ },
126
+ false: {},
35
127
  },
36
128
  },
37
129
  defaultVariants: {
@@ -40,9 +132,8 @@ const config = (0, core_1.defineRecipe)({
40
132
  shape: "text",
41
133
  touchTarget: false,
42
134
  },
43
- compoundVariants: [],
44
- });
45
- exports.buttonRecipe = Object.assign((0, core_1.resolveRecipe)(config), {
46
- config,
47
135
  });
136
+ exports.spec = spec;
137
+ const config = (0, core_1.specToRecipe)(spec);
138
+ exports.buttonRecipe = Object.assign((0, core_1.resolveRecipe)(config), { config });
48
139
  exports.default = config;
@@ -1,17 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dividerRecipe = void 0;
3
+ exports.spec = exports.dividerRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
6
  // A hairline separator — horizontal (default) or vertical.
7
- const config = (0, core_1.defineRecipe)({
7
+ const spec = (0, core_1.defineSpec)({
8
8
  name: "divider",
9
- base: `flex-shrink: 0; border: none; margin: 0; background: ${tokens_1.contract.borderSubtle};
10
- &[data-orientation="horizontal"] { width: 100%; height: 1px; }
11
- &[data-orientation="vertical"] { width: 1px; align-self: stretch; min-height: 1em; }`,
12
- variants: {},
13
- defaultVariants: {},
14
- compoundVariants: [],
9
+ base: {
10
+ flexShrink: 0,
11
+ border: "none",
12
+ margin: 0,
13
+ background: tokens_1.contract.borderSubtle,
14
+ },
15
+ parts: [
16
+ {
17
+ selector: '&[data-orientation="horizontal"]',
18
+ decls: { width: "100%", height: "1px" },
19
+ },
20
+ {
21
+ selector: '&[data-orientation="vertical"]',
22
+ decls: { width: "1px", alignSelf: "stretch", minHeight: "1em" },
23
+ },
24
+ ],
15
25
  });
26
+ exports.spec = spec;
27
+ const config = (0, core_1.specToRecipe)(spec);
16
28
  exports.dividerRecipe = (0, core_1.resolveRecipe)(config);
17
29
  exports.default = config;
@@ -1,20 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.progressRecipe = void 0;
3
+ exports.spec = exports.progressRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
6
  // A progress indicator — a continuous brand-filled track, or a segmented row of
7
7
  // pills (a "Getting started 3/6" bar).
8
- const config = (0, core_1.defineRecipe)({
8
+ const spec = (0, core_1.defineSpec)({
9
9
  name: "progress",
10
- base: `display: block; box-sizing: border-box; height: 6px; border-radius: ${tokens_1.contract.radius.full}; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent); overflow: hidden;
11
- & .crisp-progress-fill { display: block; height: 100%; border-radius: ${tokens_1.contract.radius.full}; background: ${tokens_1.contract.bgBrandSolid}; transition: width 0.25s ease; }
12
- &[data-segmented] { display: flex; gap: ${tokens_1.contract.space["1"]}; background: transparent; overflow: visible; }
13
- &[data-segmented] .crisp-progress-seg { flex: 1; height: 100%; border-radius: ${tokens_1.contract.radius.full}; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent); transition: background 0.2s; }
14
- &[data-segmented] .crisp-progress-seg[data-filled] { background: ${tokens_1.contract.bgBrandSolid}; }`,
15
- variants: {},
16
- defaultVariants: {},
17
- compoundVariants: [],
10
+ base: {
11
+ display: "block",
12
+ boxSizing: "border-box",
13
+ height: "6px",
14
+ borderRadius: tokens_1.contract.radius.full,
15
+ background: `color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent)`,
16
+ overflow: "hidden",
17
+ },
18
+ parts: [
19
+ {
20
+ slot: "fill",
21
+ selector: "& .crisp-progress-fill",
22
+ decls: {
23
+ display: "block",
24
+ height: "100%",
25
+ borderRadius: tokens_1.contract.radius.full,
26
+ background: tokens_1.contract.bgBrandSolid,
27
+ transition: "width 0.25s ease",
28
+ },
29
+ },
30
+ {
31
+ selector: "&[data-segmented]",
32
+ decls: {
33
+ display: "flex",
34
+ gap: tokens_1.contract.space["1"],
35
+ background: "transparent",
36
+ overflow: "visible",
37
+ },
38
+ },
39
+ {
40
+ slot: "seg",
41
+ selector: "&[data-segmented] .crisp-progress-seg",
42
+ decls: {
43
+ flex: 1,
44
+ height: "100%",
45
+ borderRadius: tokens_1.contract.radius.full,
46
+ background: `color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent)`,
47
+ transition: "background 0.2s",
48
+ },
49
+ },
50
+ {
51
+ slot: "seg",
52
+ selector: "&[data-segmented] .crisp-progress-seg[data-filled]",
53
+ decls: { background: tokens_1.contract.bgBrandSolid },
54
+ },
55
+ ],
18
56
  });
57
+ exports.spec = spec;
58
+ const config = (0, core_1.specToRecipe)(spec);
19
59
  exports.progressRecipe = (0, core_1.resolveRecipe)(config);
20
60
  exports.default = config;
@@ -1,16 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.spinnerRecipe = void 0;
3
+ exports.spec = exports.spinnerRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
6
  // A spinning ring loading indicator.
7
- const config = (0, core_1.defineRecipe)({
7
+ const spec = (0, core_1.defineSpec)({
8
8
  name: "spinner",
9
- globalCss: `@keyframes crisp-spin { to { transform: rotate(360deg); } }`,
10
- base: `display: inline-block; box-sizing: border-box; width: 16px; height: 16px; border-radius: ${tokens_1.contract.radius.full}; border: 2px solid color-mix(in srgb, ${tokens_1.contract.fgPrimary} 15%, transparent); border-top-color: ${tokens_1.contract.fgMuted}; animation: crisp-spin 0.6s linear infinite;`,
11
- variants: {},
12
- defaultVariants: {},
13
- compoundVariants: [],
9
+ globalCss: "@keyframes crisp-spin { to { transform: rotate(360deg); } }",
10
+ base: {
11
+ display: "inline-block",
12
+ boxSizing: "border-box",
13
+ width: "16px",
14
+ height: "16px",
15
+ borderRadius: tokens_1.contract.radius.full,
16
+ border: `2px solid color-mix(in srgb, ${tokens_1.contract.fgPrimary} 15%, transparent)`,
17
+ borderTopColor: tokens_1.contract.fgMuted,
18
+ animation: "crisp-spin 0.6s linear infinite",
19
+ },
14
20
  });
21
+ exports.spec = spec;
22
+ const config = (0, core_1.specToRecipe)(spec);
15
23
  exports.spinnerRecipe = (0, core_1.resolveRecipe)(config);
16
24
  exports.default = config;
@@ -1,28 +1,58 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.switchRecipe = void 0;
3
+ exports.spec = exports.switchRecipe = void 0;
4
4
  const core_1 = require("../../core");
5
5
  const tokens_1 = require("../../tokens");
6
6
  // A settings toggle, measured from the live app: a 24×16 pill track that
7
7
  // flips from a muted grey to brand blue (#266df0), with a 12px white thumb
8
8
  // (2px inset) that slides 8px across. background + transform both animate .2s.
9
- const config = (0, core_1.defineRecipe)({
9
+ const spec = (0, core_1.defineSpec)({
10
10
  name: "switch",
11
- base: `
12
- display: inline-flex; align-items: center; flex-shrink: 0; width: 24px; height: 16px; padding: 0; margin: 0;
13
- border: none; border-radius: ${tokens_1.contract.radius.full}; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 9%, transparent);
14
- cursor: pointer; transition: background-color .2s ease; -webkit-tap-highlight-color: transparent;
15
- & .crisp-switch-thumb { width: 12px; height: 12px; margin: 0 2px; border-radius: ${tokens_1.contract.radius.full}; background: #fff; transition: transform .2s ease; }
16
- &[data-checked] { background: ${tokens_1.contract.bgBrandSolid}; }
17
- &[data-checked] .crisp-switch-thumb { transform: translateX(8px); }
18
- &:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 2px; }
19
- &:disabled { opacity: 0.5; cursor: not-allowed; }
20
- `,
21
- variants: {},
22
- defaultVariants: {},
23
- compoundVariants: [],
24
- });
25
- exports.switchRecipe = Object.assign((0, core_1.resolveRecipe)(config), {
26
- config,
11
+ base: {
12
+ display: "inline-flex",
13
+ alignItems: "center",
14
+ flexShrink: 0,
15
+ width: "24px",
16
+ height: "16px",
17
+ padding: 0,
18
+ margin: 0,
19
+ border: "none",
20
+ borderRadius: tokens_1.contract.radius.full,
21
+ background: `color-mix(in srgb, ${tokens_1.contract.fgPrimary} 9%, transparent)`,
22
+ cursor: "pointer",
23
+ transition: "background-color .2s ease",
24
+ WebkitTapHighlightColor: "transparent",
25
+ },
26
+ parts: [
27
+ {
28
+ slot: "thumb",
29
+ selector: "& .crisp-switch-thumb",
30
+ decls: {
31
+ width: "12px",
32
+ height: "12px",
33
+ margin: "0 2px",
34
+ borderRadius: tokens_1.contract.radius.full,
35
+ background: "#fff",
36
+ transition: "transform .2s ease",
37
+ },
38
+ },
39
+ { selector: "&[data-checked]", decls: { background: tokens_1.contract.bgBrandSolid } },
40
+ {
41
+ slot: "thumb",
42
+ selector: "&[data-checked] .crisp-switch-thumb",
43
+ decls: { transform: "translateX(8px)" },
44
+ },
45
+ {
46
+ selector: "&:focus-visible",
47
+ decls: {
48
+ outline: `2px solid ${tokens_1.contract.bgBrandSolid}`,
49
+ outlineOffset: "2px",
50
+ },
51
+ },
52
+ { selector: "&:disabled", decls: { opacity: 0.5, cursor: "not-allowed" } },
53
+ ],
27
54
  });
55
+ exports.spec = spec;
56
+ const config = (0, core_1.specToRecipe)(spec);
57
+ exports.switchRecipe = Object.assign((0, core_1.resolveRecipe)(config), { config });
28
58
  exports.default = config;
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.usePrefersReducedMotion = exports.useLabelAssociation = exports.useFormReset = exports.useFocusAfterRemoval = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.formValueStyle = exports.isRtlElement = exports.withDescribedBy = exports.defineRecipe = exports.cx = void 0;
17
+ exports.usePrefersReducedMotion = exports.useLabelAssociation = exports.useFormReset = exports.useFocusAfterRemoval = exports.specToRecipe = exports.defineSpec = exports.resolveRecipe = exports.Primitive = exports.placeFloating = exports.formValueStyle = exports.isRtlElement = exports.withDescribedBy = exports.defineRecipe = exports.cx = void 0;
18
18
  var cx_1 = require("./cx");
19
19
  Object.defineProperty(exports, "cx", { enumerable: true, get: function () { return cx_1.cx; } });
20
20
  var define_1 = require("./define");
@@ -32,6 +32,9 @@ Object.defineProperty(exports, "Primitive", { enumerable: true, get: function ()
32
32
  var resolve_1 = require("./resolve");
33
33
  Object.defineProperty(exports, "resolveRecipe", { enumerable: true, get: function () { return resolve_1.resolveRecipe; } });
34
34
  __exportStar(require("./rtl"), exports);
35
+ var spec_1 = require("./spec");
36
+ Object.defineProperty(exports, "defineSpec", { enumerable: true, get: function () { return spec_1.defineSpec; } });
37
+ Object.defineProperty(exports, "specToRecipe", { enumerable: true, get: function () { return spec_1.specToRecipe; } });
35
38
  var use_focus_after_removal_1 = require("./use-focus-after-removal");
36
39
  Object.defineProperty(exports, "useFocusAfterRemoval", { enumerable: true, get: function () { return use_focus_after_removal_1.useFocusAfterRemoval; } });
37
40
  var use_form_reset_1 = require("./use-form-reset");
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.declsToCss = declsToCss;
4
+ exports.defineSpec = defineSpec;
5
+ exports.specToRecipe = specToRecipe;
6
+ const kebab = (p) => p.startsWith("--") ? p : p.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
7
+ /** `{ flexShrink: 0 }` -> `flex-shrink: 0;`, in insertion order. */
8
+ function declsToCss(decls) {
9
+ return Object.entries(decls)
10
+ .map(([prop, value]) => `${kebab(prop)}: ${value};`)
11
+ .join(" ");
12
+ }
13
+ /**
14
+ * `const` type parameter, not a plain `ComponentSpec` return: widening the
15
+ * variant keys here loses the literal union that `VariantProps<typeof recipe>`
16
+ * hands a component, and `BadgeProps["tone"]` silently degrades from
17
+ * `"neutral" | "brand" | …` to `string`. That is a public API regression that
18
+ * typechecks everywhere except at the call sites it was meant to protect.
19
+ */
20
+ function defineSpec(spec) {
21
+ return spec;
22
+ }
23
+ /**
24
+ * Compile a spec into the RecipeConfig the existing pipeline already takes, so
25
+ * nothing downstream — the CSS generator, the d.ts generator, `resolveRecipe`,
26
+ * the spec guards — has to know a spec exists. That is what keeps the
27
+ * conversion provable: `dist/` must not move, and CI's
28
+ * `git diff --exit-code dist/` is the gate.
29
+ *
30
+ * The formatting here is not cosmetic freedom. It reproduces exactly what the
31
+ * hand-written template strings emit — root declarations space-joined, each
32
+ * nested rule on its own line indented four spaces — because any other choice
33
+ * rewrites every committed stylesheet and buries the conversion's real diff.
34
+ */
35
+ function specToRecipe(spec) {
36
+ const root = declsToCss(spec.base);
37
+ const nested = (spec.parts ?? []).map((p) => `${p.selector} { ${declsToCss(p.decls)} }`);
38
+ const base = nested.length ? `${root}\n ${nested.join("\n ")}` : root;
39
+ // A variant value compiles the same way the root does: declarations first,
40
+ // then any nested rules it contributes, so a variant that restyles a part
41
+ // (Badge's solid dot) stays in the variant rather than leaking into base.
42
+ const compileValue = (v) => {
43
+ const decls = "decls" in v ? v.decls : v;
44
+ const nestedParts = "decls" in v ? (v.parts ?? []) : [];
45
+ const head = declsToCss(decls);
46
+ if (!nestedParts.length)
47
+ return head;
48
+ const tail = nestedParts
49
+ .map((p) => `${p.selector} { ${declsToCss(p.decls)} }`)
50
+ .join("\n ");
51
+ return head ? `${head}\n ${tail}` : tail;
52
+ };
53
+ const variants = {};
54
+ for (const [name, values] of Object.entries(spec.variants ?? {})) {
55
+ variants[name] = {};
56
+ for (const [value, v] of Object.entries(values)) {
57
+ variants[name][value] = compileValue(v);
58
+ }
59
+ }
60
+ return {
61
+ name: spec.name,
62
+ base,
63
+ variants: variants,
64
+ defaultVariants: (spec.defaultVariants ?? {}),
65
+ compoundVariants: (spec.compoundVariants ?? []).map(({ decls, ...sel }) => ({
66
+ ...sel,
67
+ css: declsToCss(decls),
68
+ })),
69
+ ...(spec.globalCss ? { globalCss: spec.globalCss } : {}),
70
+ };
71
+ }
@@ -1,11 +1,7 @@
1
1
  .crisp-columncalc {
2
- display: flex; align-items: stretch; width: 100%; height: 100%; font-family: var(--crisp-font-sans);
3
- /* Menu wraps its trigger in a flex div that sizes to content, which would
4
- shrink the button back to its text. Make the wrapper fill too, so the
5
- whole cell stays clickable. */
2
+ display: flex; align-items: stretch; width: 100%; height: 100%; font-family: var(--crisp-font-sans); /* Menu wraps its trigger in a flex div that sizes to content, which would shrink the button back to its text. Make the wrapper fill too, so the whole cell stays clickable. */
6
3
  & .crisp-menu { width: 100%; height: 100%; }
7
- /* No radius: the trigger fills the cell, so rounding its own corners
8
- leaves the cell's background showing through at the four corners as
4
+ /* No radius: the trigger fills the cell, so rounding its own corners leaves the cell's background showing through at the four corners as
9
5
  soon as the hover tint paints. Everything else that fills a grid cell
10
6
  agrees -- recordstable-sum-btn, the same summary trigger, declares no
11
7
  radius, and datatable-grip and the embedded pickers zero theirs. An 8px
@@ -1,12 +1,8 @@
1
1
  @layer crisp-components {
2
2
  .crisp-columncalc {
3
- display: flex; align-items: stretch; width: 100%; height: 100%; font-family: var(--crisp-font-sans);
4
- /* Menu wraps its trigger in a flex div that sizes to content, which would
5
- shrink the button back to its text. Make the wrapper fill too, so the
6
- whole cell stays clickable. */
3
+ display: flex; align-items: stretch; width: 100%; height: 100%; font-family: var(--crisp-font-sans); /* Menu wraps its trigger in a flex div that sizes to content, which would shrink the button back to its text. Make the wrapper fill too, so the whole cell stays clickable. */
7
4
  & .crisp-menu { width: 100%; height: 100%; }
8
- /* No radius: the trigger fills the cell, so rounding its own corners
9
- leaves the cell's background showing through at the four corners as
5
+ /* No radius: the trigger fills the cell, so rounding its own corners leaves the cell's background showing through at the four corners as
10
6
  soon as the hover tint paints. Everything else that fills a grid cell
11
7
  agrees -- recordstable-sum-btn, the same summary trigger, declares no
12
8
  radius, and datatable-grip and the embedded pickers zero theirs. An 8px