@sixthshift/design-system 0.8.0 → 0.9.0

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 (123) hide show
  1. package/README.md +5 -4
  2. package/dist/components/Badge/Badge.d.ts +17 -35
  3. package/dist/components/Badge/Badge.d.ts.map +1 -1
  4. package/dist/components/Badge/Badge.js +19 -22
  5. package/dist/components/Badge/Badge.js.map +1 -1
  6. package/dist/components/Badge/index.d.ts +1 -1
  7. package/dist/components/Badge/index.d.ts.map +1 -1
  8. package/dist/components/Badge/index.js +1 -1
  9. package/dist/components/Badge/index.js.map +1 -1
  10. package/dist/components/Button/Button.d.ts +23 -41
  11. package/dist/components/Button/Button.d.ts.map +1 -1
  12. package/dist/components/Button/Button.js +28 -35
  13. package/dist/components/Button/Button.js.map +1 -1
  14. package/dist/components/Button/index.d.ts +1 -1
  15. package/dist/components/Button/index.d.ts.map +1 -1
  16. package/dist/components/Button/index.js +1 -1
  17. package/dist/components/Button/index.js.map +1 -1
  18. package/dist/components/Card/Card.d.ts +2 -2
  19. package/dist/components/Card/Card.js +1 -1
  20. package/dist/components/Card/Card.js.map +1 -1
  21. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts +20 -16
  22. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
  23. package/dist/components/CheckboxGroup/CheckboxGroup.js +12 -10
  24. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  25. package/dist/components/Code/Workspace/Toolbar.d.ts +2 -1
  26. package/dist/components/Code/Workspace/Toolbar.d.ts.map +1 -1
  27. package/dist/components/Code/Workspace/Toolbar.js.map +1 -1
  28. package/dist/components/FormField/FormField.d.ts +3 -1
  29. package/dist/components/FormField/FormField.d.ts.map +1 -1
  30. package/dist/components/FormField/FormField.js.map +1 -1
  31. package/dist/components/Input/Input.d.ts +3 -3
  32. package/dist/components/Input/Input.js +3 -3
  33. package/dist/components/Markdown/Markdown.js +1 -1
  34. package/dist/components/Markdown/Markdown.js.map +1 -1
  35. package/dist/components/NumberStepper/NumberStepper.d.ts +1 -1
  36. package/dist/components/ProgressBar/ProgressBar.d.ts +11 -0
  37. package/dist/components/ProgressBar/ProgressBar.d.ts.map +1 -1
  38. package/dist/components/ProgressBar/ProgressBar.js +6 -2
  39. package/dist/components/ProgressBar/ProgressBar.js.map +1 -1
  40. package/dist/components/ProgressBar/index.d.ts +1 -1
  41. package/dist/components/ProgressBar/index.d.ts.map +1 -1
  42. package/dist/components/ProgressBar/index.js.map +1 -1
  43. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts +19 -16
  44. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
  45. package/dist/components/RadioButtonGroup/RadioButtonGroup.js +13 -12
  46. package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
  47. package/dist/components/SearchInput/SearchInput.d.ts +3 -2
  48. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  49. package/dist/components/SearchInput/SearchInput.js +13 -8
  50. package/dist/components/SearchInput/SearchInput.js.map +1 -1
  51. package/dist/components/Sparkline/Sparkline.d.ts +8 -4
  52. package/dist/components/Sparkline/Sparkline.d.ts.map +1 -1
  53. package/dist/components/Sparkline/Sparkline.js +1 -1
  54. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  55. package/dist/components/Sparkline/index.d.ts +1 -1
  56. package/dist/components/Sparkline/index.d.ts.map +1 -1
  57. package/dist/components/Sparkline/index.js.map +1 -1
  58. package/dist/components/TagChip/TagChip.d.ts +0 -13
  59. package/dist/components/TagChip/TagChip.d.ts.map +1 -1
  60. package/dist/components/TagChip/TagChip.js +10 -4
  61. package/dist/components/TagChip/TagChip.js.map +1 -1
  62. package/dist/components/TextInline/TextInline.d.ts +2 -2
  63. package/dist/components/Toast/Toast.js +1 -1
  64. package/dist/components/Toast/Toast.js.map +1 -1
  65. package/dist/components/Toggle/Toggle.d.ts +29 -18
  66. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  67. package/dist/components/Toggle/Toggle.js +48 -11
  68. package/dist/components/Toggle/Toggle.js.map +1 -1
  69. package/dist/components/Toggle/index.d.ts +1 -1
  70. package/dist/components/Toggle/index.d.ts.map +1 -1
  71. package/dist/components/Toggle/index.js +1 -1
  72. package/dist/components/Toggle/index.js.map +1 -1
  73. package/dist/components/ToggleGroup/ToggleGroup.d.ts +4 -4
  74. package/dist/components/ToggleGroup/ToggleGroup.js +4 -4
  75. package/dist/components/ToggleGroup/ToggleGroupItem.d.ts +1 -1
  76. package/dist/components/ToggleGroup/ToggleGroupItem.d.ts.map +1 -1
  77. package/dist/components/ToggleGroup/ToggleGroupItem.js +37 -7
  78. package/dist/components/ToggleGroup/ToggleGroupItem.js.map +1 -1
  79. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +3 -3
  80. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +3 -3
  81. package/dist/components/ToggleGroup/index.d.ts +1 -1
  82. package/dist/components/ToggleGroup/index.d.ts.map +1 -1
  83. package/dist/components/ToggleGroup/toggleGroup.types.d.ts +18 -16
  84. package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
  85. package/dist/typography/Code.js +1 -1
  86. package/dist/typography/Code.js.map +1 -1
  87. package/package.json +2 -1
  88. package/src/components/Badge/Badge.tsx +38 -45
  89. package/src/components/Badge/badge.recipe.css +20 -101
  90. package/src/components/Badge/index.ts +0 -1
  91. package/src/components/Button/Button.tsx +57 -51
  92. package/src/components/Button/button.recipe.css +39 -122
  93. package/src/components/Button/index.ts +0 -1
  94. package/src/components/Card/Card.tsx +1 -1
  95. package/src/components/CheckboxGroup/CheckboxGroup.tsx +17 -15
  96. package/src/components/Code/Workspace/Toolbar.tsx +2 -2
  97. package/src/components/FormField/FormField.tsx +3 -2
  98. package/src/components/Input/Input.tsx +3 -3
  99. package/src/components/Markdown/Markdown.tsx +1 -1
  100. package/src/components/Message/message.recipe.css +13 -28
  101. package/src/components/ProgressBar/ProgressBar.tsx +16 -4
  102. package/src/components/ProgressBar/index.ts +1 -1
  103. package/src/components/ProgressBar/progress-bar.recipe.css +29 -0
  104. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +18 -17
  105. package/src/components/SearchInput/SearchInput.tsx +34 -20
  106. package/src/components/SearchInput/search-input.recipe.css +14 -6
  107. package/src/components/Sparkline/Sparkline.tsx +9 -4
  108. package/src/components/Sparkline/index.ts +1 -1
  109. package/src/components/TagChip/TagChip.tsx +17 -8
  110. package/src/components/TagChip/tag-chip.recipe.css +12 -12
  111. package/src/components/Toast/Toast.tsx +1 -1
  112. package/src/components/Toggle/Toggle.tsx +67 -21
  113. package/src/components/Toggle/index.ts +9 -1
  114. package/src/components/Toggle/toggle.recipe.css +45 -73
  115. package/src/components/ToggleGroup/ToggleGroup.tsx +4 -4
  116. package/src/components/ToggleGroup/ToggleGroupItem.tsx +46 -11
  117. package/src/components/ToggleGroup/index.ts +9 -1
  118. package/src/components/ToggleGroup/toggle-group.recipe.css +62 -0
  119. package/src/components/ToggleGroup/toggleGroup.types.ts +18 -16
  120. package/src/theme/linen/theme.css +21 -0
  121. package/src/theming/intents.css +112 -0
  122. package/src/theming/tailwind.css +17 -1
  123. package/src/typography/Code.tsx +1 -1
