@adres/mgda-ui 0.0.0-stage → 0.0.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 (325) hide show
  1. package/brand-knowledge.json +451 -0
  2. package/knowledge/accordion.md +135 -0
  3. package/knowledge/activity-rings.md +123 -0
  4. package/knowledge/alert.md +184 -0
  5. package/knowledge/answer-actions.md +217 -0
  6. package/knowledge/aspect-ratio.md +82 -0
  7. package/knowledge/avatar.md +158 -0
  8. package/knowledge/badge.md +172 -0
  9. package/knowledge/bar-graph.md +167 -0
  10. package/knowledge/bento-grid.md +115 -0
  11. package/knowledge/blockquote.md +153 -0
  12. package/knowledge/box-list.md +177 -0
  13. package/knowledge/bullet-list.md +128 -0
  14. package/knowledge/button-group.md +179 -0
  15. package/knowledge/button.md +231 -0
  16. package/knowledge/calendar.md +168 -0
  17. package/knowledge/card.md +321 -0
  18. package/knowledge/cards-nav.md +170 -0
  19. package/knowledge/chain-of-thought.md +164 -0
  20. package/knowledge/chat-container.md +135 -0
  21. package/knowledge/chat-message.md +224 -0
  22. package/knowledge/checkbox-card.md +172 -0
  23. package/knowledge/checkbox.md +111 -0
  24. package/knowledge/citations.md +241 -0
  25. package/knowledge/combobox.md +265 -0
  26. package/knowledge/countdown-pill.md +153 -0
  27. package/knowledge/cta.md +118 -0
  28. package/knowledge/data-list.md +173 -0
  29. package/knowledge/data-table.md +220 -0
  30. package/knowledge/dialog.md +249 -0
  31. package/knowledge/drawer.md +236 -0
  32. package/knowledge/dropdown-menu.md +238 -0
  33. package/knowledge/dropzone.md +153 -0
  34. package/knowledge/email-chips-input.md +161 -0
  35. package/knowledge/empty-state.md +106 -0
  36. package/knowledge/entity-header.md +166 -0
  37. package/knowledge/feature-grid.md +120 -0
  38. package/knowledge/feedback-bar.md +127 -0
  39. package/knowledge/footer.md +116 -0
  40. package/knowledge/heading.md +189 -0
  41. package/knowledge/hero.md +361 -0
  42. package/knowledge/input-button.md +187 -0
  43. package/knowledge/input-group.md +185 -0
  44. package/knowledge/input.md +256 -0
  45. package/knowledge/inset.md +177 -0
  46. package/knowledge/kpi-card.md +206 -0
  47. package/knowledge/list-nav.md +181 -0
  48. package/knowledge/logo-cloud.md +102 -0
  49. package/knowledge/media-gallery.md +158 -0
  50. package/knowledge/message-actions.md +175 -0
  51. package/knowledge/nav-sidebar.md +225 -0
  52. package/knowledge/number-stepper.md +159 -0
  53. package/knowledge/page-header.md +109 -0
  54. package/knowledge/pagination.md +225 -0
  55. package/knowledge/popover.md +159 -0
  56. package/knowledge/pricing.md +132 -0
  57. package/knowledge/progress.md +156 -0
  58. package/knowledge/prompt-input.md +337 -0
  59. package/knowledge/prompt-suggestions.md +168 -0
  60. package/knowledge/quota-bar.md +160 -0
  61. package/knowledge/quote.md +116 -0
  62. package/knowledge/radio-card.md +172 -0
  63. package/knowledge/radio-group.md +135 -0
  64. package/knowledge/reasoning.md +142 -0
  65. package/knowledge/search.md +131 -0
  66. package/knowledge/section-nav.md +162 -0
  67. package/knowledge/segmented-control.md +181 -0
  68. package/knowledge/select-button.md +205 -0
  69. package/knowledge/select.md +210 -0
  70. package/knowledge/separator.md +75 -0
  71. package/knowledge/shimmer-text.md +140 -0
  72. package/knowledge/sidebar.md +227 -0
  73. package/knowledge/skeleton.md +142 -0
  74. package/knowledge/slider.md +133 -0
  75. package/knowledge/slideshow.md +159 -0
  76. package/knowledge/source.md +156 -0
  77. package/knowledge/spinner.md +120 -0
  78. package/knowledge/stat-strip.md +128 -0
  79. package/knowledge/stats.md +117 -0
  80. package/knowledge/step-indicator.md +157 -0
  81. package/knowledge/switch.md +88 -0
  82. package/knowledge/table.md +190 -0
  83. package/knowledge/tabs.md +169 -0
  84. package/knowledge/testimonials.md +115 -0
  85. package/knowledge/text-link.md +192 -0
  86. package/knowledge/text.md +194 -0
  87. package/knowledge/thinking-bar.md +115 -0
  88. package/knowledge/thinking-steps.md +140 -0
  89. package/knowledge/toaster.md +127 -0
  90. package/knowledge/toggle.md +113 -0
  91. package/knowledge/tool-call-card.md +241 -0
  92. package/knowledge/tooltip.md +120 -0
  93. package/knowledge/top-nav.md +138 -0
  94. package/knowledge.json +19854 -0
  95. package/package.json +94 -4
  96. package/src/components/accordion.stories.tsx +46 -0
  97. package/src/components/accordion.tsx +74 -0
  98. package/src/components/activity-rings.stories.tsx +73 -0
  99. package/src/components/activity-rings.tsx +121 -0
  100. package/src/components/alert.stories.tsx +43 -0
  101. package/src/components/alert.tsx +132 -0
  102. package/src/components/answer-actions.stories.tsx +54 -0
  103. package/src/components/answer-actions.test.tsx +37 -0
  104. package/src/components/answer-actions.tsx +313 -0
  105. package/src/components/aspect-ratio.stories.tsx +32 -0
  106. package/src/components/aspect-ratio.tsx +5 -0
  107. package/src/components/avatar.stories.tsx +40 -0
  108. package/src/components/avatar.tsx +154 -0
  109. package/src/components/badge.stories.tsx +27 -0
  110. package/src/components/badge.tsx +63 -0
  111. package/src/components/bar-graph.stories.tsx +28 -0
  112. package/src/components/bar-graph.tsx +204 -0
  113. package/src/components/bento-grid.stories.tsx +23 -0
  114. package/src/components/bento-grid.tsx +67 -0
  115. package/src/components/blockquote.stories.tsx +28 -0
  116. package/src/components/blockquote.tsx +88 -0
  117. package/src/components/box-list.stories.tsx +25 -0
  118. package/src/components/box-list.tsx +108 -0
  119. package/src/components/bullet-list.stories.tsx +22 -0
  120. package/src/components/bullet-list.tsx +101 -0
  121. package/src/components/button-group.stories.tsx +31 -0
  122. package/src/components/button-group.tsx +156 -0
  123. package/src/components/button.stories.tsx +66 -0
  124. package/src/components/button.test.ts +17 -0
  125. package/src/components/button.tsx +139 -0
  126. package/src/components/calendar.stories.tsx +25 -0
  127. package/src/components/calendar.tsx +367 -0
  128. package/src/components/card.stories.tsx +44 -0
  129. package/src/components/card.tsx +392 -0
  130. package/src/components/cards-nav.stories.tsx +35 -0
  131. package/src/components/cards-nav.tsx +115 -0
  132. package/src/components/chain-of-thought.stories.tsx +67 -0
  133. package/src/components/chain-of-thought.test.tsx +50 -0
  134. package/src/components/chain-of-thought.tsx +170 -0
  135. package/src/components/chat-container.stories.tsx +47 -0
  136. package/src/components/chat-container.test.tsx +49 -0
  137. package/src/components/chat-container.tsx +222 -0
  138. package/src/components/chat-message.stories.tsx +52 -0
  139. package/src/components/chat-message.test.tsx +36 -0
  140. package/src/components/chat-message.tsx +163 -0
  141. package/src/components/checkbox-card.stories.tsx +27 -0
  142. package/src/components/checkbox-card.tsx +200 -0
  143. package/src/components/checkbox.stories.tsx +26 -0
  144. package/src/components/checkbox.tsx +61 -0
  145. package/src/components/citations.stories.tsx +64 -0
  146. package/src/components/citations.test.tsx +61 -0
  147. package/src/components/citations.tsx +220 -0
  148. package/src/components/combobox.stories.tsx +184 -0
  149. package/src/components/combobox.test.tsx +82 -0
  150. package/src/components/combobox.tsx +407 -0
  151. package/src/components/countdown-pill.stories.tsx +24 -0
  152. package/src/components/countdown-pill.tsx +129 -0
  153. package/src/components/cta.stories.tsx +29 -0
  154. package/src/components/cta.tsx +52 -0
  155. package/src/components/data-list.stories.tsx +50 -0
  156. package/src/components/data-list.test.tsx +35 -0
  157. package/src/components/data-list.tsx +181 -0
  158. package/src/components/data-table.stories.tsx +29 -0
  159. package/src/components/data-table.tsx +200 -0
  160. package/src/components/dialog.stories.tsx +42 -0
  161. package/src/components/dialog.tsx +200 -0
  162. package/src/components/drawer.stories.tsx +48 -0
  163. package/src/components/drawer.tsx +385 -0
  164. package/src/components/dropdown-menu.stories.tsx +41 -0
  165. package/src/components/dropdown-menu.tsx +252 -0
  166. package/src/components/dropzone.stories.tsx +19 -0
  167. package/src/components/dropzone.tsx +250 -0
  168. package/src/components/email-chips-input.stories.tsx +135 -0
  169. package/src/components/email-chips-input.tsx +210 -0
  170. package/src/components/empty-state.stories.tsx +22 -0
  171. package/src/components/empty-state.tsx +35 -0
  172. package/src/components/entity-header.stories.tsx +43 -0
  173. package/src/components/entity-header.test.tsx +35 -0
  174. package/src/components/entity-header.tsx +92 -0
  175. package/src/components/feature-grid.stories.tsx +25 -0
  176. package/src/components/feature-grid.tsx +87 -0
  177. package/src/components/feedback-bar.stories.tsx +25 -0
  178. package/src/components/feedback-bar.test.tsx +24 -0
  179. package/src/components/feedback-bar.tsx +62 -0
  180. package/src/components/footer.stories.tsx +23 -0
  181. package/src/components/footer.tsx +78 -0
  182. package/src/components/heading.stories.tsx +45 -0
  183. package/src/components/heading.test.tsx +22 -0
  184. package/src/components/heading.tsx +77 -0
  185. package/src/components/hero.stories.tsx +38 -0
  186. package/src/components/hero.tsx +498 -0
  187. package/src/components/input-button.stories.tsx +19 -0
  188. package/src/components/input-button.tsx +112 -0
  189. package/src/components/input-group.stories.tsx +38 -0
  190. package/src/components/input-group.tsx +157 -0
  191. package/src/components/input.stories.tsx +31 -0
  192. package/src/components/input.tsx +284 -0
  193. package/src/components/inset.stories.tsx +29 -0
  194. package/src/components/inset.tsx +70 -0
  195. package/src/components/kpi-card.stories.tsx +29 -0
  196. package/src/components/kpi-card.tsx +284 -0
  197. package/src/components/list-nav.stories.tsx +22 -0
  198. package/src/components/list-nav.tsx +145 -0
  199. package/src/components/logo-cloud.stories.tsx +23 -0
  200. package/src/components/logo-cloud.tsx +42 -0
  201. package/src/components/media-gallery.stories.tsx +25 -0
  202. package/src/components/media-gallery.tsx +347 -0
  203. package/src/components/message-actions.stories.tsx +45 -0
  204. package/src/components/message-actions.test.tsx +32 -0
  205. package/src/components/message-actions.tsx +119 -0
  206. package/src/components/nav-sidebar.stories.tsx +58 -0
  207. package/src/components/nav-sidebar.test.tsx +54 -0
  208. package/src/components/nav-sidebar.tsx +462 -0
  209. package/src/components/number-stepper.stories.tsx +19 -0
  210. package/src/components/number-stepper.tsx +158 -0
  211. package/src/components/page-header.stories.tsx +25 -0
  212. package/src/components/page-header.tsx +64 -0
  213. package/src/components/pagination.stories.tsx +110 -0
  214. package/src/components/pagination.test.tsx +46 -0
  215. package/src/components/pagination.tsx +144 -0
  216. package/src/components/popover.stories.tsx +26 -0
  217. package/src/components/popover.tsx +62 -0
  218. package/src/components/pricing.stories.tsx +42 -0
  219. package/src/components/pricing.tsx +130 -0
  220. package/src/components/progress.stories.tsx +22 -0
  221. package/src/components/progress.tsx +189 -0
  222. package/src/components/prompt-input.stories.tsx +62 -0
  223. package/src/components/prompt-input.test.tsx +106 -0
  224. package/src/components/prompt-input.tsx +343 -0
  225. package/src/components/prompt-suggestions.stories.tsx +53 -0
  226. package/src/components/prompt-suggestions.test.tsx +37 -0
  227. package/src/components/prompt-suggestions.tsx +122 -0
  228. package/src/components/quota-bar.stories.tsx +19 -0
  229. package/src/components/quota-bar.tsx +191 -0
  230. package/src/components/quote.stories.tsx +18 -0
  231. package/src/components/quote.tsx +43 -0
  232. package/src/components/radio-card.stories.tsx +27 -0
  233. package/src/components/radio-card.tsx +225 -0
  234. package/src/components/radio-group.stories.tsx +32 -0
  235. package/src/components/radio-group.tsx +68 -0
  236. package/src/components/reasoning.stories.tsx +50 -0
  237. package/src/components/reasoning.test.tsx +48 -0
  238. package/src/components/reasoning.tsx +143 -0
  239. package/src/components/search.stories.tsx +18 -0
  240. package/src/components/search.tsx +133 -0
  241. package/src/components/section-nav.stories.tsx +59 -0
  242. package/src/components/section-nav.test.tsx +36 -0
  243. package/src/components/section-nav.tsx +146 -0
  244. package/src/components/segmented-control.stories.tsx +22 -0
  245. package/src/components/segmented-control.tsx +301 -0
  246. package/src/components/select-button.stories.tsx +27 -0
  247. package/src/components/select-button.tsx +174 -0
  248. package/src/components/select.stories.tsx +27 -0
  249. package/src/components/select.tsx +198 -0
  250. package/src/components/separator.stories.tsx +22 -0
  251. package/src/components/separator.tsx +23 -0
  252. package/src/components/shimmer-text.stories.tsx +23 -0
  253. package/src/components/shimmer-text.test.tsx +35 -0
  254. package/src/components/shimmer-text.tsx +43 -0
  255. package/src/components/sidebar.stories.tsx +25 -0
  256. package/src/components/sidebar.tsx +234 -0
  257. package/src/components/skeleton.stories.tsx +22 -0
  258. package/src/components/skeleton.tsx +96 -0
  259. package/src/components/slider.stories.tsx +20 -0
  260. package/src/components/slider.tsx +176 -0
  261. package/src/components/slideshow.stories.tsx +30 -0
  262. package/src/components/slideshow.tsx +291 -0
  263. package/src/components/source.stories.tsx +32 -0
  264. package/src/components/source.test.tsx +40 -0
  265. package/src/components/source.tsx +161 -0
  266. package/src/components/spinner.stories.tsx +19 -0
  267. package/src/components/spinner.tsx +76 -0
  268. package/src/components/stat-strip.stories.tsx +36 -0
  269. package/src/components/stat-strip.test.tsx +25 -0
  270. package/src/components/stat-strip.tsx +85 -0
  271. package/src/components/stats.stories.tsx +24 -0
  272. package/src/components/stats.tsx +83 -0
  273. package/src/components/step-indicator.stories.tsx +18 -0
  274. package/src/components/step-indicator.test.tsx +18 -0
  275. package/src/components/step-indicator.tsx +286 -0
  276. package/src/components/switch.stories.tsx +26 -0
  277. package/src/components/switch.tsx +32 -0
  278. package/src/components/table.stories.tsx +38 -0
  279. package/src/components/table.tsx +220 -0
  280. package/src/components/tabs.stories.tsx +25 -0
  281. package/src/components/tabs.tsx +276 -0
  282. package/src/components/testimonials.stories.tsx +22 -0
  283. package/src/components/testimonials.tsx +109 -0
  284. package/src/components/text-link.stories.tsx +47 -0
  285. package/src/components/text-link.test.tsx +18 -0
  286. package/src/components/text-link.tsx +88 -0
  287. package/src/components/text.stories.tsx +49 -0
  288. package/src/components/text.tsx +105 -0
  289. package/src/components/thinking-bar.stories.tsx +19 -0
  290. package/src/components/thinking-bar.test.tsx +27 -0
  291. package/src/components/thinking-bar.tsx +64 -0
  292. package/src/components/thinking-steps.stories.tsx +44 -0
  293. package/src/components/thinking-steps.test.tsx +42 -0
  294. package/src/components/thinking-steps.tsx +92 -0
  295. package/src/components/toaster.stories.tsx +80 -0
  296. package/src/components/toaster.tsx +36 -0
  297. package/src/components/toggle.stories.tsx +21 -0
  298. package/src/components/toggle.tsx +103 -0
  299. package/src/components/tool-call-card.stories.tsx +97 -0
  300. package/src/components/tool-call-card.test.tsx +88 -0
  301. package/src/components/tool-call-card.tsx +231 -0
  302. package/src/components/tooltip.stories.tsx +25 -0
  303. package/src/components/tooltip.tsx +43 -0
  304. package/src/components/top-nav.stories.tsx +20 -0
  305. package/src/components/top-nav.tsx +76 -0
  306. package/src/docs/Colors.stories.tsx +192 -0
  307. package/src/docs/GettingStarted.mdx +72 -0
  308. package/src/docs/Mcp.mdx +52 -0
  309. package/src/docs/Typography.stories.tsx +114 -0
  310. package/src/hooks/use-in-view.ts +32 -0
  311. package/src/hooks/use-reduced-motion.ts +26 -0
  312. package/src/index.ts +343 -0
  313. package/src/lib/cn.test.ts +67 -0
  314. package/src/lib/cn.ts +81 -0
  315. package/src/lib/motion.ts +109 -0
  316. package/src/lib/prefix-utilities.test.ts +56 -0
  317. package/src/lib/prefix-utilities.ts +50 -0
  318. package/src/styles/base.css +489 -0
  319. package/src/styles/motion.css +69 -0
  320. package/src/styles/overrides.css +44 -0
  321. package/src/styles/typography.css +173 -0
  322. package/src/theme/theme.css +391 -0
  323. package/src/theme/tokens.ts +29 -0
  324. package/src/theme/types.ts +26 -0
  325. package/README.md +0 -3
