@charcuterie/ui 0.1.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 (223) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +161 -0
  3. package/dist/Alert/Alert.d.ts +88 -0
  4. package/dist/Alert/Alert.d.ts.map +1 -0
  5. package/dist/Alert/Alert.js +67 -0
  6. package/dist/Alert/Alert.js.map +1 -0
  7. package/dist/Badge/Badge.d.ts +54 -0
  8. package/dist/Badge/Badge.d.ts.map +1 -0
  9. package/dist/Badge/Badge.js +54 -0
  10. package/dist/Badge/Badge.js.map +1 -0
  11. package/dist/Badge/useIsTextClipped.d.ts +22 -0
  12. package/dist/Badge/useIsTextClipped.d.ts.map +1 -0
  13. package/dist/Badge/useIsTextClipped.js +57 -0
  14. package/dist/Badge/useIsTextClipped.js.map +1 -0
  15. package/dist/Button/Button.d.ts +58 -0
  16. package/dist/Button/Button.d.ts.map +1 -0
  17. package/dist/Button/Button.js +37 -0
  18. package/dist/Button/Button.js.map +1 -0
  19. package/dist/Card/Card.d.ts +44 -0
  20. package/dist/Card/Card.d.ts.map +1 -0
  21. package/dist/Card/Card.js +59 -0
  22. package/dist/Card/Card.js.map +1 -0
  23. package/dist/EmptyState/EmptyState.d.ts +42 -0
  24. package/dist/EmptyState/EmptyState.d.ts.map +1 -0
  25. package/dist/EmptyState/EmptyState.js +37 -0
  26. package/dist/EmptyState/EmptyState.js.map +1 -0
  27. package/dist/IconButton/IconButton.d.ts +34 -0
  28. package/dist/IconButton/IconButton.d.ts.map +1 -0
  29. package/dist/IconButton/IconButton.js +23 -0
  30. package/dist/IconButton/IconButton.js.map +1 -0
  31. package/dist/LiveStatusIndicator/LiveStatusIndicator.d.ts +37 -0
  32. package/dist/LiveStatusIndicator/LiveStatusIndicator.d.ts.map +1 -0
  33. package/dist/LiveStatusIndicator/LiveStatusIndicator.js +39 -0
  34. package/dist/LiveStatusIndicator/LiveStatusIndicator.js.map +1 -0
  35. package/dist/MediaTile/MediaTile.d.ts +46 -0
  36. package/dist/MediaTile/MediaTile.d.ts.map +1 -0
  37. package/dist/MediaTile/MediaTile.js +79 -0
  38. package/dist/MediaTile/MediaTile.js.map +1 -0
  39. package/dist/MediaTile/mediaStatus.d.ts +22 -0
  40. package/dist/MediaTile/mediaStatus.d.ts.map +1 -0
  41. package/dist/MediaTile/mediaStatus.js +24 -0
  42. package/dist/MediaTile/mediaStatus.js.map +1 -0
  43. package/dist/Modal/Modal.d.ts +66 -0
  44. package/dist/Modal/Modal.d.ts.map +1 -0
  45. package/dist/Modal/Modal.js +111 -0
  46. package/dist/Modal/Modal.js.map +1 -0
  47. package/dist/Modal/lockScrollBehind.d.ts +22 -0
  48. package/dist/Modal/lockScrollBehind.d.ts.map +1 -0
  49. package/dist/Modal/lockScrollBehind.js +48 -0
  50. package/dist/Modal/lockScrollBehind.js.map +1 -0
  51. package/dist/Popover/Popover.d.ts +66 -0
  52. package/dist/Popover/Popover.d.ts.map +1 -0
  53. package/dist/Popover/Popover.js +88 -0
  54. package/dist/Popover/Popover.js.map +1 -0
  55. package/dist/ProgressBar/ProgressBar.d.ts +51 -0
  56. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -0
  57. package/dist/ProgressBar/ProgressBar.js +50 -0
  58. package/dist/ProgressBar/ProgressBar.js.map +1 -0
  59. package/dist/ProgressBar/progressValue.d.ts +44 -0
  60. package/dist/ProgressBar/progressValue.d.ts.map +1 -0
  61. package/dist/ProgressBar/progressValue.js +55 -0
  62. package/dist/ProgressBar/progressValue.js.map +1 -0
  63. package/dist/SegmentedControl/SegmentedControl.d.ts +59 -0
  64. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -0
  65. package/dist/SegmentedControl/SegmentedControl.js +129 -0
  66. package/dist/SegmentedControl/SegmentedControl.js.map +1 -0
  67. package/dist/SegmentedControl/SegmentedOption.d.ts +28 -0
  68. package/dist/SegmentedControl/SegmentedOption.d.ts.map +1 -0
  69. package/dist/SegmentedControl/SegmentedOption.js +40 -0
  70. package/dist/SegmentedControl/SegmentedOption.js.map +1 -0
  71. package/dist/Skeleton/Skeleton.d.ts +30 -0
  72. package/dist/Skeleton/Skeleton.d.ts.map +1 -0
  73. package/dist/Skeleton/Skeleton.js +43 -0
  74. package/dist/Skeleton/Skeleton.js.map +1 -0
  75. package/dist/Spinner/Spinner.d.ts +37 -0
  76. package/dist/Spinner/Spinner.d.ts.map +1 -0
  77. package/dist/Spinner/Spinner.js +33 -0
  78. package/dist/Spinner/Spinner.js.map +1 -0
  79. package/dist/Tabs/TabTrigger.d.ts +32 -0
  80. package/dist/Tabs/TabTrigger.d.ts.map +1 -0
  81. package/dist/Tabs/TabTrigger.js +62 -0
  82. package/dist/Tabs/TabTrigger.js.map +1 -0
  83. package/dist/Tabs/Tabs.d.ts +95 -0
  84. package/dist/Tabs/Tabs.d.ts.map +1 -0
  85. package/dist/Tabs/Tabs.js +195 -0
  86. package/dist/Tabs/Tabs.js.map +1 -0
  87. package/dist/VisuallyHidden/VisuallyHidden.d.ts +25 -0
  88. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  89. package/dist/VisuallyHidden/VisuallyHidden.js +23 -0
  90. package/dist/VisuallyHidden/VisuallyHidden.js.map +1 -0
  91. package/dist/controlStyles.d.ts +50 -0
  92. package/dist/controlStyles.d.ts.map +1 -0
  93. package/dist/controlStyles.js +60 -0
  94. package/dist/controlStyles.js.map +1 -0
  95. package/dist/expectNoAxeViolations.testHelpers.d.ts +19 -0
  96. package/dist/expectNoAxeViolations.testHelpers.d.ts.map +1 -0
  97. package/dist/expectNoAxeViolations.testHelpers.js +32 -0
  98. package/dist/expectNoAxeViolations.testHelpers.js.map +1 -0
  99. package/dist/index.d.ts +75 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +55 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/intentStyles.d.ts +76 -0
  104. package/dist/intentStyles.d.ts.map +1 -0
  105. package/dist/intentStyles.js +150 -0
  106. package/dist/intentStyles.js.map +1 -0
  107. package/dist/mountStory.testHelpers.d.ts +30 -0
  108. package/dist/mountStory.testHelpers.d.ts.map +1 -0
  109. package/dist/mountStory.testHelpers.js +19 -0
  110. package/dist/mountStory.testHelpers.js.map +1 -0
  111. package/dist/statusIntent.d.ts +33 -0
  112. package/dist/statusIntent.d.ts.map +1 -0
  113. package/dist/statusIntent.js +99 -0
  114. package/dist/statusIntent.js.map +1 -0
  115. package/dist/styles.css +130 -0
  116. package/dist/testing/expectAgentDrivable.d.ts +53 -0
  117. package/dist/testing/expectAgentDrivable.d.ts.map +1 -0
  118. package/dist/testing/expectAgentDrivable.js +163 -0
  119. package/dist/testing/expectAgentDrivable.js.map +1 -0
  120. package/dist/testing/index.d.ts +11 -0
  121. package/dist/testing/index.d.ts.map +1 -0
  122. package/dist/testing/index.js +10 -0
  123. package/dist/testing/index.js.map +1 -0
  124. package/dist/toClassName.d.ts +12 -0
  125. package/dist/toClassName.d.ts.map +1 -0
  126. package/dist/toClassName.js +12 -0
  127. package/dist/toClassName.js.map +1 -0
  128. package/dist/tokens.d.ts +16 -0
  129. package/dist/tokens.d.ts.map +1 -0
  130. package/dist/tokens.js +16 -0
  131. package/dist/tokens.js.map +1 -0
  132. package/package.json +70 -0
  133. package/src/Alert/Alert.mdx +70 -0
  134. package/src/Alert/Alert.stories.tsx +218 -0
  135. package/src/Alert/Alert.test.tsx +112 -0
  136. package/src/Alert/Alert.tsx +166 -0
  137. package/src/Badge/Badge.mdx +43 -0
  138. package/src/Badge/Badge.stories.tsx +229 -0
  139. package/src/Badge/Badge.test.tsx +153 -0
  140. package/src/Badge/Badge.tsx +130 -0
  141. package/src/Badge/useIsTextClipped.ts +77 -0
  142. package/src/Button/Button.mdx +44 -0
  143. package/src/Button/Button.stories.tsx +175 -0
  144. package/src/Button/Button.test.tsx +114 -0
  145. package/src/Button/Button.tsx +125 -0
  146. package/src/Card/Card.mdx +53 -0
  147. package/src/Card/Card.stories.tsx +207 -0
  148. package/src/Card/Card.test.tsx +56 -0
  149. package/src/Card/Card.tsx +142 -0
  150. package/src/EmptyState/EmptyState.mdx +40 -0
  151. package/src/EmptyState/EmptyState.stories.tsx +183 -0
  152. package/src/EmptyState/EmptyState.test.tsx +41 -0
  153. package/src/EmptyState/EmptyState.tsx +114 -0
  154. package/src/IconButton/IconButton.mdx +34 -0
  155. package/src/IconButton/IconButton.stories.tsx +191 -0
  156. package/src/IconButton/IconButton.test.tsx +70 -0
  157. package/src/IconButton/IconButton.tsx +54 -0
  158. package/src/LiveStatusIndicator/LiveStatusIndicator.mdx +43 -0
  159. package/src/LiveStatusIndicator/LiveStatusIndicator.stories.tsx +199 -0
  160. package/src/LiveStatusIndicator/LiveStatusIndicator.test.tsx +63 -0
  161. package/src/LiveStatusIndicator/LiveStatusIndicator.tsx +116 -0
  162. package/src/MediaTile/MediaTile.mdx +51 -0
  163. package/src/MediaTile/MediaTile.stories.tsx +211 -0
  164. package/src/MediaTile/MediaTile.test.tsx +41 -0
  165. package/src/MediaTile/MediaTile.tsx +227 -0
  166. package/src/MediaTile/mediaStatus.ts +28 -0
  167. package/src/Modal/Modal.mdx +113 -0
  168. package/src/Modal/Modal.stories.tsx +272 -0
  169. package/src/Modal/Modal.test.tsx +248 -0
  170. package/src/Modal/Modal.tsx +228 -0
  171. package/src/Modal/lockScrollBehind.ts +59 -0
  172. package/src/Popover/Popover.mdx +102 -0
  173. package/src/Popover/Popover.stories.tsx +247 -0
  174. package/src/Popover/Popover.test.tsx +191 -0
  175. package/src/Popover/Popover.tsx +164 -0
  176. package/src/ProgressBar/ProgressBar.mdx +48 -0
  177. package/src/ProgressBar/ProgressBar.stories.tsx +232 -0
  178. package/src/ProgressBar/ProgressBar.test.tsx +74 -0
  179. package/src/ProgressBar/ProgressBar.tsx +172 -0
  180. package/src/ProgressBar/progressValue.test.ts +101 -0
  181. package/src/ProgressBar/progressValue.ts +87 -0
  182. package/src/SegmentedControl/SegmentedControl.mdx +71 -0
  183. package/src/SegmentedControl/SegmentedControl.stories.tsx +150 -0
  184. package/src/SegmentedControl/SegmentedControl.test.tsx +208 -0
  185. package/src/SegmentedControl/SegmentedControl.tsx +219 -0
  186. package/src/SegmentedControl/SegmentedOption.tsx +94 -0
  187. package/src/Skeleton/Skeleton.mdx +30 -0
  188. package/src/Skeleton/Skeleton.stories.tsx +130 -0
  189. package/src/Skeleton/Skeleton.test.tsx +47 -0
  190. package/src/Skeleton/Skeleton.tsx +106 -0
  191. package/src/Spinner/Spinner.mdx +36 -0
  192. package/src/Spinner/Spinner.stories.tsx +118 -0
  193. package/src/Spinner/Spinner.test.tsx +38 -0
  194. package/src/Spinner/Spinner.tsx +92 -0
  195. package/src/Tabs/TabTrigger.tsx +126 -0
  196. package/src/Tabs/Tabs.mdx +126 -0
  197. package/src/Tabs/Tabs.stories.tsx +187 -0
  198. package/src/Tabs/Tabs.test.tsx +283 -0
  199. package/src/Tabs/Tabs.tsx +333 -0
  200. package/src/VisuallyHidden/VisuallyHidden.mdx +29 -0
  201. package/src/VisuallyHidden/VisuallyHidden.stories.tsx +58 -0
  202. package/src/VisuallyHidden/VisuallyHidden.test.tsx +31 -0
  203. package/src/VisuallyHidden/VisuallyHidden.tsx +43 -0
  204. package/src/argTypes.storyHelpers.ts +115 -0
  205. package/src/board.storyHelpers.tsx +150 -0
  206. package/src/controlStyles.ts +79 -0
  207. package/src/expectNoAxeViolations.testHelpers.ts +41 -0
  208. package/src/icons.storyHelpers.tsx +116 -0
  209. package/src/index.ts +132 -0
  210. package/src/intentStyles.ts +207 -0
  211. package/src/mdxReferences.test.ts +95 -0
  212. package/src/mountStory.testHelpers.ts +41 -0
  213. package/src/sourceRules.test.ts +303 -0
  214. package/src/statusIntent.test.ts +136 -0
  215. package/src/statusIntent.ts +137 -0
  216. package/src/storyControls.test.ts +128 -0
  217. package/src/styles.css +130 -0
  218. package/src/tailwindCandidates.test.ts +377 -0
  219. package/src/testing/expectAgentDrivable.test.tsx +71 -0
  220. package/src/testing/expectAgentDrivable.ts +273 -0
  221. package/src/testing/index.ts +17 -0
  222. package/src/toClassName.ts +13 -0
  223. package/src/tokens.ts +16 -0
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `@charcuterie/ui` — the one sanctioned barrel.
3
+ *
4
+ * Components never import each other through this file; they import
5
+ * the sibling module directly (`../Spinner/Spinner.tsx`). A barrel
6
+ * that a package's own internals go through turns every component
7
+ * into a dependency of every other one, which is how a 3 KB
8
+ * `Spinner` starts pulling in `MediaTile`.
9
+ *
10
+ * `"./src/*"` is also in `exports`, for the budget-sensitive
11
+ * consumers — `castkit/packages/slatecast` has 60 KB gz to spend and
12
+ * should be able to reach one component without the barrel. Same
13
+ * arrangement as `mux-magic/packages/tools`.
14
+ *
15
+ * The token **types and values** are re-exported at
16
+ * `@charcuterie/ui/tokens` rather than from here, so a React
17
+ * consumer's TypeScript never names a second package while Satori
18
+ * still gets `@charcuterie/tokens` with no React in sight.
19
+ *
20
+ * The token **stylesheet** is not, and M5 is why. There was a
21
+ * `"./tokens.css": "./node_modules/@charcuterie/tokens/dist/theme.css"`
22
+ * export here, and it resolved to nothing in the first real
23
+ * consumer: a hoisting linker puts `@charcuterie/tokens` at the
24
+ * *project* root, not inside this package, so that path exists only
25
+ * under a nesting one. A CSS `@import` fails silently in Tailwind —
26
+ * no error, no utilities, an unstyled app — which is exactly the
27
+ * class of failure `portal:` was chosen to catch
28
+ * ([decision](../../../docs/decisions/2026-07-29-consumers-link-tokens-by-portal-until-publish.md)).
29
+ * So a consumer installs both package names and imports
30
+ * `@charcuterie/tokens/theme.css` by its own name, which is what
31
+ * this package's README has always told it to do.
32
+ */
33
+ export { Alert } from "./Alert/Alert.js";
34
+ export { Badge } from "./Badge/Badge.js";
35
+ export { Button } from "./Button/Button.js";
36
+ export { Card } from "./Card/Card.js";
37
+ export { BADGE_SIZE_CLASS, CONTROL_SIZE_CLASS, DOT_SIZE_CLASS, ICON_CONTROL_SIZE_CLASS, MIN_TOUCH_TARGET_CLASS, SPINNER_SIZE_CLASS, } from "./controlStyles.js";
38
+ export { EmptyState } from "./EmptyState/EmptyState.js";
39
+ export { IconButton } from "./IconButton/IconButton.js";
40
+ export { DISABLED_CLASS, FOCUS_RING_CLASS, INTENT_APPEARANCE_CLASS, INTENT_CONTENT_CLASS, INTENT_HOVER_CLASS, INTENT_SOLID_FILL_CLASS, } from "./intentStyles.js";
41
+ export { LiveStatusIndicator } from "./LiveStatusIndicator/LiveStatusIndicator.js";
42
+ export { MediaTile } from "./MediaTile/MediaTile.js";
43
+ export { mediaTransitions } from "./MediaTile/mediaStatus.js";
44
+ export { Modal } from "./Modal/Modal.js";
45
+ export { Popover } from "./Popover/Popover.js";
46
+ export { ProgressBar } from "./ProgressBar/ProgressBar.js";
47
+ export { getProgressIntent, toProgressPercent, toProgressValue, } from "./ProgressBar/progressValue.js";
48
+ export { SegmentedControl } from "./SegmentedControl/SegmentedControl.js";
49
+ export { Skeleton } from "./Skeleton/Skeleton.js";
50
+ export { Spinner } from "./Spinner/Spinner.js";
51
+ export { getAsyncIntent, getAsyncLabel, getConnectionIntent, getConnectionLabel, getIsConnectionBusy, } from "./statusIntent.js";
52
+ export { Tabs } from "./Tabs/Tabs.js";
53
+ export { toClassName } from "./toClassName.js";
54
+ export { VisuallyHidden } from "./VisuallyHidden/VisuallyHidden.js";
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAqB,CAAA;AAM5C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAiB,CAAA;AAEtC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,oBAAoB,CAAA;AAK3B,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA6B,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA6B,CAAA;AAExD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,mBAAmB,CAAA;AAK1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA+C,CAAA;AAKnF,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA2B,CAAA;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAK7D,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAmB,CAAA;AAEzC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAuB,CAAA;AAK/C,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA+B,CAAA;AAE3D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gCAAgC,CAAA;AAKvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAyC,CAAA;AAK1E,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAyB,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAuB,CAAA;AAC/C,OAAO,EACL,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAA;AAO1B,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAiB,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAqC,CAAA"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * One intent → class map, for the whole fleet.
3
+ *
4
+ * This file is what M5 deletes from rip-deck. `TONE_CLASS` is
5
+ * declared *identically* in `VerdictBadge.tsx` and
6
+ * `TowerAlerts.tsx` today — hardcoded hexes and `slate-400`s, no
7
+ * light mode, no relationship to mux-magic's `statusClassMap` which
8
+ * spells the same idea a fourth way. Six intents x four
9
+ * appearances, named once, is the generalisation.
10
+ *
11
+ * **Every class name is written out in full, and that is not
12
+ * negotiable.** Tailwind v4 scans source text for *complete* class
13
+ * strings, so `` `bg-intent-${intent}-solid` `` generates nothing
14
+ * at all — the element renders unstyled with no error, no warning,
15
+ * and no failing test unless something checks. Something does:
16
+ * `tailwindCandidates.test.ts` compiles every literal in this
17
+ * package through the real Tailwind and fails on any candidate
18
+ * Tailwind cannot generate. That test is the reason this file is
19
+ * allowed to be 24 lines of near-duplication instead of a loop.
20
+ */
21
+ import type { IntentName } from "@charcuterie/tokens";
22
+ /**
23
+ * Four treatments, and the split is semantic rather than cosmetic:
24
+ *
25
+ * - `solid` — the saturated fill. One per view, usually; it is the
26
+ * thing you are meant to press.
27
+ * - `soft` — the tinted treatment the fleet already reaches for on
28
+ * status pills (`bg-blue-950 text-blue-300` in mux-magic,
29
+ * `bg-amber-950/40 text-amber-200` in rip-deck).
30
+ * - `outline` — border and content only. Reads as secondary
31
+ * without claiming a surface.
32
+ * - `ghost` — nothing until hovered. Toolbars and icon rows.
33
+ */
34
+ export type IntentAppearance = "ghost" | "outline" | "soft" | "solid";
35
+ export declare const INTENT_APPEARANCE_CLASS: Record<IntentName, Record<IntentAppearance, string>>;
36
+ /**
37
+ * Hover, split out rather than folded into the appearance above —
38
+ * because most things wearing an intent are **not** interactive.
39
+ *
40
+ * A `Badge` that lightens under the cursor is telling the user it
41
+ * can be clicked, and rip-deck's verdict pills (which cannot) would
42
+ * inherit exactly that if the two maps were one. So an interactive
43
+ * component opts in, and a static one cannot accidentally opt in.
44
+ *
45
+ * `solid` hovers toward `solidHover`; the other three land on the
46
+ * intent's tinted surface, which is what gives a ghost toolbar
47
+ * button something to become.
48
+ */
49
+ export declare const INTENT_HOVER_CLASS: Record<IntentName, Record<IntentAppearance, string>>;
50
+ /**
51
+ * The fill colour on its own, for the parts of a component that are
52
+ * a bar or a dot rather than a box — a `ProgressBar`'s fill, a
53
+ * `LiveStatusIndicator`'s dot. Same values, no border and no hover,
54
+ * because neither of those is a surface anyone can point at.
55
+ */
56
+ export declare const INTENT_SOLID_FILL_CLASS: Record<IntentName, string>;
57
+ /** The same six as text, for a glyph or a dot's ring. */
58
+ export declare const INTENT_CONTENT_CLASS: Record<IntentName, string>;
59
+ /**
60
+ * The focus ring, in one place, reading the variant's own width and
61
+ * offset.
62
+ *
63
+ * `focus-visible` rather than `focus`: a mouse click on a button
64
+ * should not leave a ring behind, and every hand-rolled button in
65
+ * the inventory either rings on click or — more often — has
66
+ * `outline-none` with nothing put back.
67
+ */
68
+ export declare const FOCUS_RING_CLASS = "focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-focus-ring";
69
+ /**
70
+ * WCAG 1.4.3 exempts *inactive* controls from contrast, which is
71
+ * the only reason `content.disabled` is allowed to be this quiet —
72
+ * and the exemption is honest only on something actually disabled.
73
+ * Keeping it in one constant is what stops it drifting onto prose.
74
+ */
75
+ export declare const DISABLED_CLASS = "disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface-sunken disabled:text-content-disabled disabled:shadow-none";
76
+ //# sourceMappingURL=intentStyles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intentStyles.d.ts","sourceRoot":"","sources":["../src/intentStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,gBAAgB,GACxB,OAAO,GACP,SAAS,GACT,MAAM,GACN,OAAO,CAAA;AAEX,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,UAAU,EACV,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAwDjC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CACrC,UAAU,EACV,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAsCjC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,UAAU,EACV,MAAM,CAQP,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CACvC,UAAU,EACV,MAAM,CAQP,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,sKACwI,CAAA;AAErK;;;;;GAKG;AACH,eAAO,MAAM,cAAc,6IACiH,CAAA"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * One intent → class map, for the whole fleet.
3
+ *
4
+ * This file is what M5 deletes from rip-deck. `TONE_CLASS` is
5
+ * declared *identically* in `VerdictBadge.tsx` and
6
+ * `TowerAlerts.tsx` today — hardcoded hexes and `slate-400`s, no
7
+ * light mode, no relationship to mux-magic's `statusClassMap` which
8
+ * spells the same idea a fourth way. Six intents x four
9
+ * appearances, named once, is the generalisation.
10
+ *
11
+ * **Every class name is written out in full, and that is not
12
+ * negotiable.** Tailwind v4 scans source text for *complete* class
13
+ * strings, so `` `bg-intent-${intent}-solid` `` generates nothing
14
+ * at all — the element renders unstyled with no error, no warning,
15
+ * and no failing test unless something checks. Something does:
16
+ * `tailwindCandidates.test.ts` compiles every literal in this
17
+ * package through the real Tailwind and fails on any candidate
18
+ * Tailwind cannot generate. That test is the reason this file is
19
+ * allowed to be 24 lines of near-duplication instead of a loop.
20
+ */
21
+ export const INTENT_APPEARANCE_CLASS = {
22
+ neutral: {
23
+ ghost: "border-transparent bg-transparent text-content-primary",
24
+ outline: "border-intent-neutral-border bg-transparent text-intent-neutral-content",
25
+ soft: "border-intent-neutral-border bg-intent-neutral-surface text-intent-neutral-content",
26
+ solid: "border-transparent bg-intent-neutral-solid text-intent-neutral-on-solid",
27
+ },
28
+ accent: {
29
+ ghost: "border-transparent bg-transparent text-intent-accent-content",
30
+ outline: "border-intent-accent-border bg-transparent text-intent-accent-content",
31
+ soft: "border-intent-accent-border bg-intent-accent-surface text-intent-accent-content",
32
+ solid: "border-transparent bg-intent-accent-solid text-intent-accent-on-solid",
33
+ },
34
+ success: {
35
+ ghost: "border-transparent bg-transparent text-intent-success-content",
36
+ outline: "border-intent-success-border bg-transparent text-intent-success-content",
37
+ soft: "border-intent-success-border bg-intent-success-surface text-intent-success-content",
38
+ solid: "border-transparent bg-intent-success-solid text-intent-success-on-solid",
39
+ },
40
+ warning: {
41
+ ghost: "border-transparent bg-transparent text-intent-warning-content",
42
+ outline: "border-intent-warning-border bg-transparent text-intent-warning-content",
43
+ soft: "border-intent-warning-border bg-intent-warning-surface text-intent-warning-content",
44
+ solid: "border-transparent bg-intent-warning-solid text-intent-warning-on-solid",
45
+ },
46
+ danger: {
47
+ ghost: "border-transparent bg-transparent text-intent-danger-content",
48
+ outline: "border-intent-danger-border bg-transparent text-intent-danger-content",
49
+ soft: "border-intent-danger-border bg-intent-danger-surface text-intent-danger-content",
50
+ solid: "border-transparent bg-intent-danger-solid text-intent-danger-on-solid",
51
+ },
52
+ info: {
53
+ ghost: "border-transparent bg-transparent text-intent-info-content",
54
+ outline: "border-intent-info-border bg-transparent text-intent-info-content",
55
+ soft: "border-intent-info-border bg-intent-info-surface text-intent-info-content",
56
+ solid: "border-transparent bg-intent-info-solid text-intent-info-on-solid",
57
+ },
58
+ };
59
+ /**
60
+ * Hover, split out rather than folded into the appearance above —
61
+ * because most things wearing an intent are **not** interactive.
62
+ *
63
+ * A `Badge` that lightens under the cursor is telling the user it
64
+ * can be clicked, and rip-deck's verdict pills (which cannot) would
65
+ * inherit exactly that if the two maps were one. So an interactive
66
+ * component opts in, and a static one cannot accidentally opt in.
67
+ *
68
+ * `solid` hovers toward `solidHover`; the other three land on the
69
+ * intent's tinted surface, which is what gives a ghost toolbar
70
+ * button something to become.
71
+ */
72
+ export const INTENT_HOVER_CLASS = {
73
+ neutral: {
74
+ ghost: "hover:bg-intent-neutral-surface",
75
+ outline: "hover:bg-intent-neutral-surface",
76
+ soft: "hover:bg-intent-neutral-surface-hover",
77
+ solid: "hover:bg-intent-neutral-solid-hover",
78
+ },
79
+ accent: {
80
+ ghost: "hover:bg-intent-accent-surface",
81
+ outline: "hover:bg-intent-accent-surface",
82
+ soft: "hover:bg-intent-accent-surface-hover",
83
+ solid: "hover:bg-intent-accent-solid-hover",
84
+ },
85
+ success: {
86
+ ghost: "hover:bg-intent-success-surface",
87
+ outline: "hover:bg-intent-success-surface",
88
+ soft: "hover:bg-intent-success-surface-hover",
89
+ solid: "hover:bg-intent-success-solid-hover",
90
+ },
91
+ warning: {
92
+ ghost: "hover:bg-intent-warning-surface",
93
+ outline: "hover:bg-intent-warning-surface",
94
+ soft: "hover:bg-intent-warning-surface-hover",
95
+ solid: "hover:bg-intent-warning-solid-hover",
96
+ },
97
+ danger: {
98
+ ghost: "hover:bg-intent-danger-surface",
99
+ outline: "hover:bg-intent-danger-surface",
100
+ soft: "hover:bg-intent-danger-surface-hover",
101
+ solid: "hover:bg-intent-danger-solid-hover",
102
+ },
103
+ info: {
104
+ ghost: "hover:bg-intent-info-surface",
105
+ outline: "hover:bg-intent-info-surface",
106
+ soft: "hover:bg-intent-info-surface-hover",
107
+ solid: "hover:bg-intent-info-solid-hover",
108
+ },
109
+ };
110
+ /**
111
+ * The fill colour on its own, for the parts of a component that are
112
+ * a bar or a dot rather than a box — a `ProgressBar`'s fill, a
113
+ * `LiveStatusIndicator`'s dot. Same values, no border and no hover,
114
+ * because neither of those is a surface anyone can point at.
115
+ */
116
+ export const INTENT_SOLID_FILL_CLASS = {
117
+ neutral: "bg-intent-neutral-solid",
118
+ accent: "bg-intent-accent-solid",
119
+ success: "bg-intent-success-solid",
120
+ warning: "bg-intent-warning-solid",
121
+ danger: "bg-intent-danger-solid",
122
+ info: "bg-intent-info-solid",
123
+ };
124
+ /** The same six as text, for a glyph or a dot's ring. */
125
+ export const INTENT_CONTENT_CLASS = {
126
+ neutral: "text-intent-neutral-content",
127
+ accent: "text-intent-accent-content",
128
+ success: "text-intent-success-content",
129
+ warning: "text-intent-warning-content",
130
+ danger: "text-intent-danger-content",
131
+ info: "text-intent-info-content",
132
+ };
133
+ /**
134
+ * The focus ring, in one place, reading the variant's own width and
135
+ * offset.
136
+ *
137
+ * `focus-visible` rather than `focus`: a mouse click on a button
138
+ * should not leave a ring behind, and every hand-rolled button in
139
+ * the inventory either rings on click or — more often — has
140
+ * `outline-none` with nothing put back.
141
+ */
142
+ export const FOCUS_RING_CLASS = "focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-focus-ring";
143
+ /**
144
+ * WCAG 1.4.3 exempts *inactive* controls from contrast, which is
145
+ * the only reason `content.disabled` is allowed to be this quiet —
146
+ * and the exemption is honest only on something actually disabled.
147
+ * Keeping it in one constant is what stops it drifting onto prose.
148
+ */
149
+ export const DISABLED_CLASS = "disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface-sunken disabled:text-content-disabled disabled:shadow-none";
150
+ //# sourceMappingURL=intentStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intentStyles.js","sourceRoot":"","sources":["../src/intentStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsBH,MAAM,CAAC,MAAM,uBAAuB,GAGhC;IACF,OAAO,EAAE;QACP,KAAK,EACH,wDAAwD;QAC1D,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,MAAM,EAAE;QACN,KAAK,EACH,8DAA8D;QAChE,OAAO,EACL,uEAAuE;QACzE,IAAI,EAAE,iFAAiF;QACvF,KAAK,EACH,uEAAuE;KAC1E;IACD,OAAO,EAAE;QACP,KAAK,EACH,+DAA+D;QACjE,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,OAAO,EAAE;QACP,KAAK,EACH,+DAA+D;QACjE,OAAO,EACL,yEAAyE;QAC3E,IAAI,EAAE,oFAAoF;QAC1F,KAAK,EACH,yEAAyE;KAC5E;IACD,MAAM,EAAE;QACN,KAAK,EACH,8DAA8D;QAChE,OAAO,EACL,uEAAuE;QACzE,IAAI,EAAE,iFAAiF;QACvF,KAAK,EACH,uEAAuE;KAC1E;IACD,IAAI,EAAE;QACJ,KAAK,EACH,4DAA4D;QAC9D,OAAO,EACL,mEAAmE;QACrE,IAAI,EAAE,2EAA2E;QACjF,KAAK,EACH,mEAAmE;KACtE;CACF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAG3B;IACF,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,gCAAgC;QACvC,OAAO,EAAE,gCAAgC;QACzC,IAAI,EAAE,sCAAsC;QAC5C,KAAK,EAAE,oCAAoC;KAC5C;IACD,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,OAAO,EAAE;QACP,KAAK,EAAE,iCAAiC;QACxC,OAAO,EAAE,iCAAiC;QAC1C,IAAI,EAAE,uCAAuC;QAC7C,KAAK,EAAE,qCAAqC;KAC7C;IACD,MAAM,EAAE;QACN,KAAK,EAAE,gCAAgC;QACvC,OAAO,EAAE,gCAAgC;QACzC,IAAI,EAAE,sCAAsC;QAC5C,KAAK,EAAE,oCAAoC;KAC5C;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,8BAA8B;QACrC,OAAO,EAAE,8BAA8B;QACvC,IAAI,EAAE,oCAAoC;QAC1C,KAAK,EAAE,kCAAkC;KAC1C;CACF,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAGhC;IACF,OAAO,EAAE,yBAAyB;IAClC,MAAM,EAAE,wBAAwB;IAChC,OAAO,EAAE,yBAAyB;IAClC,OAAO,EAAE,yBAAyB;IAClC,MAAM,EAAE,wBAAwB;IAChC,IAAI,EAAE,sBAAsB;CAC7B,CAAA;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,oBAAoB,GAG7B;IACF,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE,4BAA4B;IACpC,OAAO,EAAE,6BAA6B;IACtC,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE,4BAA4B;IACpC,IAAI,EAAE,0BAA0B;CACjC,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,mKAAmK,CAAA;AAErK;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,0IAA0I,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { within } from "storybook/test";
2
+ type Canvas = ReturnType<typeof within>;
3
+ /**
4
+ * Structural on purpose — the one thing this helper needs off a
5
+ * composed story. `ComposedStoryFn` is invariant in its `Args`, so
6
+ * naming it here would force every caller to restate the props of
7
+ * the component it is testing.
8
+ */
9
+ type MountableStory = {
10
+ run: (context: {
11
+ canvasElement: HTMLElement;
12
+ }) => Promise<void>;
13
+ };
14
+ /**
15
+ * Mount a composed story into a fresh canvas and hand back the same
16
+ * two things a `play` function used to receive.
17
+ *
18
+ * `run()` — rather than rendering the component directly — is what
19
+ * keeps these tests honest after the story/test split: the subject
20
+ * is the story a reader sees, with its args, its decorators, and its
21
+ * loaders, not a second assembly of the same component that can
22
+ * drift from it. It is also what triggers the a11y addon's
23
+ * `afterEach`, so every mount here is an axe run.
24
+ */
25
+ export declare const mountStory: (story: MountableStory) => Promise<{
26
+ canvas: Canvas;
27
+ canvasElement: HTMLElement;
28
+ }>;
29
+ export {};
30
+ //# sourceMappingURL=mountStory.testHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mountStory.testHelpers.d.ts","sourceRoot":"","sources":["../src/mountStory.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;AAEvC;;;;;GAKG;AACH,KAAK,cAAc,GAAG;IACpB,GAAG,EAAE,CAAC,OAAO,EAAE;QACb,aAAa,EAAE,WAAW,CAAA;KAC3B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpB,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,cAAc,KACpB,OAAO,CAAC;IACT,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,WAAW,CAAA;CAC3B,CAQA,CAAA"}
@@ -0,0 +1,19 @@
1
+ import { within } from "storybook/test";
2
+ /**
3
+ * Mount a composed story into a fresh canvas and hand back the same
4
+ * two things a `play` function used to receive.
5
+ *
6
+ * `run()` — rather than rendering the component directly — is what
7
+ * keeps these tests honest after the story/test split: the subject
8
+ * is the story a reader sees, with its args, its decorators, and its
9
+ * loaders, not a second assembly of the same component that can
10
+ * drift from it. It is also what triggers the a11y addon's
11
+ * `afterEach`, so every mount here is an axe run.
12
+ */
13
+ export const mountStory = async (story) => {
14
+ const canvasElement = document.createElement("div");
15
+ document.body.append(canvasElement);
16
+ await story.run({ canvasElement });
17
+ return { canvas: within(canvasElement), canvasElement };
18
+ };
19
+ //# sourceMappingURL=mountStory.testHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mountStory.testHelpers.js","sourceRoot":"","sources":["../src/mountStory.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAgBvC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,KAAqB,EAIpB,EAAE;IACH,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAEnD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAEnC,MAAM,KAAK,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,CAAA;IAElC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,EAAE,aAAa,EAAE,CAAA;AACzD,CAAC,CAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The two shared machines → an intent and a human label.
3
+ *
4
+ * This is the join M2 promised and the reason `Status` earned its
5
+ * place as a fifth state kind. `Badge` and `LiveStatusIndicator`
6
+ * consume `connectionTransitions` / `asyncTransitions` through
7
+ * these functions, so a connection state has **one** colour and
8
+ * **one** wording across the fleet — against four repos that each
9
+ * spell the same four states differently today, and one
10
+ * (`mux-magic`) whose `statusClassMap` is a
11
+ * `Record<string, string>` that silently renders an unknown status
12
+ * with no colour at all.
13
+ *
14
+ * Every switch here ends in `assertNeverStatus`, which is the whole
15
+ * point: add a state to `connectionTransitions` and this file stops
16
+ * compiling. A `Record<string, string>` would have shipped a blank
17
+ * badge instead.
18
+ */
19
+ import type { AsyncStatus, ConnectionStatus } from "@charcuterie/logic";
20
+ import type { IntentName } from "@charcuterie/tokens";
21
+ export declare const getConnectionIntent: (status: ConnectionStatus) => IntentName;
22
+ /**
23
+ * The ellipsis is a real `…`, not three dots, and the trailing one
24
+ * is load-bearing: it is what makes "Reconnecting…" read as
25
+ * in-progress to someone who glances at a wall-mounted panel from
26
+ * across the room.
27
+ */
28
+ export declare const getConnectionLabel: (status: ConnectionStatus) => string;
29
+ /** Whether the state is one a moving affordance should mark. */
30
+ export declare const getIsConnectionBusy: (status: ConnectionStatus) => boolean;
31
+ export declare const getAsyncIntent: (status: AsyncStatus) => IntentName;
32
+ export declare const getAsyncLabel: (status: AsyncStatus) => string;
33
+ //# sourceMappingURL=statusIntent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statusIntent.d.ts","sourceRoot":"","sources":["../src/statusIntent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,KACvB,UAqBF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,KACvB,MAiBF,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,KACvB,OAaF,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,QAAQ,WAAW,KAClB,UAiBF,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,QAAQ,WAAW,KAClB,MAiBF,CAAA"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The two shared machines → an intent and a human label.
3
+ *
4
+ * This is the join M2 promised and the reason `Status` earned its
5
+ * place as a fifth state kind. `Badge` and `LiveStatusIndicator`
6
+ * consume `connectionTransitions` / `asyncTransitions` through
7
+ * these functions, so a connection state has **one** colour and
8
+ * **one** wording across the fleet — against four repos that each
9
+ * spell the same four states differently today, and one
10
+ * (`mux-magic`) whose `statusClassMap` is a
11
+ * `Record<string, string>` that silently renders an unknown status
12
+ * with no colour at all.
13
+ *
14
+ * Every switch here ends in `assertNeverStatus`, which is the whole
15
+ * point: add a state to `connectionTransitions` and this file stops
16
+ * compiling. A `Record<string, string>` would have shipped a blank
17
+ * badge instead.
18
+ */
19
+ import { assertNeverStatus } from "@charcuterie/logic";
20
+ export const getConnectionIntent = (status) => {
21
+ switch (status) {
22
+ case "connected":
23
+ return "success";
24
+ case "connecting":
25
+ return "info";
26
+ // Warning rather than danger, and this is the distinction the
27
+ // shared machine exists for: a user who has seen data wants
28
+ // "lost it, getting it back", not the cold-start spinner they
29
+ // already sat through, and not the red of a dead link either.
30
+ case "reconnecting":
31
+ return "warning";
32
+ case "disconnected":
33
+ return "danger";
34
+ default:
35
+ return assertNeverStatus(status);
36
+ }
37
+ };
38
+ /**
39
+ * The ellipsis is a real `…`, not three dots, and the trailing one
40
+ * is load-bearing: it is what makes "Reconnecting…" read as
41
+ * in-progress to someone who glances at a wall-mounted panel from
42
+ * across the room.
43
+ */
44
+ export const getConnectionLabel = (status) => {
45
+ switch (status) {
46
+ case "connected":
47
+ return "Connected";
48
+ case "connecting":
49
+ return "Connecting…";
50
+ case "reconnecting":
51
+ return "Reconnecting…";
52
+ case "disconnected":
53
+ return "Disconnected";
54
+ default:
55
+ return assertNeverStatus(status);
56
+ }
57
+ };
58
+ /** Whether the state is one a moving affordance should mark. */
59
+ export const getIsConnectionBusy = (status) => {
60
+ switch (status) {
61
+ case "connecting":
62
+ case "reconnecting":
63
+ return true;
64
+ case "connected":
65
+ case "disconnected":
66
+ return false;
67
+ default:
68
+ return assertNeverStatus(status);
69
+ }
70
+ };
71
+ export const getAsyncIntent = (status) => {
72
+ switch (status) {
73
+ case "idle":
74
+ return "neutral";
75
+ case "loading":
76
+ return "info";
77
+ case "success":
78
+ return "success";
79
+ case "error":
80
+ return "danger";
81
+ default:
82
+ return assertNeverStatus(status);
83
+ }
84
+ };
85
+ export const getAsyncLabel = (status) => {
86
+ switch (status) {
87
+ case "idle":
88
+ return "Idle";
89
+ case "loading":
90
+ return "Loading…";
91
+ case "success":
92
+ return "Done";
93
+ case "error":
94
+ return "Failed";
95
+ default:
96
+ return assertNeverStatus(status);
97
+ }
98
+ };
99
+ //# sourceMappingURL=statusIntent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statusIntent.js","sourceRoot":"","sources":["../src/statusIntent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAGtD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACZ,EAAE;IACd,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,SAAS,CAAA;QAElB,KAAK,YAAY;YACf,OAAO,MAAM,CAAA;QAEf,8DAA8D;QAC9D,4DAA4D;QAC5D,8DAA8D;QAC9D,8DAA8D;QAC9D,KAAK,cAAc;YACjB,OAAO,SAAS,CAAA;QAElB,KAAK,cAAc;YACjB,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAwB,EAChB,EAAE;IACV,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW;YACd,OAAO,WAAW,CAAA;QAEpB,KAAK,YAAY;YACf,OAAO,aAAa,CAAA;QAEtB,KAAK,cAAc;YACjB,OAAO,eAAe,CAAA;QAExB,KAAK,cAAc;YACjB,OAAO,cAAc,CAAA;QAEvB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACf,EAAE;IACX,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,IAAI,CAAA;QAEb,KAAK,WAAW,CAAC;QACjB,KAAK,cAAc;YACjB,OAAO,KAAK,CAAA;QAEd;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAAmB,EACP,EAAE;IACd,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,SAAS,CAAA;QAElB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAA;QAEf,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAElB,KAAK,OAAO;YACV,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAmB,EACX,EAAE;IACV,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,MAAM,CAAA;QAEf,KAAK,SAAS;YACZ,OAAO,UAAU,CAAA;QAEnB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAA;QAEf,KAAK,OAAO;YACV,OAAO,QAAQ,CAAA;QAEjB;YACE,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,130 @@
1
+ /* `@charcuterie/ui/styles.css` — the four looping affordances, and
2
+ * nothing else.
3
+ *
4
+ * Every other visual in this package is a Tailwind utility reading
5
+ * a token, which is what makes a scheme/density/variant flip a
6
+ * repaint with no re-render. Animation is the one thing utilities
7
+ * cannot express against our tokens: Tailwind's own `animate-spin`
8
+ * and `animate-pulse` hardcode 1s and 2s, ignore
9
+ * `prefers-reduced-motion`, and are therefore beyond the theme's
10
+ * reach — which is exactly why `MotionTokens` carries a separate
11
+ * `loopFast`/`loopSlow` ramp.
12
+ *
13
+ * Consumers import this once, after `@charcuterie/tokens/theme.css`.
14
+ *
15
+ * The reduced-motion block is not a nicety. `variables.css` already
16
+ * collapses every duration to `0ms`, and for a *transition* that is
17
+ * the whole fix — but `animation: sweep 0ms infinite` still paints
18
+ * its first keyframe forever, and the first keyframe of a sweep is
19
+ * an off-screen bar. So each loop is switched off outright and owes
20
+ * a static fallback that still reads correctly, which is what the
21
+ * `@media` block below supplies. A spinner that stops spinning must
22
+ * still say "loading", and it does: the label is text in a
23
+ * `role="status"`, not the animation. */
24
+
25
+ @keyframes charcuterie-spin {
26
+ to {
27
+ transform: rotate(360deg);
28
+ }
29
+ }
30
+
31
+ @keyframes charcuterie-shimmer {
32
+ from {
33
+ background-position: 100% 0;
34
+ }
35
+
36
+ to {
37
+ background-position: -100% 0;
38
+ }
39
+ }
40
+
41
+ /* Logical, not `translateX`. A sweep is the one animation whose
42
+ * direction carries meaning — it reads as "moving forward" — so in
43
+ * RTL it has to move the other way, and `inset-inline-start` is
44
+ * what makes that free. */
45
+ @keyframes charcuterie-sweep {
46
+ from {
47
+ inset-inline-start: -40%;
48
+ }
49
+
50
+ to {
51
+ inset-inline-start: 100%;
52
+ }
53
+ }
54
+
55
+ @keyframes charcuterie-pulse {
56
+ 0% {
57
+ opacity: 1;
58
+ transform: scale(1);
59
+ }
60
+
61
+ 50% {
62
+ opacity: 0.45;
63
+ transform: scale(0.82);
64
+ }
65
+
66
+ 100% {
67
+ opacity: 1;
68
+ transform: scale(1);
69
+ }
70
+ }
71
+
72
+ .charcuterie-spin {
73
+ animation: charcuterie-spin var(--duration-loop-fast)
74
+ linear infinite;
75
+ }
76
+
77
+ .charcuterie-shimmer {
78
+ background-image: linear-gradient(
79
+ 90deg,
80
+ var(--color-surface-sunken) 0%,
81
+ var(--color-surface-raised) 50%,
82
+ var(--color-surface-sunken) 100%
83
+ );
84
+ background-size: 200% 100%;
85
+ animation: charcuterie-shimmer var(--duration-loop-slow)
86
+ var(--easing-standard) infinite;
87
+ }
88
+
89
+ .charcuterie-sweep {
90
+ animation: charcuterie-sweep var(--duration-loop-slow)
91
+ var(--easing-standard) infinite;
92
+ }
93
+
94
+ .charcuterie-pulse {
95
+ animation: charcuterie-pulse var(--duration-loop-slow)
96
+ var(--easing-standard) infinite;
97
+ }
98
+
99
+ @media (prefers-reduced-motion: reduce) {
100
+ .charcuterie-spin,
101
+ .charcuterie-shimmer,
102
+ .charcuterie-sweep,
103
+ .charcuterie-pulse {
104
+ animation: none;
105
+ }
106
+
107
+ /* Flat, not mid-gradient: a stopped shimmer left holding its
108
+ * first keyframe is a lopsided highlight that reads as a
109
+ * rendering bug rather than as a placeholder. */
110
+ .charcuterie-shimmer {
111
+ background-image: none;
112
+ background-color: var(--color-surface-sunken);
113
+ }
114
+
115
+ /* The sweep's static fallback carries the whole meaning of the
116
+ * indeterminate state — "working, no measurable progress" — so it
117
+ * fills the track with hatching rather than parking a bar at one
118
+ * end, where it would read as either finished or stuck. */
119
+ .charcuterie-sweep {
120
+ inset-inline-start: 0;
121
+ inline-size: 100%;
122
+ background-image: repeating-linear-gradient(
123
+ 135deg,
124
+ currentColor 0,
125
+ currentColor 2px,
126
+ transparent 2px,
127
+ transparent 6px
128
+ );
129
+ }
130
+ }