@adres/mgda-ui 0.0.0-stage → 0.0.2

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,64 @@
1
+ import * as React from "react"
2
+ import { cn } from "../lib/cn"
3
+
4
+ export interface PageHeaderProps
5
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
6
+ /** Page or section title. Rendered as an `<h1>` with `t-h1` typography. */
7
+ title: React.ReactNode
8
+ /** Optional subtitle / context line shown directly under the title. */
9
+ subtitle?: React.ReactNode
10
+ /**
11
+ * Right-aligned slot - typically `<Button>`s, a search input, a `Select`,
12
+ * or any other action(s) the page exposes at the top. Wraps to a new line
13
+ * on narrow viewports rather than truncating.
14
+ */
15
+ children?: React.ReactNode
16
+ }
17
+
18
+ /**
19
+ * PageHeader - top-of-content heading row.
20
+ *
21
+ * Pairs an h1 title (using `t-h1`) with an optional subtitle on the start
22
+ * side and a free-form actions slot on the end side. Designed to sit at the
23
+ * top of a page or major section inside an app shell.
24
+ *
25
+ * Layout:
26
+ * • Title + subtitle take the available inline space (`flex-1`).
27
+ * • Actions sit on the inline-end (`shrink-0`) and wrap onto a new line on
28
+ * narrow viewports.
29
+ * • Title and actions are baseline-aligned to the bottom of the row so a
30
+ * tall title (multi-line wrap) keeps actions anchored to its baseline.
31
+ *
32
+ * @example
33
+ * <PageHeader title="Students" subtitle="38 active learners">
34
+ * <Button variant="outline">Import CSV</Button>
35
+ * <Button>Invite students</Button>
36
+ * </PageHeader>
37
+ */
38
+ export const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
39
+ ({ className, title, subtitle, children, ...props }, ref) => {
40
+ return (
41
+ <div
42
+ ref={ref}
43
+ className={cn(
44
+ "ax:pb-lg ax:flex ax:flex-wrap ax:items-start ax:gap-md ax:justify-between",
45
+ className,
46
+ )}
47
+ {...props}
48
+ >
49
+ <div className="ax:flex ax:flex-col ax:gap-xs ax:flex-1 ax:min-w-0">
50
+ <h1 className="ax:t-h1">{title}</h1>
51
+ {subtitle && (
52
+ <p className="ax:t-body-sm ax:text-muted-foreground">{subtitle}</p>
53
+ )}
54
+ </div>
55
+ {children && (
56
+ <div className="ax:flex ax:items-center ax:gap-md ax:shrink-0 ax:flex-wrap">
57
+ {children}
58
+ </div>
59
+ )}
60
+ </div>
61
+ )
62
+ },
63
+ )
64
+ PageHeader.displayName = "PageHeader"
@@ -0,0 +1,110 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import {
4
+ Pagination, PaginationContent, PaginationItem, PaginationLink,
5
+ PaginationPrevious, PaginationNext, PaginationEllipsis,
6
+ } from "./pagination"
7
+
8
+ const meta: Meta<typeof Pagination> = {
9
+ title: "Components/Pagination",
10
+ component: Pagination,
11
+ tags: ["autodocs"],
12
+ }
13
+
14
+ export default meta
15
+ type Story = StoryObj<typeof Pagination>
16
+
17
+ export const Default: Story = {
18
+ render: () => {
19
+ const [page, setPage] = React.useState(1)
20
+ const pageCount = 5
21
+ return (
22
+ <Pagination>
23
+ <PaginationContent>
24
+ <PaginationItem>
25
+ <PaginationPrevious disabled={page === 1} onClick={() => setPage((p) => p - 1)} />
26
+ </PaginationItem>
27
+ {Array.from({ length: pageCount }, (_, i) => i + 1).map((n) => (
28
+ <PaginationItem key={n}>
29
+ <PaginationLink isActive={n === page} onClick={() => setPage(n)}>
30
+ {n}
31
+ </PaginationLink>
32
+ </PaginationItem>
33
+ ))}
34
+ <PaginationItem>
35
+ <PaginationNext disabled={page === pageCount} onClick={() => setPage((p) => p + 1)} />
36
+ </PaginationItem>
37
+ </PaginationContent>
38
+ </Pagination>
39
+ )
40
+ },
41
+ }
42
+
43
+ export const WithEllipsis: Story = {
44
+ render: () => {
45
+ const [page, setPage] = React.useState(6)
46
+ const pageCount = 20
47
+
48
+ const link = (n: number) => (
49
+ <PaginationItem key={n}>
50
+ <PaginationLink isActive={n === page} onClick={() => setPage(n)}>{n}</PaginationLink>
51
+ </PaginationItem>
52
+ )
53
+
54
+ return (
55
+ <Pagination>
56
+ <PaginationContent>
57
+ <PaginationItem>
58
+ <PaginationPrevious disabled={page === 1} onClick={() => setPage((p) => p - 1)} />
59
+ </PaginationItem>
60
+ {link(1)}
61
+ {page > 3 && (
62
+ <PaginationItem><PaginationEllipsis /></PaginationItem>
63
+ )}
64
+ {[page - 1, page, page + 1]
65
+ .filter((n) => n > 1 && n < pageCount)
66
+ .map(link)}
67
+ {page < pageCount - 2 && (
68
+ <PaginationItem><PaginationEllipsis /></PaginationItem>
69
+ )}
70
+ {link(pageCount)}
71
+ <PaginationItem>
72
+ <PaginationNext disabled={page === pageCount} onClick={() => setPage((p) => p + 1)} />
73
+ </PaginationItem>
74
+ </PaginationContent>
75
+ </Pagination>
76
+ )
77
+ },
78
+ }
79
+
80
+ export const Small: Story = {
81
+ render: () => (
82
+ <Pagination>
83
+ <PaginationContent>
84
+ <PaginationItem><PaginationPrevious size="sm" /></PaginationItem>
85
+ {[1, 2, 3].map((n) => (
86
+ <PaginationItem key={n}>
87
+ <PaginationLink size="sm" isActive={n === 1}>{n}</PaginationLink>
88
+ </PaginationItem>
89
+ ))}
90
+ <PaginationItem><PaginationNext size="sm" /></PaginationItem>
91
+ </PaginationContent>
92
+ </Pagination>
93
+ ),
94
+ }
95
+
96
+ export const DisabledEdges: Story = {
97
+ render: () => (
98
+ <Pagination>
99
+ <PaginationContent>
100
+ <PaginationItem><PaginationPrevious disabled /></PaginationItem>
101
+ {[1, 2, 3].map((n) => (
102
+ <PaginationItem key={n}>
103
+ <PaginationLink isActive={n === 1}>{n}</PaginationLink>
104
+ </PaginationItem>
105
+ ))}
106
+ <PaginationItem><PaginationNext /></PaginationItem>
107
+ </PaginationContent>
108
+ </Pagination>
109
+ ),
110
+ }
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import * as React from "react"
3
+ import { renderToStaticMarkup } from "react-dom/server"
4
+ import {
5
+ Pagination, PaginationContent, PaginationItem, PaginationLink,
6
+ PaginationPrevious, PaginationNext,
7
+ } from "./pagination"
8
+
9
+ describe("Pagination", () => {
10
+ it("renders a nav landmark labelled for pagination", () => {
11
+ const html = renderToStaticMarkup(
12
+ <Pagination>
13
+ <PaginationContent>
14
+ <PaginationItem><PaginationLink>1</PaginationLink></PaginationItem>
15
+ </PaginationContent>
16
+ </Pagination>,
17
+ )
18
+ expect(html).toContain('role="navigation"')
19
+ expect(html).toContain('aria-label="pagination"')
20
+ })
21
+
22
+ it("marks the active page with aria-current and the active fill", () => {
23
+ const html = renderToStaticMarkup(<PaginationLink isActive>3</PaginationLink>)
24
+ expect(html).toContain('aria-current="page"')
25
+ expect(html).toContain("ax:bg-primary-weaker")
26
+ })
27
+
28
+ it("leaves aria-current off inactive pages", () => {
29
+ const html = renderToStaticMarkup(<PaginationLink>3</PaginationLink>)
30
+ expect(html).not.toContain("aria-current")
31
+ })
32
+
33
+ it("defaults Previous/Next labels and lets them be overridden", () => {
34
+ const defaults = renderToStaticMarkup(<PaginationPrevious />)
35
+ expect(defaults).toContain("Previous")
36
+
37
+ const custom = renderToStaticMarkup(<PaginationNext aria-label="Older results">Older</PaginationNext>)
38
+ expect(custom).toContain("Older")
39
+ expect(custom).toContain('aria-label="Older results"')
40
+ })
41
+
42
+ it("passes disabled through to the native button", () => {
43
+ const html = renderToStaticMarkup(<PaginationPrevious disabled />)
44
+ expect(html).toContain("disabled")
45
+ })
46
+ })
@@ -0,0 +1,144 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Slot } from "@radix-ui/react-slot"
5
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
6
+ import { tv, type VariantProps } from "tailwind-variants"
7
+ import { cn } from "../lib/cn"
8
+
9
+ /**
10
+ * Pagination - a composable set of parts (Pagination, PaginationContent,
11
+ * PaginationItem, PaginationLink, PaginationPrevious, PaginationNext,
12
+ * PaginationEllipsis), following the shadcn/ui pattern: plain semantic
13
+ * elements (nav/ul/li/button) styled with design-system tokens, no Radix
14
+ * primitive underneath - page navigation doesn't need focus-trapping or
15
+ * portal behaviour, just a nav landmark and native buttons.
16
+ *
17
+ * Ships the primitives only, not a page-range calculator: compose the
18
+ * `PaginationItem`/`PaginationEllipsis` list yourself from your own page
19
+ * count, the same way shadcn's docs do.
20
+ */
21
+ export interface PaginationProps extends React.ComponentPropsWithoutRef<"nav"> {}
22
+
23
+ export const Pagination = React.forwardRef<HTMLElement, PaginationProps>(
24
+ ({ className, ...props }, ref) => (
25
+ <nav
26
+ ref={ref}
27
+ role="navigation"
28
+ aria-label="pagination"
29
+ className={cn("ax:mx-auto ax:flex ax:w-full ax:justify-center", className)}
30
+ {...props}
31
+ />
32
+ )
33
+ )
34
+ Pagination.displayName = "Pagination"
35
+
36
+ export const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentPropsWithoutRef<"ul">>(
37
+ ({ className, ...props }, ref) => (
38
+ <ul ref={ref} className={cn("ax:flex ax:flex-row ax:items-center ax:gap-1", className)} {...props} />
39
+ )
40
+ )
41
+ PaginationContent.displayName = "PaginationContent"
42
+
43
+ export const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<"li">>(
44
+ (props, ref) => <li ref={ref} {...props} />
45
+ )
46
+ PaginationItem.displayName = "PaginationItem"
47
+
48
+ export const paginationLinkVariants = tv({
49
+ base: [
50
+ "ax:inline-flex ax:items-center ax:justify-center ax:gap-xs ax:whitespace-nowrap ax:select-none ax:cursor-pointer",
51
+ "ax:font-medium ax:leading-none ax:text-sm ax:rounded-(--button-radius)",
52
+ "ax:transition-colors ax:transition-snappy",
53
+ "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",
54
+ "ax:disabled:pointer-events-none ax:disabled:opacity-50",
55
+ "ax:[&_svg]:pointer-events-none ax:[&_svg]:size-4 ax:[&_svg]:shrink-0",
56
+ ],
57
+ variants: {
58
+ isActive: {
59
+ // Mirrors Button's primary fill: emphasis aliases auto-invert in dark.
60
+ true: "ax:bg-primary-weaker ax:text-primary-foreground ax:dark:bg-primary-strong ax:dark:text-primary-weaker",
61
+ false: "ax:bg-transparent ax:text-foreground ax:hover:bg-muted",
62
+ },
63
+ size: {
64
+ sm: "ax:size-2xl",
65
+ md: "ax:size-3xl",
66
+ },
67
+ },
68
+ defaultVariants: {
69
+ isActive: false,
70
+ size: "md",
71
+ },
72
+ }, { twMerge: false })
73
+
74
+ export interface PaginationLinkProps
75
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
76
+ VariantProps<typeof paginationLinkVariants> {
77
+ /** Render the child element instead of a button (e.g. a router `<Link>`). */
78
+ asChild?: boolean
79
+ /** Marks the current page: sets `aria-current="page"` and the active fill. */
80
+ isActive?: boolean
81
+ }
82
+
83
+ export const PaginationLink = React.forwardRef<HTMLButtonElement, PaginationLinkProps>(
84
+ ({ className, isActive, size, asChild = false, type = "button", ...props }, ref) => {
85
+ const Comp = asChild ? Slot : "button"
86
+ return (
87
+ <Comp
88
+ ref={ref}
89
+ type={asChild ? undefined : type}
90
+ aria-current={isActive ? "page" : undefined}
91
+ className={cn(paginationLinkVariants({ isActive, size }), className)}
92
+ {...props}
93
+ />
94
+ )
95
+ }
96
+ )
97
+ PaginationLink.displayName = "PaginationLink"
98
+
99
+ export interface PaginationNavLinkProps extends Omit<PaginationLinkProps, "isActive"> {}
100
+
101
+ export const PaginationPrevious = React.forwardRef<HTMLButtonElement, PaginationNavLinkProps>(
102
+ ({ className, children, "aria-label": ariaLabel = "Go to previous page", ...props }, ref) => (
103
+ <PaginationLink
104
+ ref={ref}
105
+ aria-label={ariaLabel}
106
+ className={cn("ax:w-auto ax:px-sm", className)}
107
+ {...props}
108
+ >
109
+ <ChevronLeft />
110
+ {children !== undefined ? children : <span className="ax:hidden ax:sm:inline">Previous</span>}
111
+ </PaginationLink>
112
+ )
113
+ )
114
+ PaginationPrevious.displayName = "PaginationPrevious"
115
+
116
+ export const PaginationNext = React.forwardRef<HTMLButtonElement, PaginationNavLinkProps>(
117
+ ({ className, children, "aria-label": ariaLabel = "Go to next page", ...props }, ref) => (
118
+ <PaginationLink
119
+ ref={ref}
120
+ aria-label={ariaLabel}
121
+ className={cn("ax:w-auto ax:px-sm", className)}
122
+ {...props}
123
+ >
124
+ {children !== undefined ? children : <span className="ax:hidden ax:sm:inline">Next</span>}
125
+ <ChevronRight />
126
+ </PaginationLink>
127
+ )
128
+ )
129
+ PaginationNext.displayName = "PaginationNext"
130
+
131
+ export const PaginationEllipsis = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<"span">>(
132
+ ({ className, ...props }, ref) => (
133
+ <span
134
+ ref={ref}
135
+ aria-hidden="true"
136
+ className={cn("ax:flex ax:size-3xl ax:items-center ax:justify-center ax:text-muted-foreground", className)}
137
+ {...props}
138
+ >
139
+ <MoreHorizontal className="ax:size-4" />
140
+ <span className="ax:sr-only">More pages</span>
141
+ </span>
142
+ )
143
+ )
144
+ PaginationEllipsis.displayName = "PaginationEllipsis"
@@ -0,0 +1,26 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { Button } from "./button"
4
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover"
5
+
6
+ const meta: Meta<typeof Popover> = {
7
+ title: "Components/Popover",
8
+ component: Popover,
9
+ tags: ["autodocs"],
10
+ }
11
+
12
+ export default meta
13
+ type Story = StoryObj<typeof Popover>
14
+
15
+ export const Default: Story = {
16
+ render: () => (
17
+ <Popover>
18
+ <PopoverTrigger asChild>
19
+ <Button variant="outline">Open popover</Button>
20
+ </PopoverTrigger>
21
+ <PopoverContent className="ax:w-64">
22
+ <p className="ax:t-body-sm">Popover content with helpful context.</p>
23
+ </PopoverContent>
24
+ </Popover>
25
+ ),
26
+ }
@@ -0,0 +1,62 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { cn } from "../lib/cn"
7
+
8
+ export type PopoverSize = "sm" | "md" | "lg"
9
+
10
+ export const Popover = PopoverPrimitive.Root
11
+ export const PopoverTrigger = PopoverPrimitive.Trigger
12
+ export const PopoverAnchor = PopoverPrimitive.Anchor
13
+ export const PopoverClose = PopoverPrimitive.Close
14
+
15
+ // Padding scale is now smooth: sm → md → lg (was sm → md → xl, which skipped lg).
16
+ // Each size sets only width + padding; text sizing is left to the consumer's
17
+ // child elements (headings, body text, fields) so the popover doesn't impose
18
+ // an ambient text-size that fights nested typography.
19
+ const popoverContentVariants = cva(
20
+ [
21
+ "ax:z-[1100] ax:rounded-(--input-radius) ax:border ax:border-border ax:bg-popover ax:text-popover-foreground ax:shadow-md ax:outline-none",
22
+ "ax:data-[state=open]:animate-in ax:data-[state=closed]:animate-out",
23
+ "ax:data-[state=closed]:fade-out-0 ax:data-[state=open]:fade-in-0",
24
+ "ax:data-[state=closed]:zoom-out-[0.96] ax:data-[state=open]:zoom-in-[0.96]",
25
+ "ax:data-[side=bottom]:slide-in-from-top-1",
26
+ "ax:data-[side=left]:slide-in-from-right-1",
27
+ "ax:data-[side=right]:slide-in-from-left-1",
28
+ "ax:data-[side=top]:slide-in-from-bottom-1",
29
+ "ax:[animation-duration:var(--motion-fast)] ax:[animation-timing-function:var(--motion-ease-out)]",
30
+ "ax:motion-reduce:animate-none",
31
+ ],
32
+ {
33
+ variants: {
34
+ size: {
35
+ sm: "ax:w-56 ax:p-sm", // 224px · 12px
36
+ md: "ax:w-72 ax:p-md", // 288px · 16px
37
+ lg: "ax:w-96 ax:p-lg", // 384px · 20px
38
+ },
39
+ },
40
+ defaultVariants: { size: "md" },
41
+ }
42
+ )
43
+
44
+ export interface PopoverContentProps
45
+ extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>,
46
+ VariantProps<typeof popoverContentVariants> {}
47
+
48
+ export const PopoverContent = React.forwardRef<
49
+ React.ElementRef<typeof PopoverPrimitive.Content>,
50
+ PopoverContentProps
51
+ >(({ className, align = "center", sideOffset = 6, size, ...props }, ref) => (
52
+ <PopoverPrimitive.Portal>
53
+ <PopoverPrimitive.Content
54
+ ref={ref}
55
+ align={align}
56
+ sideOffset={sideOffset}
57
+ className={cn(popoverContentVariants({ size }), className)}
58
+ {...props}
59
+ />
60
+ </PopoverPrimitive.Portal>
61
+ ))
62
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName
@@ -0,0 +1,42 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { Button } from "./button"
4
+ import { Pricing } from "./pricing"
5
+
6
+ const tiers = [
7
+ {
8
+ name: "Starter",
9
+ price: "$0",
10
+ period: "/mo",
11
+ description: "For individuals getting started.",
12
+ features: ["1 project", "Basic support"],
13
+ cta: <Button variant="outline" className="ax:w-full">Get started</Button>,
14
+ },
15
+ {
16
+ name: "Pro",
17
+ price: "$29",
18
+ period: "/mo",
19
+ description: "For growing teams.",
20
+ features: ["Unlimited projects", "Priority support"],
21
+ highlighted: true,
22
+ badge: "Popular",
23
+ cta: <Button className="ax:w-full">Start trial</Button>,
24
+ },
25
+ ]
26
+
27
+ const meta: Meta<typeof Pricing> = {
28
+ title: "Components/Pricing",
29
+ component: Pricing,
30
+ tags: ["autodocs"],
31
+ args: {
32
+ eyebrow: "Pricing",
33
+ title: "Simple, transparent pricing",
34
+ description: "Choose the plan that fits your team.",
35
+ tiers,
36
+ },
37
+ }
38
+
39
+ export default meta
40
+ type Story = StoryObj<typeof Pricing>
41
+
42
+ export const Default: Story = {}
@@ -0,0 +1,130 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Check } from "lucide-react"
5
+ import { motion } from "motion/react"
6
+ import { cn } from "../lib/cn"
7
+ import { useReducedMotion } from "../hooks/use-reduced-motion"
8
+ import { stagger, variants, viewport as defaultViewport } from "../lib/motion"
9
+
10
+ export interface PricingTier {
11
+ name: string
12
+ price: React.ReactNode
13
+ period?: string
14
+ description?: string
15
+ features: string[]
16
+ cta: React.ReactNode
17
+ highlighted?: boolean
18
+ badge?: string
19
+ }
20
+
21
+ export interface PricingProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
22
+ eyebrow?: React.ReactNode
23
+ title?: React.ReactNode
24
+ description?: React.ReactNode
25
+ tiers: PricingTier[]
26
+ }
27
+
28
+ export function Pricing({ className, eyebrow, title, description, tiers, ...props }: PricingProps) {
29
+ const reduce = useReducedMotion()
30
+
31
+ return (
32
+ <section className={cn("ax:w-full ax:px-xl ax:md:px-3xl ax:py-5xl ax:md:py-6xl", className)} {...props}>
33
+ <div className="ax:mx-auto ax:max-w-7xl">
34
+ {(eyebrow || title || description) && (
35
+ <div className="ax:mx-auto ax:max-w-2xl ax:text-center ax:mb-4xl ax:md:mb-5xl ax:space-y-md">
36
+ {eyebrow && (
37
+ <span className="ax:inline-block ax:t-overline ax:text-primary">
38
+ {eyebrow}
39
+ </span>
40
+ )}
41
+ {title && (
42
+ <h2 className="ax:t-display-md ax:md:t-display-lg ax:text-foreground ax:[text-wrap:balance]">
43
+ {title}
44
+ </h2>
45
+ )}
46
+ {description && (
47
+ <p className="ax:t-body-md ax:md:t-body-lg ax:text-muted-foreground ax:[text-wrap:pretty]">
48
+ {description}
49
+ </p>
50
+ )}
51
+ </div>
52
+ )}
53
+
54
+ <motion.div
55
+ className={cn(
56
+ "ax:grid ax:gap-xl",
57
+ tiers.length === 2 && "ax:md:grid-cols-2 ax:max-w-3xl ax:mx-auto",
58
+ tiers.length === 3 && "ax:md:grid-cols-3",
59
+ tiers.length >= 4 && "ax:md:grid-cols-2 ax:lg:grid-cols-4",
60
+ )}
61
+ {...(reduce ? {} : {
62
+ variants: stagger(0, 0.1),
63
+ initial: "hidden",
64
+ whileInView: "visible",
65
+ viewport: defaultViewport,
66
+ })}
67
+ >
68
+ {tiers.map((tier, i) => (
69
+ <motion.div
70
+ key={tier.name}
71
+ {...(reduce ? {} : { variants: variants.fadeUp })}
72
+ className={cn(
73
+ "ax:relative ax:flex ax:flex-col ax:rounded-xl ax:border ax:p-xl ax:gap-xl",
74
+ tier.highlighted
75
+ ? "ax:border-primary ax:bg-primary ax:text-primary-foreground ax:shadow-lg"
76
+ : "ax:border-border ax:bg-card ax:text-card-foreground ax:shadow-sm"
77
+ )}
78
+ >
79
+ {tier.badge && (
80
+ <span
81
+ className={cn(
82
+ "ax:absolute ax:-top-3 ax:left-1/2 ax:-translate-x-1/2 ax:rounded-full ax:px-3 ax:py-0.5 ax:t-label-sm",
83
+ tier.highlighted
84
+ ? "ax:bg-background ax:text-foreground"
85
+ : "ax:bg-primary ax:text-primary-foreground"
86
+ )}
87
+ >
88
+ {tier.badge}
89
+ </span>
90
+ )}
91
+
92
+ <div className="ax:space-y-1.5">
93
+ <div className="ax:t-overline ax:opacity-80">{tier.name}</div>
94
+ <div className="ax:flex ax:items-end ax:gap-2xs">
95
+ <span className="ax:t-display-lg">{tier.price}</span>
96
+ {tier.period && (
97
+ <span className="ax:t-body-sm ax:opacity-70 ax:mb-1">/{tier.period}</span>
98
+ )}
99
+ </div>
100
+ {tier.description && (
101
+ <p className={cn("ax:t-body-sm", tier.highlighted ? "ax:opacity-80" : "ax:text-muted-foreground")}>
102
+ {tier.description}
103
+ </p>
104
+ )}
105
+ </div>
106
+
107
+ <ul className="ax:flex-1 ax:space-y-2.5">
108
+ {tier.features.map((feature) => (
109
+ <li key={feature} className="ax:flex ax:items-start ax:gap-2.5 ax:t-body-sm">
110
+ <Check
111
+ className={cn(
112
+ "ax:size-4 ax:shrink-0 ax:mt-0.5",
113
+ tier.highlighted ? "ax:text-primary-foreground" : "ax:text-primary"
114
+ )}
115
+ />
116
+ <span className={tier.highlighted ? "ax:opacity-90" : "ax:text-foreground"}>
117
+ {feature}
118
+ </span>
119
+ </li>
120
+ ))}
121
+ </ul>
122
+
123
+ <div>{tier.cta}</div>
124
+ </motion.div>
125
+ ))}
126
+ </motion.div>
127
+ </div>
128
+ </section>
129
+ )
130
+ }
@@ -0,0 +1,22 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { Progress } from "./progress"
4
+
5
+ const meta: Meta<typeof Progress> = {
6
+ title: "Components/Progress",
7
+ component: Progress,
8
+ tags: ["autodocs"],
9
+ args: {
10
+ value: 65,
11
+ size: "md",
12
+ },
13
+ }
14
+
15
+ export default meta
16
+ type Story = StoryObj<typeof Progress>
17
+
18
+ export const Default: Story = {}
19
+
20
+ export const Ring: Story = {
21
+ args: { shape: "ring", value: 72 },
22
+ }