@@ -0,0 +1,31 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { ButtonGroup, ButtonGroupItem } from "./button-group"
4
+
5
+ const meta: Meta<typeof ButtonGroup> = {
6
+ title: "Components/ButtonGroup",
7
+ component: ButtonGroup,
8
+ tags: ["autodocs"],
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof ButtonGroup>
13
+
14
+ export const Default: Story = {
15
+ render: () => (
16
+ <ButtonGroup>
17
+ <ButtonGroupItem active>List</ButtonGroupItem>
18
+ <ButtonGroupItem>Grid</ButtonGroupItem>
19
+ <ButtonGroupItem>Board</ButtonGroupItem>
20
+ </ButtonGroup>
21
+ ),
22
+ }
23
+
24
+ export const Primary: Story = {
25
+ render: () => (
26
+ <ButtonGroup variant="primary">
27
+ <ButtonGroupItem>Save</ButtonGroupItem>
28
+ <ButtonGroupItem compact>▾</ButtonGroupItem>
29
+ </ButtonGroup>
30
+ ),
31
+ }
@@ -0,0 +1,156 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cva } from "class-variance-authority"
5
+ import { cn } from "../lib/cn"
6
+
7
+ // ─── Types ────────────────────────────────────────────────────────────────────
8
+
9
+ export type ButtonGroupSize = "sm" | "md" | "lg"
10
+ export type ButtonGroupVariant = "outline" | "primary"
11
+ export type ButtonGroupSelected = "primary" | "muted"
12
+
13
+ // ─── Context ──────────────────────────────────────────────────────────────────
14
+
15
+ interface ButtonGroupContextValue {
16
+ size: ButtonGroupSize
17
+ variant: ButtonGroupVariant
18
+ selected: ButtonGroupSelected
19
+ }
20
+
21
+ const ButtonGroupContext = React.createContext<ButtonGroupContextValue>({
22
+ size: "md",
23
+ variant: "outline",
24
+ selected: "primary",
25
+ })
26
+
27
+ // ─── Root ─────────────────────────────────────────────────────────────────────
28
+
29
+ const rootVariants = cva(
30
+ "ax:inline-flex ax:items-stretch ax:overflow-hidden ax:rounded-[var(--radius)]",
31
+ {
32
+ variants: {
33
+ variant: {
34
+ outline: "ax:border ax:border-border",
35
+ primary: "",
36
+ },
37
+ },
38
+ defaultVariants: { variant: "outline" },
39
+ }
40
+ )
41
+
42
+ export interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
43
+ /** Item height + type metrics - mirrors Button at the same size. */
44
+ size?: ButtonGroupSize
45
+ /** Visual mode. `outline` = bordered container with transparent items;
46
+ * `primary` = solid primary surface across all items (split-button look). */
47
+ variant?: ButtonGroupVariant
48
+ /** How an `active` item stands out (only relevant when variant="outline").
49
+ * `primary` - strong, brand-coloured active cell (view toggle).
50
+ * `muted` - quiet, neutral-grey active cell (filter group). */
51
+ selected?: ButtonGroupSelected
52
+ }
53
+
54
+ /**
55
+ * ButtonGroup - a row of connected buttons sharing one container.
56
+ *
57
+ * Use for: view toggles, filter groups, toolbars, split-action buttons.
58
+ * NOT for: navigating between content panels (use Tabs) or single-selection
59
+ * view-mode switches with a sliding indicator (use SegmentedControl).
60
+ */
61
+ export const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(
62
+ ({ className, size = "md", variant = "outline", selected = "primary", children, ...props }, ref) => (
63
+ <ButtonGroupContext.Provider value={{ size, variant, selected }}>
64
+ <div
65
+ ref={ref}
66
+ role="group"
67
+ className={cn(rootVariants({ variant }), className)}
68
+ {...props}
69
+ >
70
+ {children}
71
+ </div>
72
+ </ButtonGroupContext.Provider>
73
+ )
74
+ )
75
+ ButtonGroup.displayName = "ButtonGroup"
76
+
77
+ // ─── Item ─────────────────────────────────────────────────────────────────────
78
+
79
+ // Height + type metrics mirror Button so a ButtonGroup reads as a strip of
80
+ // connected Buttons. Padding is applied separately per size + compact flag
81
+ // to avoid Tailwind class collisions.
82
+ const itemBase = cva(
83
+ [
84
+ "ax:inline-flex ax:items-center ax:justify-center ax:gap-xs ax:whitespace-nowrap ax:select-none ax:cursor-pointer",
85
+ "ax:transition-colors ax:transition-snappy",
86
+ "ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-inset",
87
+ "ax:disabled:pointer-events-none ax:disabled:opacity-50",
88
+ "ax:[&_svg]:pointer-events-none ax:[&_svg]:shrink-0",
89
+ ],
90
+ {
91
+ variants: {
92
+ size: {
93
+ sm: "ax:h-2xl ax:t-label-xs ax:[&_svg]:size-3",
94
+ md: "ax:h-3xl ax:t-label-sm ax:[&_svg]:size-3.5",
95
+ lg: "ax:h-4xl ax:text-base ax:[&_svg]:size-4",
96
+ },
97
+ },
98
+ }
99
+ )
100
+
101
+ const PADDING: Record<ButtonGroupSize, { normal: string; compact: string }> = {
102
+ sm: { normal: "ax:px-sm", compact: "ax:px-2" },
103
+ md: { normal: "ax:px-md", compact: "ax:px-2.5" },
104
+ lg: { normal: "ax:px-xl", compact: "ax:px-3" },
105
+ }
106
+
107
+ // Cells use emphasis aliases, so the selected/primary fills auto-invert to deep
108
+ // tints in dark (no dark: override). One exception: the idle cell's hover:
109
+ // `neutral-weakest` inverts to ~the dark background, giving no visible lift, so a
110
+ // `dark:hover:bg-neutral-weaker` provides the lift. `outlineSelectedMuted` uses
111
+ // mode-aware `bg-selected`/`text-foreground`.
112
+ const STATE = {
113
+ outlineIdle: "ax:bg-muted ax:text-foreground ax:border-r ax:border-border ax:last:border-r-0 ax:hover:bg-muted/50",
114
+ outlineSelectedPrimary: "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker ax:font-medium ax:border-r ax:border-border ax:last:border-r-0 ax:hover:bg-primary-weak/80 ax:dark:hover:bg-primary-strong/80",
115
+ outlineSelectedMuted: "ax:bg-selected ax:text-foreground ax:font-medium ax:border-r ax:border-border ax:last:border-r-0",
116
+ primary: "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker ax:font-medium ax:hover:bg-primary-weak/80 ax:border-r ax:border-background ax:dark:hover:bg-primary-strong/80 ax:last:border-r-0",
117
+ } as const
118
+
119
+ export interface ButtonGroupItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
120
+ /** Marks this item as the currently-selected cell (sets aria-pressed). */
121
+ active?: boolean
122
+ /** Reduces horizontal padding - use for chevron/icon-only trailing buttons
123
+ * (the dropdown side of a split button). */
124
+ compact?: boolean
125
+ }
126
+
127
+ export const ButtonGroupItem = React.forwardRef<HTMLButtonElement, ButtonGroupItemProps>(
128
+ ({ className, active, compact, type = "button", children, ...props }, ref) => {
129
+ const { size, variant, selected } = React.useContext(ButtonGroupContext)
130
+ const stateClass =
131
+ variant === "primary"
132
+ ? STATE.primary
133
+ : active
134
+ ? selected === "muted"
135
+ ? STATE.outlineSelectedMuted
136
+ : STATE.outlineSelectedPrimary
137
+ : STATE.outlineIdle
138
+ return (
139
+ <button
140
+ ref={ref}
141
+ type={type}
142
+ aria-pressed={active ?? undefined}
143
+ className={cn(
144
+ itemBase({ size }),
145
+ PADDING[size][compact ? "compact" : "normal"],
146
+ stateClass,
147
+ className,
148
+ )}
149
+ {...props}
150
+ >
151
+ {children}
152
+ </button>
153
+ )
154
+ }
155
+ )
156
+ ButtonGroupItem.displayName = "ButtonGroupItem"
@@ -0,0 +1,66 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { Settings } from "lucide-react"
4
+ import { Button } from "./button"
5
+
6
+ const meta: Meta<typeof Button> = {
7
+ title: "Components/Button",
8
+ component: Button,
9
+ tags: ["autodocs"],
10
+ argTypes: {
11
+ variant: {
12
+ control: "select",
13
+ options: ["primary", "secondary", "outline", "ghost", "destructive", "accent"],
14
+ },
15
+ size: {
16
+ control: "select",
17
+ options: ["sm", "md", "lg", "icon-sm", "icon", "icon-lg"],
18
+ },
19
+ elevated: { control: "boolean" },
20
+ aurora: { control: "boolean" },
21
+ disabled: { control: "boolean" },
22
+ },
23
+ args: {
24
+ children: "Button",
25
+ variant: "primary",
26
+ size: "md",
27
+ elevated: false,
28
+ aurora: false,
29
+ disabled: false,
30
+ },
31
+ }
32
+
33
+ export default meta
34
+ type Story = StoryObj<typeof Button>
35
+
36
+ export const Primary: Story = {}
37
+
38
+ export const Secondary: Story = {
39
+ args: { variant: "secondary" },
40
+ }
41
+
42
+ export const Outline: Story = {
43
+ args: { variant: "outline" },
44
+ }
45
+
46
+ export const Destructive: Story = {
47
+ args: { variant: "destructive" },
48
+ }
49
+
50
+ export const Elevated: Story = {
51
+ args: { elevated: true },
52
+ }
53
+
54
+ export const Large: Story = {
55
+ args: { size: "lg", children: "Get started" },
56
+ }
57
+
58
+ export const IconSizes: Story = {
59
+ render: () => (
60
+ <div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
61
+ <Button variant="outline" size="icon-sm" aria-label="Settings"><Settings /></Button>
62
+ <Button variant="outline" size="icon" aria-label="Settings"><Settings /></Button>
63
+ <Button variant="outline" size="icon-lg" aria-label="Settings"><Settings /></Button>
64
+ </div>
65
+ ),
66
+ }
@@ -0,0 +1,17 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { buttonVariants } from "./button"
3
+
4
+ describe("buttonVariants", () => {
5
+ it("includes the ax prefix on the base classes", () => {
6
+ expect(buttonVariants()).toContain("ax:inline-flex")
7
+ })
8
+
9
+ it("matches each icon size to its text-button height", () => {
10
+ expect(buttonVariants({ size: "sm" })).toContain("ax:h-2xl")
11
+ expect(buttonVariants({ size: "icon-sm" })).toContain("ax:size-2xl")
12
+ expect(buttonVariants({ size: "md" })).toContain("ax:h-3xl")
13
+ expect(buttonVariants({ size: "icon" })).toContain("ax:size-3xl")
14
+ expect(buttonVariants({ size: "lg" })).toContain("ax:h-4xl")
15
+ expect(buttonVariants({ size: "icon-lg" })).toContain("ax:size-4xl")
16
+ })
17
+ })
@@ -0,0 +1,139 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Slot } from "@radix-ui/react-slot"
5
+ import { tv, type VariantProps } from "tailwind-variants"
6
+ import { cn } from "../lib/cn"
7
+
8
+ /**
9
+ * Button - the most leveraged primitive in the system.
10
+ *
11
+ * Variants reflect role, not colour: every brand can re-skin them by changing
12
+ * `--primary`, `--secondary`, `--accent`, etc.; the component never knows
13
+ * which brand it's wearing.
14
+ *
15
+ * Sizes follow the type+touch ladder:
16
+ * sm - dense UI rows (32px tall)
17
+ * md - DEFAULT, comfortable body buttons (40px)
18
+ * lg - primary marketing CTAs (48px)
19
+ * icon-sm / icon / icon-lg - square 32 / 40 / 48px icon-only actions, matching
20
+ * sm / md / lg heights (a11y label still required)
21
+ */
22
+ export const buttonVariants = tv({
23
+ base: [
24
+ "ax:inline-flex ax:items-center ax:justify-center ax:gap-xs ax:whitespace-nowrap ax:select-none ax:cursor-pointer",
25
+ // Font size is set per-size below (not via `t-label-sm`) so an app that
26
+ // re-scales the `t-label-*` utilities can't shrink the button text.
27
+ "ax:font-medium ax:leading-none",
28
+ "ax:transition-[transform,background-color,border-color,color,box-shadow] ax:transition-snappy",
29
+ "ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-offset-2 ax:focus-visible:ring-offset-background",
30
+ "ax:disabled:pointer-events-none ax:disabled:opacity-50",
31
+ "ax:active:scale-[0.98]",
32
+ "ax:[&_svg]:pointer-events-none ax:[&_svg]:size-4 ax:[&_svg]:shrink-0",
33
+ ],
34
+ variants: {
35
+ variant: {
36
+ primary: [
37
+ // `weak` fill + `weaker` hover: emphasis aliases auto-invert to deep
38
+ // tints in dark, so `-foreground` stays legible in both modes (no dark:).
39
+ "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-weakest ax:dark:text-primary-strong",
40
+ "ax:hover:bg-primary-weak/80 ax:dark:hover:bg-primary-weak/80",
41
+ ],
42
+ secondary: [
43
+ "ax:bg-secondary-weaker ax:text-secondary-foreground ax:dark:bg-secondary-strong ax:dark:text-secondary-muted",
44
+ "ax:hover:bg-secondary-weaker/80 ax:dark:hover:bg-secondary-strong/80",
45
+ "ax:shadow-xs",
46
+ ],
47
+ outline: [
48
+ "ax:bg-transparent ax:text-foreground ax:border ax:border-border",
49
+ "ax:hover:bg-muted",
50
+ ],
51
+ ghost: [
52
+ "ax:bg-transparent ax:text-foreground",
53
+ "ax:hover:bg-muted",
54
+ ],
55
+ destructive: [
56
+ "ax:bg-destructive-weaker ax:text-destructive-foreground",
57
+ "ax:hover:bg-destructive-weaker/80",
58
+ ],
59
+ accent: [
60
+ "ax:bg-accent-weak ax:text-accent-foreground",
61
+ "ax:hover:bg-accent-weak/80",
62
+ "ax:shadow-sm",
63
+ ],
64
+ },
65
+ size: {
66
+ sm: "ax:h-2xl ax:px-sm ax:rounded-(--button-radius) ax:text-sm",
67
+ md: "ax:h-3xl ax:px-md ax:rounded-(--button-radius) ax:text-sm",
68
+ lg: "ax:h-4xl ax:px-xl ax:rounded-(--button-radius) ax:text-base",
69
+ // Square icon-only buttons, one per height above: 32 / 40 / 48px.
70
+ "icon-sm": "ax:size-2xl ax:rounded-(--button-radius)",
71
+ icon: "ax:size-3xl ax:rounded-(--button-radius)",
72
+ "icon-lg": "ax:size-4xl ax:rounded-(--button-radius) ax:[&_svg]:size-5",
73
+ },
74
+ // Opt-in tinted drop shadow. Only variants with a defined tinted shadow
75
+ // (primary, destructive) react, wired in compoundVariants below.
76
+ elevated: {
77
+ true: "",
78
+ false: "",
79
+ },
80
+ },
81
+ compoundVariants: [
82
+ // Tinted button shadow is a light-mode flourish, cleared in dark, where the
83
+ // low-contrast page makes a coloured lift read as muddy.
84
+ { variant: "primary", elevated: true, class: "ax:shadow-primary-btn ax:dark:shadow-none" },
85
+ { variant: "destructive", elevated: true, class: "ax:shadow-destructive-btn ax:dark:shadow-none" },
86
+ ],
87
+ defaultVariants: {
88
+ variant: "primary",
89
+ size: "md",
90
+ elevated: false,
91
+ },
92
+ }, { twMerge: false })
93
+
94
+ export interface ButtonProps
95
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
96
+ VariantProps<typeof buttonVariants> {
97
+ /** When true, renders the child element instead of a button (e.g. for `<Link>`). */
98
+ asChild?: boolean
99
+ /** Decorative "aurora" - two soft, blurred orbs (primary + accent) drifting
100
+ * behind the label for a modern, glowing look. Designed for the `primary`
101
+ * variant. Ignored with `asChild` (it needs to inject its own children). */
102
+ aurora?: boolean
103
+ }
104
+
105
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
106
+ ({ className, variant, size, elevated, aurora = false, asChild = false, children, ...props }, ref) => {
107
+ const Comp = asChild ? Slot : "button"
108
+ // Aurora injects decorative children, so it can't pass through Slot.
109
+ const showAurora = aurora && !asChild
110
+ return (
111
+ <Comp
112
+ ref={ref}
113
+ data-slot="button"
114
+ className={cn(
115
+ buttonVariants({ variant, size, elevated }),
116
+ // `isolate` scopes the orbs' negative z-index to the button; `overflow-hidden`
117
+ // clips them to the (rounded) button shape.
118
+ showAurora && "ax:relative ax:isolate ax:overflow-hidden",
119
+ className,
120
+ )}
121
+ {...props}
122
+ >
123
+ {showAurora ? (
124
+ <>
125
+ {/* Blurred orbs sit behind the label (-z-10): a primary glow top-start,
126
+ an accent glow bottom-end. */}
127
+ <span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-top-12 ax:-left-2 ax:-z-10 ax:size-14 ax:rounded-full ax:bg-primary/40 ax:dark:bg-primary-weaker ax:blur-xl ax:saturate-250" />
128
+ <span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-top-4 ax:-z-10 ax:size-16 ax:rounded-full ax:bg-primary-weaker ax:dark:bg-primary/20 ax:blur-xl ax:saturate-250" />
129
+ <span aria-hidden="true" className="ax:pointer-events-none ax:absolute ax:-bottom-12 ax:-right-6 ax:-z-10 ax:size-16 ax:rounded-full ax:bg-primary-weaker ax:blur-xl ax:saturate-250" />
130
+ {children}
131
+ </>
132
+ ) : (
133
+ children
134
+ )}
135
+ </Comp>
136
+ )
137
+ }
138
+ )
139
+ Button.displayName = "Button"
@@ -0,0 +1,25 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { Calendar } from "./calendar"
4
+
5
+ const meta: Meta<typeof Calendar> = {
6
+ title: "Components/Calendar",
7
+ component: Calendar,
8
+ tags: ["autodocs"],
9
+ args: {
10
+ mode: "single",
11
+ defaultValue: new Date(2026, 7, 11),
12
+ },
13
+ }
14
+
15
+ export default meta
16
+ type Story = StoryObj<typeof Calendar>
17
+
18
+ export const Default: Story = {}
19
+
20
+ export const Range: Story = {
21
+ args: {
22
+ mode: "range",
23
+ defaultValue: { from: new Date(2026, 7, 10), to: new Date(2026, 7, 15) },
24
+ },
25
+ }