@misoto22/design 0.3.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 (191) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Accordion/Accordion.d.ts +34 -0
  3. package/dist/components/Accordion/Accordion.js +37 -0
  4. package/dist/components/Accordion/Accordion.js.map +1 -0
  5. package/dist/components/Alert/Alert.d.ts +32 -0
  6. package/dist/components/Alert/Alert.js +49 -0
  7. package/dist/components/Alert/Alert.js.map +1 -0
  8. package/dist/components/AppShell/AppShell.d.ts +38 -0
  9. package/dist/components/AppShell/AppShell.js +92 -0
  10. package/dist/components/AppShell/AppShell.js.map +1 -0
  11. package/dist/components/Avatar/Avatar.d.ts +29 -0
  12. package/dist/components/Avatar/Avatar.js +39 -0
  13. package/dist/components/Avatar/Avatar.js.map +1 -0
  14. package/dist/components/Badge/Badge.d.ts +30 -0
  15. package/dist/components/Badge/Badge.js +29 -0
  16. package/dist/components/Badge/Badge.js.map +1 -0
  17. package/dist/components/Breadcrumb/Breadcrumb.d.ts +31 -0
  18. package/dist/components/Breadcrumb/Breadcrumb.js +31 -0
  19. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -0
  20. package/dist/components/Button/Button.d.ts +70 -0
  21. package/dist/components/Button/Button.js +81 -0
  22. package/dist/components/Button/Button.js.map +1 -0
  23. package/dist/components/Calendar/Calendar.d.ts +28 -0
  24. package/dist/components/Calendar/Calendar.js +87 -0
  25. package/dist/components/Calendar/Calendar.js.map +1 -0
  26. package/dist/components/Card/Card.d.ts +49 -0
  27. package/dist/components/Card/Card.js +64 -0
  28. package/dist/components/Card/Card.js.map +1 -0
  29. package/dist/components/Checkbox/Checkbox.d.ts +23 -0
  30. package/dist/components/Checkbox/Checkbox.js +24 -0
  31. package/dist/components/Checkbox/Checkbox.js.map +1 -0
  32. package/dist/components/Collapsible/Collapsible.d.ts +31 -0
  33. package/dist/components/Collapsible/Collapsible.js +35 -0
  34. package/dist/components/Collapsible/Collapsible.js.map +1 -0
  35. package/dist/components/Combobox/Combobox.d.ts +63 -0
  36. package/dist/components/Combobox/Combobox.js +151 -0
  37. package/dist/components/Combobox/Combobox.js.map +1 -0
  38. package/dist/components/Command/Command.d.ts +98 -0
  39. package/dist/components/Command/Command.js +166 -0
  40. package/dist/components/Command/Command.js.map +1 -0
  41. package/dist/components/ContextMenu/ContextMenu.d.ts +38 -0
  42. package/dist/components/ContextMenu/ContextMenu.js +79 -0
  43. package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
  44. package/dist/components/DatePicker/DatePicker.d.ts +98 -0
  45. package/dist/components/DatePicker/DatePicker.js +214 -0
  46. package/dist/components/DatePicker/DatePicker.js.map +1 -0
  47. package/dist/components/Dialog/Dialog.d.ts +48 -0
  48. package/dist/components/Dialog/Dialog.js +72 -0
  49. package/dist/components/Dialog/Dialog.js.map +1 -0
  50. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -0
  51. package/dist/components/DropdownMenu/DropdownMenu.js +89 -0
  52. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
  53. package/dist/components/EmptyState/EmptyState.d.ts +31 -0
  54. package/dist/components/EmptyState/EmptyState.js +30 -0
  55. package/dist/components/EmptyState/EmptyState.js.map +1 -0
  56. package/dist/components/ErrorState/ErrorState.d.ts +34 -0
  57. package/dist/components/ErrorState/ErrorState.js +32 -0
  58. package/dist/components/ErrorState/ErrorState.js.map +1 -0
  59. package/dist/components/Field/Field.d.ts +44 -0
  60. package/dist/components/Field/Field.js +54 -0
  61. package/dist/components/Field/Field.js.map +1 -0
  62. package/dist/components/FigureBand/FigureBand.d.ts +51 -0
  63. package/dist/components/FigureBand/FigureBand.js +52 -0
  64. package/dist/components/FigureBand/FigureBand.js.map +1 -0
  65. package/dist/components/FloatingIconButton/FloatingIconButton.d.ts +29 -0
  66. package/dist/components/FloatingIconButton/FloatingIconButton.js +32 -0
  67. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -0
  68. package/dist/components/Input/Input.d.ts +22 -0
  69. package/dist/components/Input/Input.js +21 -0
  70. package/dist/components/Input/Input.js.map +1 -0
  71. package/dist/components/Kbd/Kbd.d.ts +20 -0
  72. package/dist/components/Kbd/Kbd.js +21 -0
  73. package/dist/components/Kbd/Kbd.js.map +1 -0
  74. package/dist/components/LinkArrow/LinkArrow.d.ts +24 -0
  75. package/dist/components/LinkArrow/LinkArrow.js +24 -0
  76. package/dist/components/LinkArrow/LinkArrow.js.map +1 -0
  77. package/dist/components/NativeSelect/NativeSelect.d.ts +27 -0
  78. package/dist/components/NativeSelect/NativeSelect.js +46 -0
  79. package/dist/components/NativeSelect/NativeSelect.js.map +1 -0
  80. package/dist/components/NavItem/NavItem.d.ts +39 -0
  81. package/dist/components/NavItem/NavItem.js +31 -0
  82. package/dist/components/NavItem/NavItem.js.map +1 -0
  83. package/dist/components/Pagination/Pagination.d.ts +44 -0
  84. package/dist/components/Pagination/Pagination.js +109 -0
  85. package/dist/components/Pagination/Pagination.js.map +1 -0
  86. package/dist/components/Popover/Popover.d.ts +39 -0
  87. package/dist/components/Popover/Popover.js +57 -0
  88. package/dist/components/Popover/Popover.js.map +1 -0
  89. package/dist/components/Progress/Progress.d.ts +31 -0
  90. package/dist/components/Progress/Progress.js +45 -0
  91. package/dist/components/Progress/Progress.js.map +1 -0
  92. package/dist/components/RadioGroup/RadioGroup.d.ts +43 -0
  93. package/dist/components/RadioGroup/RadioGroup.js +43 -0
  94. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  95. package/dist/components/ScrollArea/ScrollArea.d.ts +31 -0
  96. package/dist/components/ScrollArea/ScrollArea.js +54 -0
  97. package/dist/components/ScrollArea/ScrollArea.js.map +1 -0
  98. package/dist/components/SearchableMenu/SearchableMenu.d.ts +59 -0
  99. package/dist/components/SearchableMenu/SearchableMenu.js +79 -0
  100. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -0
  101. package/dist/components/Select/Select.d.ts +71 -0
  102. package/dist/components/Select/Select.js +114 -0
  103. package/dist/components/Select/Select.js.map +1 -0
  104. package/dist/components/Separator/Separator.d.ts +30 -0
  105. package/dist/components/Separator/Separator.js +35 -0
  106. package/dist/components/Separator/Separator.js.map +1 -0
  107. package/dist/components/Sheet/Sheet.d.ts +38 -0
  108. package/dist/components/Sheet/Sheet.js +68 -0
  109. package/dist/components/Sheet/Sheet.js.map +1 -0
  110. package/dist/components/Skeleton/Skeleton.d.ts +63 -0
  111. package/dist/components/Skeleton/Skeleton.js +44 -0
  112. package/dist/components/Skeleton/Skeleton.js.map +1 -0
  113. package/dist/components/Slider/Slider.d.ts +35 -0
  114. package/dist/components/Slider/Slider.js +52 -0
  115. package/dist/components/Slider/Slider.js.map +1 -0
  116. package/dist/components/Spinner/Spinner.d.ts +50 -0
  117. package/dist/components/Spinner/Spinner.js +48 -0
  118. package/dist/components/Spinner/Spinner.js.map +1 -0
  119. package/dist/components/StatusDot/StatusDot.d.ts +29 -0
  120. package/dist/components/StatusDot/StatusDot.js +44 -0
  121. package/dist/components/StatusDot/StatusDot.js.map +1 -0
  122. package/dist/components/StatusPill/StatusPill.d.ts +25 -0
  123. package/dist/components/StatusPill/StatusPill.js +31 -0
  124. package/dist/components/StatusPill/StatusPill.js.map +1 -0
  125. package/dist/components/Switch/Switch.d.ts +23 -0
  126. package/dist/components/Switch/Switch.js +23 -0
  127. package/dist/components/Switch/Switch.js.map +1 -0
  128. package/dist/components/Table/Table.d.ts +96 -0
  129. package/dist/components/Table/Table.js +136 -0
  130. package/dist/components/Table/Table.js.map +1 -0
  131. package/dist/components/Tabs/Tabs.d.ts +35 -0
  132. package/dist/components/Tabs/Tabs.js +48 -0
  133. package/dist/components/Tabs/Tabs.js.map +1 -0
  134. package/dist/components/Tag/Tag.d.ts +28 -0
  135. package/dist/components/Tag/Tag.js +22 -0
  136. package/dist/components/Tag/Tag.js.map +1 -0
  137. package/dist/components/Textarea/Textarea.d.ts +20 -0
  138. package/dist/components/Textarea/Textarea.js +32 -0
  139. package/dist/components/Textarea/Textarea.js.map +1 -0
  140. package/dist/components/Toast/Toast.d.ts +24 -0
  141. package/dist/components/Toast/Toast.js +41 -0
  142. package/dist/components/Toast/Toast.js.map +1 -0
  143. package/dist/components/ToggleGroup/ToggleGroup.d.ts +41 -0
  144. package/dist/components/ToggleGroup/ToggleGroup.js +78 -0
  145. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -0
  146. package/dist/components/Tooltip/Tooltip.d.ts +43 -0
  147. package/dist/components/Tooltip/Tooltip.js +40 -0
  148. package/dist/components/Tooltip/Tooltip.js.map +1 -0
  149. package/dist/fonts/hanken-grotesk-300.woff2 +0 -0
  150. package/dist/fonts/hanken-grotesk-400.woff2 +0 -0
  151. package/dist/fonts/hanken-grotesk-500.woff2 +0 -0
  152. package/dist/fonts/hanken-grotesk-600.woff2 +0 -0
  153. package/dist/fonts/hanken-grotesk-700.woff2 +0 -0
  154. package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
  155. package/dist/fonts/ibm-plex-mono-500.woff2 +0 -0
  156. package/dist/fonts/ibm-plex-mono-600.woff2 +0 -0
  157. package/dist/fonts/newsreader-300.woff2 +0 -0
  158. package/dist/fonts/newsreader-400.woff2 +0 -0
  159. package/dist/fonts/newsreader-500.woff2 +0 -0
  160. package/dist/fonts/newsreader-600.woff2 +0 -0
  161. package/dist/fonts.css +96 -0
  162. package/dist/index.d.ts +77 -0
  163. package/dist/index.js +63 -0
  164. package/dist/index.js.map +1 -0
  165. package/dist/keyframes.css +212 -0
  166. package/dist/lib/cn.d.ts +21 -0
  167. package/dist/lib/cn.js +9 -0
  168. package/dist/lib/cn.js.map +1 -0
  169. package/dist/lib/control.d.ts +31 -0
  170. package/dist/lib/control.js +14 -0
  171. package/dist/lib/control.js.map +1 -0
  172. package/dist/lib/index.d.ts +4 -0
  173. package/dist/lib/index.js +11 -0
  174. package/dist/lib/index.js.map +1 -0
  175. package/dist/lib/overlay-container.d.ts +38 -0
  176. package/dist/lib/overlay-container.js +15 -0
  177. package/dist/lib/overlay-container.js.map +1 -0
  178. package/dist/lib/useSelectionIndicator.d.ts +29 -0
  179. package/dist/lib/useSelectionIndicator.js +45 -0
  180. package/dist/lib/useSelectionIndicator.js.map +1 -0
  181. package/dist/semantic.css +127 -0
  182. package/dist/styles.css +98 -0
  183. package/dist/themes.css +115 -0
  184. package/dist/tokens/brand.d.ts +47 -0
  185. package/dist/tokens/brand.js +38 -0
  186. package/dist/tokens/brand.js.map +1 -0
  187. package/dist/tokens.css +323 -0
  188. package/dist/tokens.d.ts +153 -0
  189. package/dist/tokens.js +766 -0
  190. package/dist/tokens.json +765 -0
  191. package/package.json +130 -0