@@ -1,8 +1,16 @@
1
1
  import type * as React from "react";
2
- import type { ButtonIntentName, ButtonVariantName } from "../Button/Button.js";
3
- import type { ToggleProps } from "../Toggle/Toggle.js";
4
- /** Extract a prop's union type from ToggleProps, stripping null added by CVA */
5
- type ToggleProp<K extends keyof ToggleProps> = NonNullable<ToggleProps[K]>;
2
+ /**
3
+ * ToggleGroup's own axes. They match Toggle's and Button's, deliberately
4
+ * duplicated rather than imported: a group of toggle buttons is its own
5
+ * component, and importing it must not pull in either.
6
+ */
7
+ type Loose<T extends string> = T | (string & {});
8
+ export type ToggleGroupVariantName = "solid" | "outline" | "ghost";
9
+ export type ToggleGroupIntentName = "neutral" | "brand" | "danger" | "success" | "warning";
10
+ export type ToggleGroupVariant = Loose<ToggleGroupVariantName>;
11
+ export type ToggleGroupIntent = Loose<ToggleGroupIntentName>;
12
+ /** `xl` is left out — too large for grouped toggles. */
13
+ export type ToggleGroupSize = "xs" | "sm" | "md" | "lg";
6
14
  export type ToggleGroupOption = {
7
15
  /** Unique value for the option */
8
16
  value: string;
@@ -20,24 +28,18 @@ export type ToggleGroupBaseProps = Omit<React.HTMLAttributes<HTMLDivElement>, "o
20
28
  appearance?: "segmented" | "separate";
21
29
  /** Layout orientation */
22
30
  orientation?: "vertical" | "horizontal";
23
- /**
24
- * Button variant (link excluded as it has no clear pressed state in groups).
25
- *
26
- * Deliberately the closed union, not Button's widened `ButtonVariant`: an
27
- * `Exclude` cannot remove `link` from a type that already admits any string,
28
- * so narrowing here requires narrowing from the closed names.
29
- */
30
- variant?: Exclude<ButtonVariantName, "link">;
31
- /** Color intent */
32
- intent?: ButtonIntentName;
31
+ /** The items' treatment. */
32
+ variant?: ToggleGroupVariant;
33
+ /** Colour intent — widened, so a consumer-defined intent reaches the group. */
34
+ intent?: ToggleGroupIntent;
33
35
  /**
34
36
  * Name of a brand the theme defines. Rendered once, as `data-brand` on the
35
37
  * group element, so every item inherits it. Omitted, the group follows the
36
38
  * nearest `data-brand` ancestor.
37
39
  */
38
40
  brand?: string | undefined;
39
- /** Button size (xl excluded — too large for grouped toggles) */
40
- size?: Exclude<ToggleProp<"size">, "xl">;
41
+ /** Item size (xl excluded — too large for grouped toggles) */
42
+ size?: ToggleGroupSize;
41
43
  /** Square every item at the current size, for icon-only options */
42
44
  iconOnly?: boolean;
43
45
  /** Disable all options */
@@ -1 +1 @@
1
- {"version":3,"file":"toggleGroup.types.d.ts","sourceRoot":"","sources":["../../../src/components/ToggleGroup/toggleGroup.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,gFAAgF;AAChF,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,WAAW,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IAC1F,wBAAwB;IACxB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,wBAAwB;IACxB,UAAU,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;IACtC,yBAAyB;IACzB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC7C,mBAAmB;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,gEAAgE;IAChE,IAAI,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,GAAG;IAC1D,IAAI,EAAE,QAAQ,CAAC;IACf,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,GAAG;IAC5D,IAAI,EAAE,UAAU,CAAC;IACjB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC"}
1
+ {"version":3,"file":"toggleGroup.types.d.ts","sourceRoot":"","sources":["../../../src/components/ToggleGroup/toggleGroup.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAEpC;;;;GAIG;AACH,KAAK,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AACjD,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAC3F,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC;AAC7D,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IAC1F,wBAAwB;IACxB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,wBAAwB;IACxB,UAAU,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;IACtC,yBAAyB;IACzB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,4BAA4B;IAC5B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,8DAA8D;IAC9D,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,GAAG;IAC1D,IAAI,EAAE,QAAQ,CAAC;IACf,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,GAAG;IAC5D,IAAI,EAAE,UAAU,CAAC;IACjB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,wBAAwB,CAAC"}
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Text } from "@sixthshift/design-system/text";
3
3
  import { cn } from "@sixthshift/design-system/utils";
4
4
  import * as React from "react";
5
- export const Code = React.forwardRef(({ className, ...props }, ref) => (_jsx(Text, { ref: ref, as: "code", className: cn("rounded bg-bg-subtle px-1.5 py-0.5 font-mono text-sm", className), ...props })));
5
+ export const Code = React.forwardRef(({ className, ...props }, ref) => (_jsx(Text, { ref: ref, as: "code", className: cn("rounded-sm bg-bg-subtle px-1.5 py-0.5 font-mono text-sm", className), ...props })));
6
6
  Code.displayName = "Code";
7
7
  //# sourceMappingURL=Code.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/typography/Code.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAyB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAC7F,KAAC,IAAI,IAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAC,MAAM,EAAC,SAAS,EAAE,EAAE,CAAC,sDAAsD,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAC1H,CAAC,CAAC;AACH,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"Code.js","sourceRoot":"","sources":["../../src/typography/Code.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,iCAAiC,CAAC;AACrD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAyB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAC7F,KAAC,IAAI,IAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAC,MAAM,EAAC,SAAS,EAAE,EAAE,CAAC,yDAAyD,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAC7H,CAAC,CAAC;AACH,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sixthshift/design-system",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Personal design system - tokens, theme, and component library",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -361,6 +361,7 @@
361
361
  "check:fix": "biome check --write .",
362
362
  "check:recipes": "bun run scripts/check-recipes.ts",
363
363
  "check:contrast": "bun run scripts/check-contrast.ts",
364
+ "check:radius": "bun run scripts/check-radius.ts",
364
365
  "check:api": "bun run scripts/api-snapshot.ts --check",
365
366
  "check:size": "bun run scripts/check-size.ts",
366
367
  "api:update": "bun run scripts/api-snapshot.ts",
@@ -7,7 +7,7 @@ import * as React from "react";
7
7
  *
8
8
  * Every colour reads a `--badge-*` component token whose value is decided by
9
9
  * src/components/Badge/badge.recipe.css. That file is the mapping from
10
- * `(variant, intent)` to a semantic token — the layer that used to be
10
+ * `variant` to the intent slots — the layer that used to be
11
11
  * `compoundVariants` here, where it was compiled into class-name literals and
12
12
  * unreachable from outside. Nothing in this file names a colour, which is the
13
13
  * point: the semantics are now configurable without a release.
@@ -17,8 +17,16 @@ const badgeVariants = cva(
17
17
  // space before a `+`, silently welding the last class of one fragment to the
18
18
  // first of the next. The screenshots catch it, but a single string means the
19
19
  // sorter has nothing to break.
20
- "badge border-(color:--badge-border) focus:ring-(color:--badge-ring) inline-flex items-center rounded-md border bg-(--badge-bg) px-2.5 py-0.5 font-semibold text-(--badge-fg) text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-offset-2",
21
- { variants: {} }
20
+ "badge border-(color:--badge-border) focus:ring-(color:--badge-ring) inline-flex items-center rounded-md border bg-(--badge-bg) font-semibold text-(--badge-fg) transition-colors focus:outline-hidden focus:ring-2 focus:ring-offset-2",
21
+ {
22
+ variants: {
23
+ size: {
24
+ sm: "px-1.5 py-0 text-[10px]",
25
+ md: "px-2.5 py-0.5 text-xs",
26
+ },
27
+ },
28
+ defaultVariants: { size: "md" },
29
+ }
22
30
  );
23
31
 
24
32
  /**
@@ -41,62 +49,47 @@ const variantStructure: Record<string, string> = {
41
49
  type Loose<T extends string> = T | (string & {});
42
50
 
43
51
  export type BadgeVariantName = "solid" | "soft" | "outline";
44
- /**
45
- * `primary` was this component's name for the brand colour, and nothing else in
46
- * the system used it — the token is `--bg-brand`, and
47
- * docs/modals.md already told callers to write `intent="brand"`. It is off the
48
- * closed union as of this release but still resolves, via an aliased selector in
49
- * badge.css; `BadgeIntent` is widened, so it also still type-checks.
50
- */
51
- export type BadgeIntentName = "neutral" | "brand" | "danger" | "success" | "warning" | "muted";
52
+ /** The system's intent set — the families src/theming/intents.css defines. */
53
+ export type BadgeIntentName = "neutral" | "brand" | "danger" | "success" | "warning";
52
54
  export type BadgeVariant = Loose<BadgeVariantName>;
53
55
  export type BadgeIntent = Loose<BadgeIntentName>;
54
56
 
55
- export type BadgeRecipeProps = VariantProps<typeof badgeVariants> & {
56
- variant?: BadgeVariant | undefined;
57
- intent?: BadgeIntent | undefined;
58
- /** Re-points this element's brand tokens to a brand the theme defines. See the Scoped brands docs. */
59
- brand?: string | undefined;
60
- className?: string | undefined;
61
- };
62
-
63
- /**
64
- * The class string plus the attributes the recipe selects on. `data-brand` is
65
- * omitted unless `brand` is set, same as `buttonRecipe`.
66
- */
67
- export function badgeRecipe({ variant = "solid", intent = "brand", brand, className }: BadgeRecipeProps) {
68
- return {
69
- className: cn(variantStructure[variant], badgeVariants({ className })),
70
- "data-variant": variant,
71
- "data-intent": intent,
72
- "data-brand": brand,
57
+ export type BadgeProps = React.HTMLAttributes<HTMLSpanElement> &
58
+ VariantProps<typeof badgeVariants> & {
59
+ variant?: BadgeVariant | undefined;
60
+ intent?: BadgeIntent | undefined;
61
+ /** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the badge follows the nearest `data-brand` ancestor. */
62
+ brand?: string | undefined;
73
63
  };
74
- }
75
-
76
- export type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
77
- variant?: BadgeVariant | undefined;
78
- intent?: BadgeIntent | undefined;
79
- /** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the badge follows the nearest `data-brand` ancestor. */
80
- brand?: string | undefined;
81
- };
82
64
 
83
65
  /**
84
66
  * Small label for status, category, or count.
85
67
  *
86
68
  * Two independent axes, same split as Button: `variant` is the fill
87
69
  * (`solid`, `soft`, `outline`), `intent` is what it signals (`neutral`,
88
- * `brand`, `danger`, `success`, `warning`, `muted`) — `muted` has no
89
- * Button equivalent, for de-emphasized metadata rather than a status.
70
+ * `brand`, `danger`, `success`, `warning`). `neutral`, the default, is the
71
+ * grey family; a brand-coloured badge says `intent="brand"`. `size` is `sm`
72
+ * or `md` (the default).
90
73
  * Renders a `<span>`; purely presentational, so give it visible text — it
91
74
  * does not manage an accessible name of its own.
92
75
  *
93
- * Colour is not decided here: each `(variant, intent)` pairing resolves
94
- * through a `--badge-*` component token (src/components/Badge/badge.recipe.css), the
95
- * same seam Button uses, so a consumer can re-point a cell without a
96
- * release.
76
+ * Colour is not decided here: each variant resolves through a `--badge-*`
77
+ * component token (src/components/Badge/badge.recipe.css) that paints with the
78
+ * intent slots, the same seam Button uses, so a consumer can re-point a cell
79
+ * or add an intent without a release.
97
80
  */
98
- export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(({ className, variant = "solid", intent = "brand", brand, ...props }, ref) => {
99
- return <span ref={ref} {...badgeRecipe({ variant, intent, brand, className })} {...props} />;
81
+ export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(({ className, variant = "solid", intent = "neutral", brand, size, ...props }, ref) => {
82
+ return (
83
+ <span
84
+ ref={ref}
85
+ className={cn(variantStructure[variant], badgeVariants({ size, className }))}
86
+ // What the recipe selects on; `data-brand` is omitted unless set.
87
+ data-variant={variant}
88
+ data-intent={intent}
89
+ data-brand={brand}
90
+ {...props}
91
+ />
92
+ );
100
93
  });
101
94
  Badge.displayName = "Badge";
102
95
 
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Badge recipe — the component-token layer for Badge.
3
3
  *
4
- * This is the mapping from `(variant, intent)` to a semantic token. It used
5
- * to live in `compoundVariants` inside Badge.tsx, where it was compiled into
6
- * class-name literals and no consumer could reach it. Here it is data, so a
7
- * consumer can re-point a cell, scope a change to a subtree, or add an intent
8
- * the library never shipped.
4
+ * This is the mapping from `variant` to a colour. It used to live in
5
+ * `compoundVariants` inside Badge.tsx, where it was compiled into class-name
6
+ * literals and no consumer could reach it. Here it is data, so a consumer can
7
+ * re-point a cell, scope a change to a subtree, or add a variant the library
8
+ * never shipped. Each cell paints with the intent slots from
9
+ * src/theming/intents.css, so every variant works in every intent.
9
10
  *
10
11
  * The grammar is `--badge-{context}`, context one of bg, fg, border, ring.
11
12
  * Note what is deliberately *absent*: intent and variant never appear in a
@@ -34,107 +35,25 @@
34
35
  --badge-ring: var(--border-brand);
35
36
  }
36
37
 
37
- /* solid — a filled surface, with text read on top of it. The `fg-on-*`
38
- partner is the pairing check-contrast.ts verifies, so these cells inherit
39
- that guarantee rather than restating it. Border stays transparent (the
40
- floor already sets it); only bg/fg vary. */
41
- .badge[data-variant="solid"][data-intent="neutral"] {
42
- --badge-bg: var(--bg-strong);
43
- --badge-fg: var(--fg-on-strong);
38
+ /* solid — a filled surface, with text read on top of it. The slot pair is
39
+ a `bg-*` / `fg-on-*` partnership, which check-contrast.ts verifies. Border
40
+ stays transparent (the floor already sets it). */
41
+ .badge[data-variant="solid"] {
42
+ --badge-bg: var(--intent-solid-bg);
43
+ --badge-fg: var(--intent-solid-fg);
44
44
  }
45
45
 
46
- .badge[data-variant="solid"][data-intent="brand"] {
47
- --badge-bg: var(--bg-brand);
48
- --badge-fg: var(--fg-on-brand);
46
+ /* soft — the family's tint, with its own text partner. */
47
+ .badge[data-variant="soft"] {
48
+ --badge-bg: var(--intent-tint-bg);
49
+ --badge-fg: var(--intent-tint-fg);
49
50
  }
50
51
 
51
- .badge[data-variant="solid"][data-intent="danger"] {
52
- --badge-bg: var(--bg-danger);
53
- --badge-fg: var(--fg-on-danger);
54
- }
55
-
56
- .badge[data-variant="solid"][data-intent="success"] {
57
- --badge-bg: var(--bg-success);
58
- --badge-fg: var(--fg-on-success);
59
- }
60
-
61
- .badge[data-variant="solid"][data-intent="warning"] {
62
- --badge-bg: var(--bg-warning);
63
- --badge-fg: var(--fg-on-warning);
64
- }
65
-
66
- .badge[data-variant="solid"][data-intent="muted"] {
67
- --badge-bg: var(--bg-subtle);
68
- --badge-fg: var(--fg-subtle);
69
- }
70
-
71
- /* soft — a subdued surface, text read on top of it. Border stays
72
- transparent, same as solid. */
73
- .badge[data-variant="soft"][data-intent="neutral"] {
74
- --badge-bg: var(--bg-subtle);
75
- --badge-fg: var(--fg-normal);
76
- }
77
-
78
- .badge[data-variant="soft"][data-intent="brand"] {
79
- --badge-bg: var(--bg-brand-subtle);
80
- --badge-fg: var(--fg-on-brand-subtle);
81
- }
82
-
83
- .badge[data-variant="soft"][data-intent="danger"] {
84
- --badge-bg: var(--bg-danger-subtle);
85
- --badge-fg: var(--fg-on-danger-subtle);
86
- }
87
-
88
- .badge[data-variant="soft"][data-intent="success"] {
89
- --badge-bg: var(--bg-success-subtle);
90
- --badge-fg: var(--fg-on-success-subtle);
91
- }
92
-
93
- .badge[data-variant="soft"][data-intent="warning"] {
94
- --badge-bg: var(--bg-warning-subtle);
95
- --badge-fg: var(--fg-on-warning-subtle);
96
- }
97
-
98
- .badge[data-variant="soft"][data-intent="muted"] {
99
- --badge-bg: var(--bg-subtle);
100
- --badge-fg: var(--fg-subtle);
101
- }
102
-
103
- /* outline — a resting surface (bg-normal, shared across every intent) with
104
- the text and border colour carrying the intent. `neutral` and `muted`
105
- both land on `border-normal`, but differ in foreground, so they stay
106
- separate cells rather than merged into one selector list. */
52
+ /* outline — a resting surface shared across every intent, with the text and
53
+ border carrying the family. */
107
54
  .badge[data-variant="outline"] {
108
55
  --badge-bg: var(--bg-normal);
109
- }
110
-
111
- .badge[data-variant="outline"][data-intent="neutral"] {
112
- --badge-border: var(--border-normal);
113
- --badge-fg: var(--fg-normal);
114
- }
115
-
116
- .badge[data-variant="outline"][data-intent="brand"] {
117
- --badge-border: var(--border-brand);
118
- --badge-fg: var(--fg-brand);
119
- }
120
-
121
- .badge[data-variant="outline"][data-intent="danger"] {
122
- --badge-border: var(--border-danger);
123
- --badge-fg: var(--fg-danger);
124
- }
125
-
126
- .badge[data-variant="outline"][data-intent="success"] {
127
- --badge-border: var(--border-success);
128
- --badge-fg: var(--fg-success);
129
- }
130
-
131
- .badge[data-variant="outline"][data-intent="warning"] {
132
- --badge-border: var(--border-warning);
133
- --badge-fg: var(--fg-warning);
134
- }
135
-
136
- .badge[data-variant="outline"][data-intent="muted"] {
137
- --badge-border: var(--border-normal);
138
- --badge-fg: var(--fg-subtle);
56
+ --badge-border: var(--intent-border);
57
+ --badge-fg: var(--intent-fg);
139
58
  }
140
59
  }
@@ -5,6 +5,5 @@ export {
5
5
  type BadgeProps,
6
6
  type BadgeVariant,
7
7
  type BadgeVariantName,
8
- badgeRecipe,
9
8
  badgeVariants,
10
9
  } from "./Badge";
@@ -8,7 +8,7 @@ import { Slot } from "../../internal/Slot";
8
8
  *
9
9
  * Every colour reads a `--button-*` component token whose value is decided by
10
10
  * src/components/Button/button.recipe.css. That file is the mapping from
11
- * `(variant, intent, state)` to a semantic token — the layer that used to be
11
+ * `(variant, state)` to the intent slots — the layer that used to be
12
12
  * `compoundVariants` here, where it was compiled into class-name literals and
13
13
  * unreachable from outside. Nothing in this file names a colour, which is the
14
14
  * point: the semantics are now configurable without a release.
@@ -36,7 +36,7 @@ const buttonVariants = cva(
36
36
  );
37
37
 
38
38
  /**
39
- * The non-colour half of `variant` — border width, elevation, underline.
39
+ * The non-colour half of `variant` — border width, elevation.
40
40
  *
41
41
  * A plain lookup rather than a CVA variant so an unrecognised variant stays a
42
42
  * legal value that contributes no structure, leaving a consumer's CSS free to
@@ -46,9 +46,15 @@ const variantStructure: Record<string, string> = {
46
46
  solid: "shadow",
47
47
  outline: "border shadow-xs",
48
48
  ghost: "",
49
- link: "underline-offset-4 hover:underline",
50
49
  };
51
50
 
51
+ /**
52
+ * `inline` — inline text, no button box. Not a variant: it is different
53
+ * geometry, not a quieter fill, so it replaces the box rather than joining the
54
+ * variant axis. The size's text scale stays; its height and padding go.
55
+ */
56
+ const inlineGeometry = "h-auto px-0 py-0 underline-offset-4 hover:underline";
57
+
52
58
  /**
53
59
  * Squares the box at whatever size is set, and drops the horizontal padding.
54
60
  *
@@ -72,49 +78,16 @@ const iconOnlyGeometry: Record<string, string> = {
72
78
  * has never heard of, while still autocompleting the ones it ships.
73
79
  *
74
80
  * The closed unions are exported alongside, because widening a type gives up the
75
- * ability to narrow it downstream: once `string` is in the union,
76
- * `Exclude<ButtonVariant, "link">` can no longer remove `link`. ToggleGroup
77
- * relies on exactly that exclusion, so it builds on the closed names.
81
+ * ability to narrow it downstream: once `string` is in the union, an `Exclude`
82
+ * can no longer remove anything. Code that must narrow builds on the closed names.
78
83
  */
79
84
  type Loose<T extends string> = T | (string & {});
80
85
 
81
- export type ButtonVariantName = "solid" | "outline" | "ghost" | "link";
86
+ export type ButtonVariantName = "solid" | "outline" | "ghost";
82
87
  export type ButtonIntentName = "neutral" | "brand" | "danger" | "success" | "warning";
83
88
  export type ButtonVariant = Loose<ButtonVariantName>;
84
89
  export type ButtonIntent = Loose<ButtonIntentName>;
85
90
 
86
- export type ButtonRecipeProps = VariantProps<typeof buttonVariants> & {
87
- variant?: ButtonVariant | undefined;
88
- intent?: ButtonIntent | undefined;
89
- /** Re-points this element's brand tokens to a brand the theme defines. See the Scoped brands docs. */
90
- brand?: string | undefined;
91
- iconOnly?: boolean | undefined;
92
- className?: string | undefined;
93
- };
94
-
95
- /**
96
- * The class string plus the attributes the recipe selects on.
97
- *
98
- * `data-brand` is only emitted when `brand` is set: `undefined` makes React
99
- * omit the attribute, so the element takes its brand from its surroundings.
100
- *
101
- * Shared with Toggle and ToggleGroupItem, which are built on Button's look. The
102
- * data attributes are half the contract now, so anything reusing that look has
103
- * to emit them as well or it lands on the recipe's floor instead of a cell.
104
- */
105
- export function buttonRecipe({ variant = "solid", intent = "neutral", brand, size, iconOnly = false, className }: ButtonRecipeProps) {
106
- return {
107
- className: cn(
108
- variantStructure[variant],
109
- // `className` stays last so a caller still outranks the icon geometry.
110
- buttonVariants({ size, className: cn(iconOnly ? iconOnlyGeometry[size ?? "md"] : undefined, className) })
111
- ),
112
- "data-variant": variant,
113
- "data-intent": intent,
114
- "data-brand": brand,
115
- };
116
- }
117
-
118
91
  export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
119
92
  VariantProps<typeof buttonVariants> & {
120
93
  variant?: ButtonVariant | undefined;
@@ -127,6 +100,8 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
127
100
  brand?: string | undefined;
128
101
  /** Square the button at its current size, for an icon with no label. */
129
102
  iconOnly?: boolean;
103
+ /** Render as inline text with no button box — a link-styled action. Ignores `variant`. */
104
+ inline?: boolean;
130
105
  asChild?: boolean;
131
106
  loading?: boolean;
132
107
  };
@@ -135,9 +110,9 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
135
110
  * A single action the user can take.
136
111
  *
137
112
  * Appearance is three independent axes, so every combination is expressible:
138
- * `variant` is the fill and shape (`solid`, `outline`, `ghost`, `link`),
139
- * `intent` is what the action *means* (`brand` — the default — plus `neutral`,
140
- * `danger`, `success`, `warning`), and `size` is the height scale. Reach for
113
+ * `variant` is the treatment (`solid`, `outline`, `ghost`), `intent` is what
114
+ * the action *means* (`neutral` — the default — plus `brand`, `danger`,
115
+ * `success`, `warning`), and `size` is the height scale. Reach for
141
116
  * `intent="danger"` on a destructive action rather than a red `variant` — that
142
117
  * is the distinction that keeps "outline danger" possible.
143
118
  *
@@ -145,14 +120,17 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
145
120
  * means on Badge and Message. A brand-coloured button is
146
121
  * `intent="brand"` — an affirmative choice, which is why `neutral` is the
147
122
  * default: omitting the prop means "no particular meaning", so it should not
148
- * quietly hand back the brand colour. Button used to spell brand as `neutral`
149
- * for `solid` and `link` while `outline` and `ghost` were already grey, so a
150
- * primary action now needs `intent="brand"` said out loud.
123
+ * quietly hand back the brand colour. A primary action says `intent="brand"`
124
+ * out loud.
125
+ *
126
+ * Colour is not decided here. Each variant resolves through a `--button-*`
127
+ * component token painted from the intent slots, so a consumer can re-point
128
+ * any cell — or add an intent this library never shipped — from their own
129
+ * stylesheet. See the Component tokens story below.
151
130
  *
152
- * Colour is not decided here. Each pairing resolves through a `--button-*`
153
- * component token, so a consumer can re-point any cell — or add an intent this
154
- * library never shipped — from their own stylesheet. See the Component tokens
155
- * story below.
131
+ * `inline` renders the action as inline text with no box — underline on
132
+ * hover, colour from `intent` — for an action that sits in a line of text or a
133
+ * toast. It is terminal: `variant` is ignored.
156
134
  *
157
135
  * `iconOnly` squares the box at whatever `size` is set and drops the
158
136
  * horizontal padding, for a button whose whole content is an icon — give it an
@@ -170,12 +148,40 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
170
148
  */
171
149
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
172
150
  (
173
- { className, variant = "solid", intent = "neutral", brand, size, iconOnly = false, asChild = false, loading = false, children, disabled, ...props },
151
+ {
152
+ className,
153
+ variant = "solid",
154
+ intent = "neutral",
155
+ brand,
156
+ size,
157
+ iconOnly = false,
158
+ inline = false,
159
+ asChild = false,
160
+ loading = false,
161
+ children,
162
+ disabled,
163
+ ...props
164
+ },
174
165
  ref
175
166
  ) => {
176
167
  const Comp = asChild ? Slot : "button";
168
+ const shape = inline ? inlineGeometry : iconOnly ? iconOnlyGeometry[size ?? "md"] : undefined;
177
169
  return (
178
- <Comp {...buttonRecipe({ variant, intent, brand, size, iconOnly, className })} ref={ref} disabled={disabled || loading} {...props}>
170
+ <Comp
171
+ // `className` stays last so a caller still outranks the shape geometry.
172
+ className={cn(inline ? undefined : variantStructure[variant], buttonVariants({ size, className: cn(shape, className) }))}
173
+ // What the recipe selects on. `inline` is terminal: it drops
174
+ // `data-variant`, so no variant cell can match. `data-brand` is
175
+ // `undefined` unless set, so React omits it and the element takes its
176
+ // brand from its surroundings.
177
+ data-variant={inline ? undefined : variant}
178
+ data-inline={inline ? "true" : undefined}
179
+ data-intent={intent}
180
+ data-brand={brand}
181
+ ref={ref}
182
+ disabled={disabled || loading}
183
+ {...props}
184
+ >
179
185
  {loading ? (
180
186
  <>
181
187
  <svg className="animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">