@@ -0,0 +1,63 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ type SkeletonProps = HTMLAttributes<HTMLDivElement>;
5
+ /**
6
+ * The fill every skeleton part is drawn from: `--stone`, and nothing else.
7
+ *
8
+ * Deliberately NOT a shimmer. A shimmer sweeps a highlight across the shape,
9
+ * which implies a light source; the White Reset has none, so the sweep reads as
10
+ * a bug rather than as loading. The whole page breathes together instead —
11
+ * see `SkeletonPage`, which owns the single pulse.
12
+ *
13
+ * @example
14
+ * <Skeleton className="h-3 w-40 rounded-(--radius-pill)" />
15
+ */
16
+ declare function Skeleton({ className, ...rest }: SkeletonProps): react.JSX.Element;
17
+ /** A line of type. Height is the line's; width is how far it runs. */
18
+ declare function SkeletonLine({ className, ...rest }: SkeletonProps): react.JSX.Element;
19
+ /** A block: an image mount, a plate, a chart. */
20
+ declare function SkeletonBlock({ className, ...rest }: SkeletonProps): react.JSX.Element;
21
+ /** A circle: an avatar, a marker. */
22
+ declare function SkeletonCircle({ className, ...rest }: SkeletonProps): react.JSX.Element;
23
+ interface SkeletonTextProps extends SkeletonProps {
24
+ /** How many lines of prose to stand in for. */
25
+ lines?: number;
26
+ }
27
+ /**
28
+ * A paragraph.
29
+ *
30
+ * The last line is short, because real prose ends mid-measure. A block of equal
31
+ * full-width bars reads as a table, and the reader then flinches when a
32
+ * paragraph arrives instead.
33
+ */
34
+ declare function SkeletonText({ lines, className, ...rest }: SkeletonTextProps): react.JSX.Element;
35
+ interface SkeletonPageProps extends HTMLAttributes<HTMLDivElement> {
36
+ /**
37
+ * What is loading, in words. Announced once; the shapes themselves are
38
+ * `aria-hidden`, so this sentence is the entire experience for a screen
39
+ * reader and "Loading" alone tells it nothing.
40
+ */
41
+ label: string;
42
+ children: ReactNode;
43
+ }
44
+ /**
45
+ * The frame a skeleton sits in: the live region, and the single pulse.
46
+ *
47
+ * One `animate-pulse` on the wrapper rather than one per part, so the page
48
+ * breathes together instead of shimmering out of phase — twenty independently
49
+ * animated bars is a visual noise floor, not a loading state.
50
+ *
51
+ * A skeleton whose shape does not match what replaces it is worse than none: it
52
+ * promises a layout and then the page jumps out from under the reader. Compose
53
+ * the parts to match the real thing, and change both together.
54
+ *
55
+ * @example
56
+ * <SkeletonPage label="Loading projects">
57
+ * <SkeletonLine className="h-2.5 w-28" />
58
+ * <SkeletonBlock className="mt-6 h-40" />
59
+ * </SkeletonPage>
60
+ */
61
+ declare function SkeletonPage({ label, children, className, ...rest }: SkeletonPageProps): react.JSX.Element;
62
+
63
+ export { Skeleton, SkeletonBlock, SkeletonCircle, SkeletonLine, SkeletonPage, type SkeletonPageProps, type SkeletonProps, SkeletonText, type SkeletonTextProps, Skeleton as default };
@@ -0,0 +1,44 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ function Skeleton({ className, ...rest }) {
4
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn("bg-(--stone)", className), ...rest });
5
+ }
6
+ function SkeletonLine({ className, ...rest }) {
7
+ return /* @__PURE__ */ jsx(Skeleton, { className: cn("h-3 rounded-(--radius-pill)", className), ...rest });
8
+ }
9
+ function SkeletonBlock({ className, ...rest }) {
10
+ return /* @__PURE__ */ jsx(Skeleton, { className: cn("rounded-(--radius-sm)", className), ...rest });
11
+ }
12
+ function SkeletonCircle({ className, ...rest }) {
13
+ return /* @__PURE__ */ jsx(Skeleton, { className: cn("size-9 rounded-full", className), ...rest });
14
+ }
15
+ function SkeletonText({ lines = 3, className, ...rest }) {
16
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2.5", className), ...rest, children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx(SkeletonLine, { className: index === lines - 1 ? "w-[62%]" : "w-full" }, index)) });
17
+ }
18
+ function SkeletonPage({ label, children, className, ...rest }) {
19
+ return /* @__PURE__ */ jsxs(
20
+ "div",
21
+ {
22
+ role: "status",
23
+ "aria-busy": "true",
24
+ "data-m22-animated": true,
25
+ className: cn("animate-pulse motion-reduce:animate-none", className),
26
+ ...rest,
27
+ children: [
28
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
29
+ children
30
+ ]
31
+ }
32
+ );
33
+ }
34
+ var Skeleton_default = Skeleton;
35
+ export {
36
+ Skeleton,
37
+ SkeletonBlock,
38
+ SkeletonCircle,
39
+ SkeletonLine,
40
+ SkeletonPage,
41
+ SkeletonText,
42
+ Skeleton_default as default
43
+ };
44
+ //# sourceMappingURL=Skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Skeleton/Skeleton.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type SkeletonProps = HTMLAttributes<HTMLDivElement>\n\n/**\n * The fill every skeleton part is drawn from: `--stone`, and nothing else.\n *\n * Deliberately NOT a shimmer. A shimmer sweeps a highlight across the shape,\n * which implies a light source; the White Reset has none, so the sweep reads as\n * a bug rather than as loading. The whole page breathes together instead —\n * see `SkeletonPage`, which owns the single pulse.\n *\n * @example\n * <Skeleton className=\"h-3 w-40 rounded-(--radius-pill)\" />\n */\nexport function Skeleton({ className, ...rest }: SkeletonProps) {\n return <div aria-hidden=\"true\" className={cn('bg-(--stone)', className)} {...rest} />\n}\n\n/** A line of type. Height is the line's; width is how far it runs. */\nexport function SkeletonLine({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('h-3 rounded-(--radius-pill)', className)} {...rest} />\n}\n\n/** A block: an image mount, a plate, a chart. */\nexport function SkeletonBlock({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('rounded-(--radius-sm)', className)} {...rest} />\n}\n\n/** A circle: an avatar, a marker. */\nexport function SkeletonCircle({ className, ...rest }: SkeletonProps) {\n return <Skeleton className={cn('size-9 rounded-full', className)} {...rest} />\n}\n\nexport interface SkeletonTextProps extends SkeletonProps {\n /** How many lines of prose to stand in for. */\n lines?: number\n}\n\n/**\n * A paragraph.\n *\n * The last line is short, because real prose ends mid-measure. A block of equal\n * full-width bars reads as a table, and the reader then flinches when a\n * paragraph arrives instead.\n */\nexport function SkeletonText({ lines = 3, className, ...rest }: SkeletonTextProps) {\n return (\n <div className={cn('flex flex-col gap-2.5', className)} {...rest}>\n {Array.from({ length: lines }, (_, index) => (\n <SkeletonLine key={index} className={index === lines - 1 ? 'w-[62%]' : 'w-full'} />\n ))}\n </div>\n )\n}\n\nexport interface SkeletonPageProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * What is loading, in words. Announced once; the shapes themselves are\n * `aria-hidden`, so this sentence is the entire experience for a screen\n * reader and \"Loading\" alone tells it nothing.\n */\n label: string\n children: ReactNode\n}\n\n/**\n * The frame a skeleton sits in: the live region, and the single pulse.\n *\n * One `animate-pulse` on the wrapper rather than one per part, so the page\n * breathes together instead of shimmering out of phase — twenty independently\n * animated bars is a visual noise floor, not a loading state.\n *\n * A skeleton whose shape does not match what replaces it is worse than none: it\n * promises a layout and then the page jumps out from under the reader. Compose\n * the parts to match the real thing, and change both together.\n *\n * @example\n * <SkeletonPage label=\"Loading projects\">\n * <SkeletonLine className=\"h-2.5 w-28\" />\n * <SkeletonBlock className=\"mt-6 h-40\" />\n * </SkeletonPage>\n */\nexport function SkeletonPage({ label, children, className, ...rest }: SkeletonPageProps) {\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n data-m22-animated\n className={cn('animate-pulse motion-reduce:animate-none', className)}\n {...rest}\n >\n <span className=\"sr-only\">{label}</span>\n {children}\n </div>\n )\n}\n\nexport default Skeleton\n"],"mappings":"AAiBS,cAqEL,YArEK;AAhBT,SAAS,UAAU;AAeZ,SAAS,SAAS,EAAE,WAAW,GAAG,KAAK,GAAkB;AAC9D,SAAO,oBAAC,SAAI,eAAY,QAAO,WAAW,GAAG,gBAAgB,SAAS,GAAI,GAAG,MAAM;AACrF;AAGO,SAAS,aAAa,EAAE,WAAW,GAAG,KAAK,GAAkB;AAClE,SAAO,oBAAC,YAAS,WAAW,GAAG,+BAA+B,SAAS,GAAI,GAAG,MAAM;AACtF;AAGO,SAAS,cAAc,EAAE,WAAW,GAAG,KAAK,GAAkB;AACnE,SAAO,oBAAC,YAAS,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,MAAM;AAChF;AAGO,SAAS,eAAe,EAAE,WAAW,GAAG,KAAK,GAAkB;AACpE,SAAO,oBAAC,YAAS,WAAW,GAAG,uBAAuB,SAAS,GAAI,GAAG,MAAM;AAC9E;AAcO,SAAS,aAAa,EAAE,QAAQ,GAAG,WAAW,GAAG,KAAK,GAAsB;AACjF,SACE,oBAAC,SAAI,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,MACzD,gBAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,UACjC,oBAAC,gBAAyB,WAAW,UAAU,QAAQ,IAAI,YAAY,YAApD,KAA8D,CAClF,GACH;AAEJ;AA6BO,SAAS,aAAa,EAAE,OAAO,UAAU,WAAW,GAAG,KAAK,GAAsB;AACvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,qBAAiB;AAAA,MACjB,WAAW,GAAG,4CAA4C,SAAS;AAAA,MAClE,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAU,WAAW,iBAAM;AAAA,QAChC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,mBAAQ;","names":[]}
@@ -0,0 +1,35 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as SliderPrimitive from '@radix-ui/react-slider';
4
+
5
+ interface SliderProps extends ComponentProps<typeof SliderPrimitive.Root> {
6
+ /**
7
+ * Names the control. Required: a slider with no name announces only a number,
8
+ * and a number with no noun is not information.
9
+ *
10
+ * A range slider (two thumbs) needs one name per thumb — pass an array.
11
+ */
12
+ label: string | [string, string];
13
+ /** Prints the current value beside the label. */
14
+ showValue?: boolean;
15
+ /** Renders the value with a unit or a currency, e.g. `(n) => n + '%'`. */
16
+ format?: (value: number) => string;
17
+ }
18
+ /**
19
+ * A value chosen along a range.
20
+ *
21
+ * Radix owns the keyboard contract — arrows step, Page keys jump, Home and End
22
+ * reach the ends — and the ARIA that reports the value. What is here is the
23
+ * look, and the labelling, which is the part a slider most often gets wrong: a
24
+ * thumb that announces "42" and nothing else leaves a screen reader user with a
25
+ * number and no idea what it measures.
26
+ *
27
+ * A 44px hit area sits invisibly around the 16px thumb, because a thumb sized
28
+ * for the design is well under any pointer-target guideline.
29
+ *
30
+ * @example
31
+ * <Slider label="Quality" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />
32
+ */
33
+ declare function Slider({ label, showValue, format, className, onValueChange, ...props }: SliderProps): react.JSX.Element;
34
+
35
+ export { Slider, type SliderProps, Slider as default };
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as SliderPrimitive from "@radix-ui/react-slider";
4
+ import { useState } from "react";
5
+ import { cn } from "../../lib/cn.js";
6
+ function Slider({
7
+ label,
8
+ showValue = false,
9
+ format = String,
10
+ className,
11
+ onValueChange,
12
+ ...props
13
+ }) {
14
+ const names = Array.isArray(label) ? label : [label];
15
+ const [uncontrolled, setUncontrolled] = useState(props.defaultValue ?? []);
16
+ const current = props.value ?? uncontrolled;
17
+ const handleChange = (next) => {
18
+ if (props.value === void 0) setUncontrolled(next);
19
+ onValueChange?.(next);
20
+ };
21
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-3", className), children: [
22
+ showValue && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between mono-meta text-(--ink-3-aa)", children: [
23
+ /* @__PURE__ */ jsx("span", { children: names[0] }),
24
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-(--ink)", children: current.map((value) => format(value)).join(" \u2013 ") })
25
+ ] }),
26
+ /* @__PURE__ */ jsxs(
27
+ SliderPrimitive.Root,
28
+ {
29
+ className: "group relative flex w-full touch-none select-none items-center py-3",
30
+ onValueChange: handleChange,
31
+ ...props,
32
+ children: [
33
+ /* @__PURE__ */ jsx(SliderPrimitive.Track, { className: "relative h-1 w-full grow overflow-hidden rounded-(--radius-pill) bg-(--stone) transition-[height] duration-(--duration-fast) group-hover:h-1.5", children: /* @__PURE__ */ jsx(SliderPrimitive.Range, { className: "absolute h-full bg-(--accent)" }) }),
34
+ current.map((_, index) => /* @__PURE__ */ jsx(
35
+ SliderPrimitive.Thumb,
36
+ {
37
+ "aria-label": names[index] ?? names[0],
38
+ className: "relative block size-4 rounded-full border border-(--accent) bg-(--paper) transition-[transform,background-color] duration-(--duration-fast) ease-(--ease-out-expo) before:absolute before:left-1/2 before:top-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-[''] hover:scale-110 hover:bg-(--stone) active:scale-125 data-[state=active]:scale-125 disabled:opacity-(--disabled-opacity) motion-reduce:transition-none"
39
+ },
40
+ index
41
+ ))
42
+ ]
43
+ }
44
+ )
45
+ ] });
46
+ }
47
+ var Slider_default = Slider;
48
+ export {
49
+ Slider,
50
+ Slider_default as default
51
+ };
52
+ //# sourceMappingURL=Slider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Slider/Slider.tsx"],"sourcesContent":["'use client'\n\nimport * as SliderPrimitive from '@radix-ui/react-slider'\nimport { useState, type ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface SliderProps extends ComponentProps<typeof SliderPrimitive.Root> {\n /**\n * Names the control. Required: a slider with no name announces only a number,\n * and a number with no noun is not information.\n *\n * A range slider (two thumbs) needs one name per thumb — pass an array.\n */\n label: string | [string, string]\n /** Prints the current value beside the label. */\n showValue?: boolean\n /** Renders the value with a unit or a currency, e.g. `(n) => n + '%'`. */\n format?: (value: number) => string\n}\n\n/**\n * A value chosen along a range.\n *\n * Radix owns the keyboard contract — arrows step, Page keys jump, Home and End\n * reach the ends — and the ARIA that reports the value. What is here is the\n * look, and the labelling, which is the part a slider most often gets wrong: a\n * thumb that announces \"42\" and nothing else leaves a screen reader user with a\n * number and no idea what it measures.\n *\n * A 44px hit area sits invisibly around the 16px thumb, because a thumb sized\n * for the design is well under any pointer-target guideline.\n *\n * @example\n * <Slider label=\"Quality\" defaultValue={[80]} max={100} step={5} showValue format={(n) => `${n}%`} />\n */\nexport function Slider({\n label,\n showValue = false,\n format = String,\n className,\n onValueChange,\n ...props\n}: SliderProps) {\n const names = Array.isArray(label) ? label : [label]\n\n // Tracked here, not read off the props. An uncontrolled slider's\n // `defaultValue` never changes, so the printed figure sat at its starting\n // number while the thumb moved — the one thing `showValue` exists to avoid.\n const [uncontrolled, setUncontrolled] = useState<number[]>(props.defaultValue ?? [])\n const current = props.value ?? uncontrolled\n\n const handleChange = (next: number[]) => {\n if (props.value === undefined) setUncontrolled(next)\n onValueChange?.(next)\n }\n\n return (\n <div className={cn('flex w-full flex-col gap-3', className)}>\n {showValue && (\n <div className=\"flex items-baseline justify-between mono-meta text-(--ink-3-aa)\">\n <span>{names[0]}</span>\n <span className=\"tabular-nums text-(--ink)\">\n {current.map((value) => format(value)).join(' – ')}\n </span>\n </div>\n )}\n <SliderPrimitive.Root\n className=\"group relative flex w-full touch-none select-none items-center py-3\"\n onValueChange={handleChange}\n {...props}\n >\n <SliderPrimitive.Track className=\"relative h-1 w-full grow overflow-hidden rounded-(--radius-pill) bg-(--stone) transition-[height] duration-(--duration-fast) group-hover:h-1.5\">\n <SliderPrimitive.Range className=\"absolute h-full bg-(--accent)\" />\n </SliderPrimitive.Track>\n {current.map((_, index) => (\n <SliderPrimitive.Thumb\n key={index}\n aria-label={names[index] ?? names[0]}\n // The visible thumb is 16px; the `before` pseudo-element widens the\n // hit area to 44px without changing what is drawn.\n // `transition-transform` and not `all`: the thumb's own position is\n // set by Radix as a `left` percentage, and transitioning that would\n // make it lag the pointer. Only the grow-on-grab is animated.\n className=\"relative block size-4 rounded-full border border-(--accent) bg-(--paper) transition-[transform,background-color] duration-(--duration-fast) ease-(--ease-out-expo) before:absolute before:left-1/2 before:top-1/2 before:size-11 before:-translate-x-1/2 before:-translate-y-1/2 before:content-[''] hover:scale-110 hover:bg-(--stone) active:scale-125 data-[state=active]:scale-125 disabled:opacity-(--disabled-opacity) motion-reduce:transition-none\"\n />\n ))}\n </SliderPrimitive.Root>\n </div>\n )\n}\n\nexport default Slider\n"],"mappings":";AA2DQ,SACE,KADF;AAzDR,YAAY,qBAAqB;AACjC,SAAS,gBAAqC;AAC9C,SAAS,UAAU;AA+BZ,SAAS,OAAO;AAAA,EACrB;AAAA,EACA,YAAY;AAAA,EACZ,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AACd,QAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAKnD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAmB,MAAM,gBAAgB,CAAC,CAAC;AACnF,QAAM,UAAU,MAAM,SAAS;AAE/B,QAAM,eAAe,CAAC,SAAmB;AACvC,QAAI,MAAM,UAAU,OAAW,iBAAgB,IAAI;AACnD,oBAAgB,IAAI;AAAA,EACtB;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,8BAA8B,SAAS,GACvD;AAAA,iBACC,qBAAC,SAAI,WAAU,mEACb;AAAA,0BAAC,UAAM,gBAAM,CAAC,GAAE;AAAA,MAChB,oBAAC,UAAK,WAAU,6BACb,kBAAQ,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC,EAAE,KAAK,UAAK,GACnD;AAAA,OACF;AAAA,IAEF;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,WAAU;AAAA,QACV,eAAe;AAAA,QACd,GAAG;AAAA,QAEJ;AAAA,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,kJAC/B,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,iCAAgC,GACnE;AAAA,UACC,QAAQ,IAAI,CAAC,GAAG,UACf;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cAEC,cAAY,MAAM,KAAK,KAAK,MAAM,CAAC;AAAA,cAMnC,WAAU;AAAA;AAAA,YAPL;AAAA,UAQP,CACD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
@@ -0,0 +1,50 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
5
+ /** 14px / 18px / 26px. Match the size of the type it sits beside. */
6
+ size?: 'sm' | 'md' | 'lg';
7
+ /**
8
+ * `default` draws the ring against the page — a hairline track in the rule
9
+ * colour with the leading quarter in ink. `current` draws it in the inherited
10
+ * text colour, which is what a spinner inside a filled button needs: on an
11
+ * ink ground an ink ring is invisible.
12
+ */
13
+ tone?: 'default' | 'current';
14
+ /**
15
+ * Announced to assistive tech. Pass the specific thing being waited on
16
+ * ("Loading projects"), not the generic word — a screen reader user hearing
17
+ * "Loading" three times cannot tell which three things.
18
+ *
19
+ * Pass `null` for a spinner that sits inside a control which already names
20
+ * the operation (a button whose own label changes to "Saving…"), so the two
21
+ * are not read out twice.
22
+ */
23
+ label?: string | null;
24
+ }
25
+ /**
26
+ * The system's one "working" indicator.
27
+ *
28
+ * A ring, not a blurred glow and not a shimmer: the White Reset has no light
29
+ * source, so depth and softness are not available to it. The leading quarter is
30
+ * the only thing that distinguishes the ring from a plain circle, which is why
31
+ * the track stays a hairline — a spinner is a hint that time is passing, not a
32
+ * feature of the page.
33
+ *
34
+ * It spins under `motion-safe` only. A reader who has asked for less motion
35
+ * gets a static ring, which still reads as "not finished" because the leading
36
+ * quarter is darker than the track.
37
+ *
38
+ * For a wait long enough that the reader would otherwise wonder whether the
39
+ * page is broken, prefer `Skeleton` — a shape that describes what is coming
40
+ * beats a dot that describes nothing.
41
+ *
42
+ * @example
43
+ * <Spinner size="lg" label="Loading projects" />
44
+ * @example
45
+ * // Inside a filled button, where the ground is ink:
46
+ * <Spinner size="sm" tone="current" label={null} />
47
+ */
48
+ declare function Spinner({ size, tone, className, label, ...rest }: SpinnerProps): react.JSX.Element;
49
+
50
+ export { Spinner, type SpinnerProps, Spinner as default };
@@ -0,0 +1,48 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const SIZE = {
4
+ sm: "size-3.5 border-[1.5px]",
5
+ md: "size-[18px] border-2",
6
+ lg: "size-6.5 border-2"
7
+ };
8
+ const TONE = {
9
+ default: "border-(--rule-2) border-t-(--ink)",
10
+ current: "border-current/25 border-t-current"
11
+ };
12
+ function Spinner({
13
+ size = "md",
14
+ tone = "default",
15
+ className,
16
+ label = "Loading",
17
+ ...rest
18
+ }) {
19
+ return /* @__PURE__ */ jsxs(
20
+ "span",
21
+ {
22
+ role: label === null ? void 0 : "status",
23
+ "aria-hidden": label === null ? true : void 0,
24
+ className: cn("inline-flex shrink-0 items-center justify-center", className),
25
+ ...rest,
26
+ children: [
27
+ /* @__PURE__ */ jsx(
28
+ "span",
29
+ {
30
+ "data-m22-animated": true,
31
+ className: cn(
32
+ "inline-block rounded-full border-solid motion-safe:animate-[m22-spin_0.7s_linear_infinite]",
33
+ SIZE[size],
34
+ TONE[tone]
35
+ )
36
+ }
37
+ ),
38
+ label !== null && /* @__PURE__ */ jsx("span", { className: "sr-only", children: label })
39
+ ]
40
+ }
41
+ );
42
+ }
43
+ var Spinner_default = Spinner;
44
+ export {
45
+ Spinner,
46
+ Spinner_default as default
47
+ };
48
+ //# sourceMappingURL=Spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Spinner/Spinner.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {\n /** 14px / 18px / 26px. Match the size of the type it sits beside. */\n size?: 'sm' | 'md' | 'lg'\n /**\n * `default` draws the ring against the page — a hairline track in the rule\n * colour with the leading quarter in ink. `current` draws it in the inherited\n * text colour, which is what a spinner inside a filled button needs: on an\n * ink ground an ink ring is invisible.\n */\n tone?: 'default' | 'current'\n /**\n * Announced to assistive tech. Pass the specific thing being waited on\n * (\"Loading projects\"), not the generic word — a screen reader user hearing\n * \"Loading\" three times cannot tell which three things.\n *\n * Pass `null` for a spinner that sits inside a control which already names\n * the operation (a button whose own label changes to \"Saving…\"), so the two\n * are not read out twice.\n */\n label?: string | null\n}\n\nconst SIZE = {\n sm: 'size-3.5 border-[1.5px]',\n md: 'size-[18px] border-2',\n lg: 'size-6.5 border-2',\n} as const\n\nconst TONE = {\n default: 'border-(--rule-2) border-t-(--ink)',\n current: 'border-current/25 border-t-current',\n} as const\n\n/**\n * The system's one \"working\" indicator.\n *\n * A ring, not a blurred glow and not a shimmer: the White Reset has no light\n * source, so depth and softness are not available to it. The leading quarter is\n * the only thing that distinguishes the ring from a plain circle, which is why\n * the track stays a hairline — a spinner is a hint that time is passing, not a\n * feature of the page.\n *\n * It spins under `motion-safe` only. A reader who has asked for less motion\n * gets a static ring, which still reads as \"not finished\" because the leading\n * quarter is darker than the track.\n *\n * For a wait long enough that the reader would otherwise wonder whether the\n * page is broken, prefer `Skeleton` — a shape that describes what is coming\n * beats a dot that describes nothing.\n *\n * @example\n * <Spinner size=\"lg\" label=\"Loading projects\" />\n * @example\n * // Inside a filled button, where the ground is ink:\n * <Spinner size=\"sm\" tone=\"current\" label={null} />\n */\nexport function Spinner({\n size = 'md',\n tone = 'default',\n className,\n label = 'Loading',\n ...rest\n}: SpinnerProps) {\n return (\n <span\n role={label === null ? undefined : 'status'}\n aria-hidden={label === null ? true : undefined}\n className={cn('inline-flex shrink-0 items-center justify-center', className)}\n {...rest}\n >\n <span\n data-m22-animated\n className={cn(\n 'inline-block rounded-full border-solid motion-safe:animate-[m22-spin_0.7s_linear_infinite]',\n SIZE[size],\n TONE[tone],\n )}\n />\n {label !== null && <span className=\"sr-only\">{label}</span>}\n </span>\n )\n}\n\nexport default Spinner\n"],"mappings":"AAmEI,SAME,KANF;AAlEJ,SAAS,UAAU;AAwBnB,MAAM,OAAO;AAAA,EACX,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,OAAO;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AACX;AAyBO,SAAS,QAAQ;AAAA,EACtB,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,GAAiB;AACf,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,UAAU,OAAO,SAAY;AAAA,MACnC,eAAa,UAAU,OAAO,OAAO;AAAA,MACrC,WAAW,GAAG,oDAAoD,SAAS;AAAA,MAC1E,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YACjB,WAAW;AAAA,cACT;AAAA,cACA,KAAK,IAAI;AAAA,cACT,KAAK,IAAI;AAAA,YACX;AAAA;AAAA,QACF;AAAA,QACC,UAAU,QAAQ,oBAAC,UAAK,WAAU,WAAW,iBAAM;AAAA;AAAA;AAAA,EACtD;AAEJ;AAEA,IAAO,kBAAQ;","names":[]}
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ type StatusTone = 'success' | 'warning' | 'danger' | 'neutral';
5
+ interface StatusDotProps extends HTMLAttributes<HTMLSpanElement> {
6
+ /** 7px / 8px. `md` is the default. */
7
+ size?: 'sm' | 'md';
8
+ tone?: StatusTone;
9
+ /** A halo that grows and dissolves, for "live right now". Off for a settled state. */
10
+ pulse?: boolean;
11
+ }
12
+ /**
13
+ * The dot beside a status word.
14
+ *
15
+ * `aria-hidden` without exception: the dot repeats a state the adjacent label
16
+ * already names, and a screen reader announcing "available" twice is worse than
17
+ * not announcing the decoration at all. If a call site has no visible label,
18
+ * the fix is a label, not an `aria-label` on the dot.
19
+ *
20
+ * The halo is a separate absolutely-positioned ring rather than a box-shadow,
21
+ * because Law 2 of this system is that a shadow is never blurred — and it is
22
+ * motion-safe, so a reader who asked for less motion gets a static dot.
23
+ *
24
+ * @example
25
+ * <span className="inline-flex items-center gap-2"><StatusDot /> Available</span>
26
+ */
27
+ declare function StatusDot({ size, tone, pulse, className, ...rest }: StatusDotProps): react.JSX.Element;
28
+
29
+ export { StatusDot, type StatusDotProps, type StatusTone, StatusDot as default };
@@ -0,0 +1,44 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const SIZE = { sm: "size-[7px]", md: "size-2" };
4
+ const TONE = {
5
+ success: "bg-(--ok)",
6
+ warning: "bg-(--warn)",
7
+ danger: "bg-(--danger)",
8
+ neutral: "bg-(--ink-3-aa)"
9
+ };
10
+ function StatusDot({
11
+ size = "md",
12
+ tone = "success",
13
+ pulse = true,
14
+ className,
15
+ ...rest
16
+ }) {
17
+ return /* @__PURE__ */ jsxs(
18
+ "span",
19
+ {
20
+ "aria-hidden": "true",
21
+ className: cn("relative inline-grid shrink-0 place-items-center", SIZE[size], className),
22
+ ...rest,
23
+ children: [
24
+ /* @__PURE__ */ jsx("span", { className: cn("absolute inset-0 rounded-full", TONE[tone]) }),
25
+ pulse && /* @__PURE__ */ jsx(
26
+ "span",
27
+ {
28
+ "data-m22-animated": true,
29
+ className: cn(
30
+ "absolute inset-0 rounded-full motion-safe:animate-[m22-halo_2.4s_ease-out_infinite]",
31
+ TONE[tone]
32
+ )
33
+ }
34
+ )
35
+ ]
36
+ }
37
+ );
38
+ }
39
+ var StatusDot_default = StatusDot;
40
+ export {
41
+ StatusDot,
42
+ StatusDot_default as default
43
+ };
44
+ //# sourceMappingURL=StatusDot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/StatusDot/StatusDot.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type StatusTone = 'success' | 'warning' | 'danger' | 'neutral'\n\nexport interface StatusDotProps extends HTMLAttributes<HTMLSpanElement> {\n /** 7px / 8px. `md` is the default. */\n size?: 'sm' | 'md'\n tone?: StatusTone\n /** A halo that grows and dissolves, for \"live right now\". Off for a settled state. */\n pulse?: boolean\n}\n\nconst SIZE = { sm: 'size-[7px]', md: 'size-2' } as const\n\nconst TONE: Record<StatusTone, string> = {\n success: 'bg-(--ok)',\n warning: 'bg-(--warn)',\n danger: 'bg-(--danger)',\n neutral: 'bg-(--ink-3-aa)',\n}\n\n/**\n * The dot beside a status word.\n *\n * `aria-hidden` without exception: the dot repeats a state the adjacent label\n * already names, and a screen reader announcing \"available\" twice is worse than\n * not announcing the decoration at all. If a call site has no visible label,\n * the fix is a label, not an `aria-label` on the dot.\n *\n * The halo is a separate absolutely-positioned ring rather than a box-shadow,\n * because Law 2 of this system is that a shadow is never blurred — and it is\n * motion-safe, so a reader who asked for less motion gets a static dot.\n *\n * @example\n * <span className=\"inline-flex items-center gap-2\"><StatusDot /> Available</span>\n */\nexport function StatusDot({\n size = 'md',\n tone = 'success',\n pulse = true,\n className,\n ...rest\n}: StatusDotProps) {\n return (\n <span\n aria-hidden=\"true\"\n className={cn('relative inline-grid shrink-0 place-items-center', SIZE[size], className)}\n {...rest}\n >\n <span className={cn('absolute inset-0 rounded-full', TONE[tone])} />\n {pulse && (\n <span\n data-m22-animated\n className={cn(\n 'absolute inset-0 rounded-full motion-safe:animate-[m22-halo_2.4s_ease-out_infinite]',\n TONE[tone],\n )}\n />\n )}\n </span>\n )\n}\n\nexport default StatusDot\n"],"mappings":"AA6CI,SAKE,KALF;AA5CJ,SAAS,UAAU;AAYnB,MAAM,OAAO,EAAE,IAAI,cAAc,IAAI,SAAS;AAE9C,MAAM,OAAmC;AAAA,EACvC,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAiBO,SAAS,UAAU;AAAA,EACxB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAW,GAAG,oDAAoD,KAAK,IAAI,GAAG,SAAS;AAAA,MACtF,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAW,GAAG,iCAAiC,KAAK,IAAI,CAAC,GAAG;AAAA,QACjE,SACC;AAAA,UAAC;AAAA;AAAA,YACC,qBAAiB;AAAA,YACjB,WAAW;AAAA,cACT;AAAA,cACA,KAAK,IAAI;AAAA,YACX;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
@@ -0,0 +1,25 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+ import { StatusTone } from '../StatusDot/StatusDot.js';
4
+
5
+ interface StatusPillProps extends HTMLAttributes<HTMLSpanElement> {
6
+ children: ReactNode;
7
+ tone?: StatusTone;
8
+ /** Pulsing live dot (default) vs a settled one. */
9
+ pulse?: boolean;
10
+ }
11
+ /**
12
+ * A live state, named: a dot plus an uppercase mono label in an outlined pill.
13
+ *
14
+ * One component rather than a dot and a pill assembled per call site, which is
15
+ * how the same "available for work" chip ended up with three different dot
16
+ * sizes and two different pulse timings on one site.
17
+ *
18
+ * @example
19
+ * <StatusPill>Available for work</StatusPill>
20
+ * @example
21
+ * <StatusPill tone="warning" pulse={false}>Degraded</StatusPill>
22
+ */
23
+ declare function StatusPill({ children, tone, pulse, className, ...rest }: StatusPillProps): react.JSX.Element;
24
+
25
+ export { StatusPill, type StatusPillProps, StatusPill as default };
@@ -0,0 +1,31 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ import { StatusDot } from "../StatusDot/StatusDot.js";
4
+ function StatusPill({
5
+ children,
6
+ tone = "success",
7
+ pulse = true,
8
+ className,
9
+ ...rest
10
+ }) {
11
+ return /* @__PURE__ */ jsxs(
12
+ "span",
13
+ {
14
+ className: cn(
15
+ "inline-flex items-center gap-2.5 rounded-(--radius-pill) border border-(--rule-2) bg-(--paper) py-1.5 ps-2.5 pe-3 eyebrow tracking-[0.12em] text-(--ink-2)",
16
+ className
17
+ ),
18
+ ...rest,
19
+ children: [
20
+ /* @__PURE__ */ jsx(StatusDot, { tone, pulse }),
21
+ children
22
+ ]
23
+ }
24
+ );
25
+ }
26
+ var StatusPill_default = StatusPill;
27
+ export {
28
+ StatusPill,
29
+ StatusPill_default as default
30
+ };
31
+ //# sourceMappingURL=StatusPill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/StatusPill/StatusPill.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { StatusDot, type StatusTone } from '../StatusDot/StatusDot'\n\nexport interface StatusPillProps extends HTMLAttributes<HTMLSpanElement> {\n children: ReactNode\n tone?: StatusTone\n /** Pulsing live dot (default) vs a settled one. */\n pulse?: boolean\n}\n\n/**\n * A live state, named: a dot plus an uppercase mono label in an outlined pill.\n *\n * One component rather than a dot and a pill assembled per call site, which is\n * how the same \"available for work\" chip ended up with three different dot\n * sizes and two different pulse timings on one site.\n *\n * @example\n * <StatusPill>Available for work</StatusPill>\n * @example\n * <StatusPill tone=\"warning\" pulse={false}>Degraded</StatusPill>\n */\nexport function StatusPill({\n children,\n tone = 'success',\n pulse = true,\n className,\n ...rest\n}: StatusPillProps) {\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-2.5 rounded-(--radius-pill) border border-(--rule-2) bg-(--paper) py-1.5 ps-2.5 pe-3 eyebrow tracking-[0.12em] text-(--ink-2)',\n className,\n )}\n {...rest}\n >\n <StatusDot tone={tone} pulse={pulse} />\n {children}\n </span>\n )\n}\n\nexport default StatusPill\n"],"mappings":"AA+BI,SAOE,KAPF;AA9BJ,SAAS,UAAU;AACnB,SAAS,iBAAkC;AAqBpC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,aAAU,MAAY,OAAc;AAAA,QACpC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import * as SwitchPrimitive from '@radix-ui/react-switch';
4
+
5
+ type SwitchProps = ComponentProps<typeof SwitchPrimitive.Root>;
6
+ /**
7
+ * A setting that takes effect immediately.
8
+ *
9
+ * Distinct from `Checkbox`, and the distinction is not cosmetic: a switch
10
+ * applies on flip, a checkbox applies on submit. A switch inside a form with a
11
+ * Save button is a lie about when the change happened.
12
+ *
13
+ * The track fills with ink when on and the thumb is paper with a hairline —
14
+ * rather than a white thumb floating on a drop shadow, which this system does
15
+ * not have. The off state is a filled rule-coloured track, so the control still
16
+ * reads as a control on a white page.
17
+ *
18
+ * @example
19
+ * <Field label="Email notifications"><Switch defaultChecked /></Field>
20
+ */
21
+ declare function Switch({ className, ...props }: SwitchProps): react.JSX.Element;
22
+
23
+ export { Switch, type SwitchProps, Switch as default };
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as SwitchPrimitive from "@radix-ui/react-switch";
4
+ import { cn } from "../../lib/cn.js";
5
+ function Switch({ className, ...props }) {
6
+ return /* @__PURE__ */ jsx(
7
+ SwitchPrimitive.Root,
8
+ {
9
+ className: cn(
10
+ "group inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-(--radius-pill) border border-(--rule-2) bg-(--stone) px-0.5 transition-colors duration-(--duration-fast) hover:border-(--rule-hard) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none",
11
+ className
12
+ ),
13
+ ...props,
14
+ children: /* @__PURE__ */ jsx(SwitchPrimitive.Thumb, { className: "block size-3.5 translate-x-0 rounded-full bg-(--paper) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) group-active:w-5 data-[state=checked]:translate-x-4 rtl:data-[state=checked]:-translate-x-4 motion-reduce:transition-none" })
15
+ }
16
+ );
17
+ }
18
+ var Switch_default = Switch;
19
+ export {
20
+ Switch,
21
+ Switch_default as default
22
+ };
23
+ //# sourceMappingURL=Switch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Switch/Switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitive from '@radix-ui/react-switch'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport type SwitchProps = ComponentProps<typeof SwitchPrimitive.Root>\n\n/**\n * A setting that takes effect immediately.\n *\n * Distinct from `Checkbox`, and the distinction is not cosmetic: a switch\n * applies on flip, a checkbox applies on submit. A switch inside a form with a\n * Save button is a lie about when the change happened.\n *\n * The track fills with ink when on and the thumb is paper with a hairline —\n * rather than a white thumb floating on a drop shadow, which this system does\n * not have. The off state is a filled rule-coloured track, so the control still\n * reads as a control on a white page.\n *\n * @example\n * <Field label=\"Email notifications\"><Switch defaultChecked /></Field>\n */\nexport function Switch({ className, ...props }: SwitchProps) {\n return (\n <SwitchPrimitive.Root\n className={cn(\n 'group inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-(--radius-pill) border border-(--rule-2) bg-(--stone) px-0.5 transition-colors duration-(--duration-fast) hover:border-(--rule-hard) data-[state=checked]:border-(--accent) data-[state=checked]:bg-(--accent) disabled:opacity-(--disabled-opacity) disabled:pointer-events-none',\n className,\n )}\n {...props}\n >\n {/* The thumb narrows as it starts moving and rounds out again when it\n lands, which is what makes a 16px slide read as a deliberate flip\n rather than a jump. `transition-[transform,width]` and not `all`, so\n the track's own colour change keeps its shorter duration. */}\n <SwitchPrimitive.Thumb className=\"block size-3.5 translate-x-0 rounded-full bg-(--paper) transition-[transform,width] duration-(--duration-base) ease-(--ease-out-expo) group-active:w-5 data-[state=checked]:translate-x-4 rtl:data-[state=checked]:-translate-x-4 motion-reduce:transition-none\" />\n </SwitchPrimitive.Root>\n )\n}\n\nexport default Switch\n"],"mappings":";AAoCM;AAlCN,YAAY,qBAAqB;AAEjC,SAAS,UAAU;AAmBZ,SAAS,OAAO,EAAE,WAAW,GAAG,MAAM,GAAgB;AAC3D,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAMJ,8BAAC,gBAAgB,OAAhB,EAAsB,WAAU,mQAAkQ;AAAA;AAAA,EACrS;AAEJ;AAEA,IAAO,iBAAQ;","names":[]}
@@ -0,0 +1,96 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, TableHTMLAttributes } from 'react';
3
+
4
+ type TableAlign = 'start' | 'center' | 'end';
5
+ /**
6
+ * Which rules the table draws.
7
+ *
8
+ * `rows` is the default and the right answer for reading down a column: one
9
+ * hairline between records and nothing else, so the eye tracks a line across
10
+ * without a grid competing for it. `grid` adds the vertical rules a dense
11
+ * numeric table needs to keep columns apart. `bordered` puts an edge around the
12
+ * whole thing, for a table that sits loose on a page rather than inside a card
13
+ * that already has one. `none` is for a table inside something that draws its
14
+ * own structure.
15
+ */
16
+ type TableBorders = 'rows' | 'grid' | 'bordered' | 'bordered-grid' | 'none';
17
+ interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
18
+ /**
19
+ * Describes the table for a screen reader, which cannot see the heading above
20
+ * it. Rendered as a visually-hidden `<caption>` — required, because an
21
+ * unnamed table in a page with three tables is unnavigable.
22
+ */
23
+ caption: string;
24
+ /** Prints the caption instead of hiding it. */
25
+ showCaption?: boolean;
26
+ /**
27
+ * Pins the header row while the body scrolls. Needs a bounded height on the
28
+ * container — otherwise the page scrolls, not the table, and nothing sticks.
29
+ */
30
+ stickyHeader?: boolean;
31
+ /** Which rules to draw. See {@link TableBorders}. */
32
+ borders?: TableBorders;
33
+ /** Tightens the row padding, for a table that is mostly numbers. */
34
+ density?: 'comfortable' | 'compact';
35
+ }
36
+ /**
37
+ * A ruled data table.
38
+ *
39
+ * Wrapped in its own horizontally scrolling container, because a table is the
40
+ * one block that legitimately exceeds the measure — and a page that scrolls
41
+ * sideways as a whole is a layout bug, while a table that does is a table.
42
+ *
43
+ * That container is focusable, and it has to be. A scrollable region whose
44
+ * contents are not themselves focusable is unreachable by keyboard: there is no
45
+ * element to Tab to and therefore no way to press an arrow key at it, so the
46
+ * columns past the fold simply do not exist for anyone not using a mouse.
47
+ *
48
+ * No zebra striping at any border setting. In a monochrome system a striped row
49
+ * is a second surface competing with the page ground, and the hairline between
50
+ * rows is already enough to track a line across.
51
+ *
52
+ * @example
53
+ * <Table caption="Deploy history">
54
+ * <THead><TR><TH>Commit</TH><TH align="end" sortable sortDirection={dir} onSort={sort}>Duration</TH></TR></THead>
55
+ * <TBody><TR><TD>a1b2c3d</TD><TD align="end">2m 14s</TD></TR></TBody>
56
+ * </Table>
57
+ * @example
58
+ * <Table caption="Inventory" borders="bordered-grid" density="compact">…</Table>
59
+ */
60
+ declare function Table({ caption, showCaption, stickyHeader, borders, density, className, children, ...rest }: TableProps): react.JSX.Element;
61
+ /** Header group. The rule under it is drawn by the table's border setting. */
62
+ declare function THead({ className, ...rest }: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
63
+ declare function TBody({ className, ...rest }: HTMLAttributes<HTMLTableSectionElement>): react.JSX.Element;
64
+ declare function TR({ className, ...rest }: HTMLAttributes<HTMLTableRowElement>): react.JSX.Element;
65
+ type SortDirection = 'ascending' | 'descending' | 'none';
66
+ interface THProps extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align'> {
67
+ /** Which edge the column's contents sit against. Numbers belong at `end`. */
68
+ align?: TableAlign;
69
+ /** Makes the label a button and shows the sort marker. */
70
+ sortable?: boolean;
71
+ /**
72
+ * Which way this column is sorted. Also set as `aria-sort`, which is the
73
+ * only way a screen reader learns a table is ordered at all — a caret drawn
74
+ * in the header tells it nothing.
75
+ */
76
+ sortDirection?: SortDirection;
77
+ onSort?: () => void;
78
+ }
79
+ /**
80
+ * A column label. Mono and uppercase, so it never reads as data.
81
+ *
82
+ * When sortable, the label becomes a `<button>` INSIDE the `<th>` rather than
83
+ * the `<th>` becoming clickable: a cell with a click handler is not focusable
84
+ * and not announced as a control, so the sort exists only for a mouse.
85
+ *
86
+ * Sorting is per column and opt-in. A table where every header is a button
87
+ * invites the reader to try sorting a column the data cannot be ordered by.
88
+ */
89
+ declare function TH({ align, sortable, sortDirection, onSort, className, children, ...rest }: THProps): react.JSX.Element;
90
+ interface TDProps extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'> {
91
+ /** Which edge the cell's contents sit against. Match the column's header. */
92
+ align?: TableAlign;
93
+ }
94
+ declare function TD({ align, className, ...rest }: TDProps): react.JSX.Element;
95
+
96
+ export { type SortDirection, TBody, TD, type TDProps, TH, type THProps, THead, TR, Table, type TableAlign, type TableBorders, type TableProps, Table as default };