@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,498 @@
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 { Card, type CardProps } from "./card"
8
+ import { useReducedMotion } from "../hooks/use-reduced-motion"
9
+ import { transitions, variants } from "../lib/motion"
10
+
11
+ export type HeroLayout = "centered" | "split" | "capsule" | "magazine" | "cinematic" | "editorial" | "mosaic" | "search"
12
+
13
+ export interface HeroStat {
14
+ value: React.ReactNode
15
+ label: React.ReactNode
16
+ }
17
+
18
+ export interface HeroProps extends CardProps {
19
+ /** Optional pre-headline label / eyebrow. In magazine layout, appears in the footer-left strip. */
20
+ eyebrow?: React.ReactNode
21
+ /** Required: the marketing headline. Accepts JSX for styled spans. */
22
+ headline: React.ReactNode
23
+ /** Optional supporting paragraph. */
24
+ description?: React.ReactNode
25
+ /** CTA action slots - typically primary + secondary buttons. */
26
+ actions?: React.ReactNode
27
+ /** Main image / media slot. */
28
+ media?: React.ReactNode
29
+ /** Secondary image slot - magazine right column. */
30
+ secondaryMedia?: React.ReactNode
31
+ /** Visual template. Defaults to "split" when media is set, "centered" otherwise. */
32
+ layout?: HeroLayout
33
+ /** Metric pairs rendered below content (capsule/split) or in a bottom grid (cinematic). */
34
+ stats?: HeroStat[]
35
+ /** Brand name list for the magazine footer strip. */
36
+ brands?: React.ReactNode[]
37
+ /** Pill / live-indicator badge. split: above headline. cinematic: top-right. */
38
+ badge?: React.ReactNode
39
+ /** Brand identity element - cinematic top-left logo. */
40
+ brand?: React.ReactNode
41
+ /** Content overlaid on the media column - split floating cards, badges, etc. */
42
+ overlay?: React.ReactNode
43
+ /** Search widget slot - rendered below description in the "search" layout. */
44
+ search?: React.ReactNode
45
+ /** Trust-point strings shown below the search widget. A checkmark is prepended to each. */
46
+ badges?: string[]
47
+ /** "dark" applies a deep neutral background - intended for the cinematic layout. */
48
+ theme?: "light" | "dark"
49
+ }
50
+
51
+ export const Hero = React.forwardRef<HTMLDivElement, HeroProps>(
52
+ (
53
+ {
54
+ className,
55
+ style,
56
+ eyebrow,
57
+ headline,
58
+ description,
59
+ actions,
60
+ media,
61
+ secondaryMedia,
62
+ layout: layoutProp,
63
+ stats,
64
+ brands,
65
+ badge,
66
+ brand,
67
+ overlay,
68
+ search,
69
+ badges,
70
+ theme,
71
+ padding = "none",
72
+ variant,
73
+ ...props
74
+ },
75
+ ref
76
+ ) => {
77
+ const reduce = useReducedMotion()
78
+ const layout = layoutProp ?? (media ? "split" : "centered")
79
+
80
+ // cinematic manages its own background: use ghost to avoid bg-card conflict
81
+ const cardVariant = variant ?? (layout === "cinematic" ? "ghost" : "default")
82
+
83
+ const mergedStyle: React.CSSProperties = {
84
+ ...(theme === "dark" ? { background: "hsl(var(--neutral-950))" } : {}),
85
+ ...style,
86
+ }
87
+
88
+ const enter = reduce
89
+ ? {}
90
+ : {
91
+ variants: variants.fadeUp,
92
+ initial: "hidden" as const,
93
+ animate: "visible" as const,
94
+ transition: transitions.slow,
95
+ }
96
+
97
+ const cardRoot = (children: React.ReactNode) => (
98
+ <Card
99
+ ref={ref}
100
+ variant={cardVariant}
101
+ padding={padding}
102
+ style={mergedStyle}
103
+ className={cn("ax:relative ax:w-full", className)}
104
+ {...props}
105
+ >
106
+ {children}
107
+ </Card>
108
+ )
109
+
110
+ // ── Centered ─────────────────────────────────────────────────────────
111
+ if (layout === "centered") {
112
+ return cardRoot(
113
+ <div className="ax:px-xl ax:md:px-3xl ax:py-5xl ax:md:py-6xl ax:mx-auto ax:max-w-7xl">
114
+ <div className="ax:max-w-3xl ax:text-center ax:mx-auto">
115
+ <motion.div {...enter} className="ax:flex ax:flex-col ax:gap-lg ax:items-center">
116
+ {eyebrow && (
117
+ <span className="ax:inline-flex ax:items-center ax:self-start ax:t-overline ax:text-primary ax:px-2.5 ax:py-1 ax:rounded-full ax:bg-primary/10">
118
+ {eyebrow}
119
+ </span>
120
+ )}
121
+ <h1 className="ax:t-display-lg ax:md:t-display-xl ax:lg:t-display-2xl ax:text-foreground ax:[text-wrap:balance]">
122
+ {headline}
123
+ </h1>
124
+ {description && (
125
+ <p className="ax:t-body-md ax:md:t-body-lg ax:text-muted-foreground ax:[text-wrap:pretty] ax:max-w-prose ax:mx-auto">
126
+ {description}
127
+ </p>
128
+ )}
129
+ {actions && <div className="ax:flex ax:flex-wrap ax:gap-sm ax:pt-xs">{actions}</div>}
130
+ </motion.div>
131
+ </div>
132
+ </div>
133
+ )
134
+ }
135
+
136
+ // ── Split ─────────────────────────────────────────────────────────────
137
+ if (layout === "split") {
138
+ return cardRoot(
139
+ <div className="ax:grid ax:md:grid-cols-2 ax:min-h-[480px]">
140
+ <motion.div
141
+ {...enter}
142
+ className="ax:flex ax:flex-col ax:justify-center ax:gap-6 ax:px-10 ax:py-14 ax:bg-background"
143
+ >
144
+ {badge && <div className="ax:w-fit">{badge}</div>}
145
+ <div>
146
+ <h1 className="ax:t-display-lg ax:md:t-display-xl ax:text-foreground ax:leading-[1.1] ax:[text-wrap:balance]">
147
+ {headline}
148
+ </h1>
149
+ {description && (
150
+ <p className="ax:mt-4 ax:t-body-md ax:text-muted-foreground ax:max-w-sm ax:[text-wrap:pretty]">
151
+ {description}
152
+ </p>
153
+ )}
154
+ </div>
155
+ {actions && (
156
+ <div className="ax:flex ax:items-center ax:gap-3 ax:flex-wrap">{actions}</div>
157
+ )}
158
+ {stats && stats.length > 0 && (
159
+ <div className="ax:flex ax:items-center ax:gap-5 ax:pt-2 ax:flex-wrap">
160
+ {stats.map((stat, i) => (
161
+ <div key={i}>
162
+ <p className="ax:t-h3 ax:font-bold ax:text-foreground ax:tabular-nums">{stat.value}</p>
163
+ <p className="ax:t-caption ax:text-muted-foreground">{stat.label}</p>
164
+ </div>
165
+ ))}
166
+ </div>
167
+ )}
168
+ </motion.div>
169
+ {media && (
170
+ <motion.div
171
+ {...(reduce
172
+ ? {}
173
+ : {
174
+ variants: variants.scaleIn,
175
+ initial: "hidden" as const,
176
+ animate: "visible" as const,
177
+ transition: { ...transitions.slow, delay: 0.1 },
178
+ })}
179
+ className="ax:relative ax:min-h-[320px] ax:overflow-hidden"
180
+ >
181
+ {media}
182
+ {overlay}
183
+ </motion.div>
184
+ )}
185
+ </div>
186
+ )
187
+ }
188
+
189
+ // ── Capsule ───────────────────────────────────────────────────────────
190
+ if (layout === "capsule") {
191
+ return cardRoot(
192
+ <motion.div {...enter} className="ax:px-8 ax:pt-10 ax:pb-8 ax:md:px-12 ax:md:pt-12 ax:md:pb-10">
193
+ {/* Row 1 - Headline + description */}
194
+ <div className="ax:flex ax:items-start ax:justify-between ax:gap-8 ax:mb-10">
195
+ <h1 className="ax:t-display-lg ax:md:t-display-xl ax:font-semibold ax:leading-[1.06] ax:text-foreground ax:[text-wrap:balance]">
196
+ {headline}
197
+ </h1>
198
+ {description && (
199
+ <p className="ax:hidden ax:md:block ax:t-body-sm ax:text-muted-foreground ax:leading-relaxed ax:max-w-[260px] ax:shrink-0">
200
+ {description}
201
+ </p>
202
+ )}
203
+ </div>
204
+ {/* Row 2 - Capsule image + featured stat */}
205
+ {(media || stats?.[0]) && (
206
+ <div className="ax:flex ax:items-center ax:gap-8 ax:mb-8">
207
+ {media && (
208
+ <div className="ax:flex-1 ax:rounded-full ax:overflow-hidden ax:h-[200px] ax:md:h-[240px] ax:lg:h-[260px]">
209
+ {media}
210
+ </div>
211
+ )}
212
+ {stats?.[0] && (
213
+ <div className="ax:hidden ax:md:block ax:shrink-0 ax:text-right">
214
+ <p className="ax:t-display-sm ax:lg:t-display-xl ax:font-bold ax:text-foreground ax:tabular-nums ax:leading-none">
215
+ {stats[0].value}
216
+ </p>
217
+ <p className="ax:t-body-sm ax:text-muted-foreground ax:mt-2">{stats[0].label}</p>
218
+ </div>
219
+ )}
220
+ </div>
221
+ )}
222
+ {/* Row 3 - Bottom stats */}
223
+ {stats && stats.length > 1 && (
224
+ <div className="ax:flex ax:items-center ax:gap-10 ax:md:gap-16">
225
+ {stats.slice(1).map((stat, i) => (
226
+ <div key={i}>
227
+ <p className="ax:t-h1 ax:md:t-display-md ax:font-bold ax:text-foreground ax:tabular-nums">{stat.value}</p>
228
+ <p className="ax:t-caption ax:text-muted-foreground ax:mt-0.5">{stat.label}</p>
229
+ </div>
230
+ ))}
231
+ </div>
232
+ )}
233
+ {actions && <div className="ax:flex ax:flex-wrap ax:gap-sm ax:mt-lg">{actions}</div>}
234
+ </motion.div>
235
+ )
236
+ }
237
+
238
+ // ── Magazine ──────────────────────────────────────────────────────────
239
+ if (layout === "magazine") {
240
+ return cardRoot(
241
+ <motion.div {...enter} className="ax:p-8 ax:md:p-10 ax:lg:p-12">
242
+ {/* Row 1 - Headline + description */}
243
+ <div className="ax:flex ax:items-start ax:justify-between ax:gap-6 ax:mb-8 ax:md:mb-10">
244
+ <h1 className="ax:t-display-sm ax:md:t-display-xl ax:lg:t-display-2xl ax:leading-[0.92] ax:text-foreground ax:[text-wrap:balance]">
245
+ {headline}
246
+ </h1>
247
+ {description && (
248
+ <p className="ax:hidden ax:md:block ax:t-body-sm ax:text-muted-foreground ax:leading-relaxed ax:max-w-[190px] ax:shrink-0 ax:pt-1">
249
+ {description}
250
+ </p>
251
+ )}
252
+ </div>
253
+ {/* Row 2 - Main image + right column */}
254
+ {(media || secondaryMedia || stats?.[0]) && (
255
+ <div className="ax:flex ax:gap-3 ax:items-stretch">
256
+ {media && (
257
+ <div className="ax:relative ax:flex-1 ax:rounded-xl ax:overflow-hidden ax:min-h-[260px] ax:md:min-h-[340px] ax:lg:min-h-[380px]">
258
+ {media}
259
+ </div>
260
+ )}
261
+ {(stats?.[0] || secondaryMedia) && (
262
+ <div className="ax:hidden ax:md:flex ax:flex-col ax:gap-3 ax:shrink-0 ax:w-36 ax:lg:w-44">
263
+ {stats?.[0] && (
264
+ <div className="ax:rounded-xl ax:bg-muted ax:px-4 ax:py-5">
265
+ <p className="ax:t-display-md ax:lg:t-display-lg ax:font-black ax:text-foreground ax:tabular-nums ax:leading-none">
266
+ {stats[0].value}
267
+ </p>
268
+ <p className="ax:t-overline ax:text-muted-foreground ax:mt-2">{stats[0].label}</p>
269
+ </div>
270
+ )}
271
+ {secondaryMedia && (
272
+ <div className="ax:relative ax:flex-1 ax:rounded-xl ax:overflow-hidden">{secondaryMedia}</div>
273
+ )}
274
+ </div>
275
+ )}
276
+ </div>
277
+ )}
278
+ {/* Row 3 - Footer strip */}
279
+ {(eyebrow || brands || stats?.[1]) && (
280
+ <div className="ax:flex ax:items-center ax:justify-between ax:gap-4 ax:mt-6 ax:pt-5 ax:border-t ax:border-border">
281
+ {eyebrow && (
282
+ <p className="ax:t-overline ax:font-black ax:text-muted-foreground ax:leading-snug ax:shrink-0">
283
+ {eyebrow}
284
+ </p>
285
+ )}
286
+ {brands && brands.length > 0 && (
287
+ <div className="ax:hidden ax:sm:flex ax:items-center ax:gap-5 ax:t-label-sm ax:text-muted-foreground/50">
288
+ {brands.map((b, i) => (
289
+ <span key={i}>{b}</span>
290
+ ))}
291
+ </div>
292
+ )}
293
+ {stats?.[1] && (
294
+ <div className="ax:shrink-0 ax:text-right">
295
+ <p className="ax:t-h1 ax:lg:t-display-md ax:font-black ax:text-foreground ax:tabular-nums ax:leading-none">
296
+ {stats[1].value}
297
+ </p>
298
+ <p className="ax:t-overline ax:text-muted-foreground ax:mt-1">{stats[1].label}</p>
299
+ </div>
300
+ )}
301
+ </div>
302
+ )}
303
+ </motion.div>
304
+ )
305
+ }
306
+
307
+ // ── Editorial ─────────────────────────────────────────────────────────
308
+ if (layout === "editorial") {
309
+ return cardRoot(
310
+ <>
311
+ {/* Top accent rule */}
312
+ <div className="ax:h-1 ax:bg-primary" />
313
+ <div className="ax:px-10 ax:py-14">
314
+ {/* Eyebrow row - dateline + issue badge */}
315
+ <div className="ax:flex ax:items-center ax:justify-between ax:mb-8">
316
+ <div className="ax:flex ax:items-center ax:gap-3">
317
+ <span className="ax:h-px ax:w-8 ax:inline-block ax:bg-primary" />
318
+ <p className="ax:t-code ax:uppercase ax:tracking-[0.25em] ax:text-muted-foreground">{eyebrow}</p>
319
+ </div>
320
+ {badge}
321
+ </div>
322
+ <motion.div {...enter}>
323
+ {/* Oversized headline */}
324
+ <h1 className="ax:t-display-sm ax:md:t-display-2xl ax:lg:t-display-3xl ax:text-foreground ax:[text-wrap:balance]">
325
+ {headline}
326
+ </h1>
327
+ {/* Below-headline grid - description left, stats + actions right */}
328
+ <div className="ax:mt-8 ax:pt-8 ax:border-t ax:border-border ax:grid ax:md:grid-cols-[2fr_1fr] ax:gap-8">
329
+ {description && (
330
+ <p className="ax:t-body-md ax:text-muted-foreground ax:leading-relaxed ax:max-w-prose">
331
+ {description}
332
+ </p>
333
+ )}
334
+ <div className="ax:flex ax:flex-col ax:gap-4 ax:justify-between">
335
+ {stats && stats.length > 0 && (
336
+ <div className="ax:grid ax:grid-cols-2 ax:gap-4">
337
+ {stats.map((stat, i) => (
338
+ <div key={i} className="ax:border-l-2 ax:border-primary ax:pl-3">
339
+ <p className="ax:t-h2">{stat.value}</p>
340
+ <p className="ax:t-caption ax:text-muted-foreground ax:mt-0.5">{stat.label}</p>
341
+ </div>
342
+ ))}
343
+ </div>
344
+ )}
345
+ {actions && <div className="ax:flex ax:gap-2 ax:mt-auto">{actions}</div>}
346
+ </div>
347
+ </div>
348
+ </motion.div>
349
+ </div>
350
+ {/* Bottom rule */}
351
+ <div className="ax:h-px ax:bg-border" />
352
+ </>
353
+ )
354
+ }
355
+
356
+ // ── Mosaic ────────────────────────────────────────────────────────────
357
+ if (layout === "mosaic") {
358
+ return cardRoot(
359
+ <div className="ax:grid ax:md:grid-cols-[1fr_1.2fr] ax:min-h-[480px]">
360
+ <motion.div {...enter} className="ax:flex ax:flex-col ax:justify-center ax:gap-6 ax:px-10 ax:py-14">
361
+ {eyebrow && <p className="ax:t-overline ax:text-primary">{eyebrow}</p>}
362
+ <h1 className="ax:t-display-lg ax:md:t-display-xl ax:text-foreground ax:leading-[1.1] ax:[text-wrap:balance]">
363
+ {headline}
364
+ </h1>
365
+ {description && (
366
+ <p className="ax:t-body-sm ax:text-muted-foreground ax:leading-relaxed ax:max-w-xs">{description}</p>
367
+ )}
368
+ {actions && <div className="ax:flex ax:flex-col ax:gap-3">{actions}</div>}
369
+ </motion.div>
370
+ {media && (
371
+ <motion.div
372
+ {...(reduce
373
+ ? {}
374
+ : {
375
+ variants: variants.scaleIn,
376
+ initial: "hidden" as const,
377
+ animate: "visible" as const,
378
+ transition: { ...transitions.slow, delay: 0.1 },
379
+ })}
380
+ className="ax:p-4"
381
+ >
382
+ {media}
383
+ </motion.div>
384
+ )}
385
+ </div>
386
+ )
387
+ }
388
+
389
+ // ── Search-first ──────────────────────────────────────────────────────
390
+ if (layout === "search") {
391
+ return cardRoot(
392
+ <div className="ax:px-6 ax:py-16 ax:text-center">
393
+ <motion.div {...enter} className="ax:flex ax:flex-col ax:items-center ax:gap-4">
394
+ {eyebrow && <p className="ax:t-overline ax:text-primary">{eyebrow}</p>}
395
+ <h1 className="ax:t-display-lg ax:md:t-display-xl ax:text-foreground ax:leading-tight ax:[text-wrap:balance]">
396
+ {headline}
397
+ </h1>
398
+ {description && (
399
+ <p className="ax:t-body-md ax:text-muted-foreground ax:max-w-lg ax:mx-auto ax:[text-wrap:pretty]">
400
+ {description}
401
+ </p>
402
+ )}
403
+ {search && <div className="ax:mt-6 ax:w-full ax:max-w-2xl ax:mx-auto">{search}</div>}
404
+ {badges && badges.length > 0 && (
405
+ <div className="ax:mt-4 ax:flex ax:items-center ax:justify-center ax:gap-6 ax:flex-wrap">
406
+ {badges.map((b, i) => (
407
+ <div key={i} className="ax:flex ax:items-center ax:gap-1.5 ax:t-caption ax:text-muted-foreground">
408
+ <Check className="ax:size-3.5 ax:text-primary ax:shrink-0" />
409
+ {b}
410
+ </div>
411
+ ))}
412
+ </div>
413
+ )}
414
+ </motion.div>
415
+ </div>
416
+ )
417
+ }
418
+
419
+ // ── Cinematic ─────────────────────────────────────────────────────────
420
+ const isDark = theme === "dark"
421
+ const col = {
422
+ text: isDark ? "hsl(var(--neutral-50))" : undefined,
423
+ muted: isDark ? "hsl(var(--neutral-400))" : undefined,
424
+ dim: isDark ? "hsl(var(--neutral-500))" : undefined,
425
+ bg: isDark ? "hsl(var(--neutral-900))" : undefined,
426
+ border: isDark ? "hsl(var(--neutral-800))" : undefined,
427
+ }
428
+
429
+ return cardRoot(
430
+ <div className="ax:relative ax:min-h-[520px] ax:flex ax:flex-col ax:justify-between ax:px-10 ax:py-14 ax:overflow-hidden">
431
+ {/* Atmospheric glow decorations */}
432
+ <div
433
+ className="ax:absolute ax:-top-32 ax:left-1/2 ax:-translate-x-1/2 ax:w-[600px] ax:h-[400px] ax:rounded-full ax:opacity-20 ax:blur-3xl ax:pointer-events-none"
434
+ style={{ background: "hsl(var(--primary))" }}
435
+ />
436
+ <div
437
+ className="ax:absolute ax:-bottom-20 ax:right-0 ax:w-72 ax:h-72 ax:rounded-full ax:opacity-10 ax:blur-3xl ax:pointer-events-none"
438
+ style={{ background: "hsl(var(--accent))" }}
439
+ />
440
+ {/* Top bar - brand identity + live badge */}
441
+ {(brand || badge) && (
442
+ <div className="ax:relative ax:flex ax:items-center ax:justify-between">{brand}{badge}</div>
443
+ )}
444
+ {/* Center - eyebrow + headline + description + actions */}
445
+ <motion.div {...enter} className="ax:relative ax:text-center ax:py-8">
446
+ {eyebrow && (
447
+ <p
448
+ className="ax:t-code ax:uppercase ax:tracking-[0.3em] ax:mb-6"
449
+ style={{ color: col.dim }}
450
+ >
451
+ {eyebrow}
452
+ </p>
453
+ )}
454
+ <h1
455
+ className="ax:t-display-xl ax:md:t-display-3xl ax:[text-wrap:balance]"
456
+ style={{ color: col.text }}
457
+ >
458
+ {headline}
459
+ </h1>
460
+ {description && (
461
+ <p className="ax:mt-6 ax:t-body-sm ax:max-w-md ax:mx-auto" style={{ color: col.muted }}>
462
+ {description}
463
+ </p>
464
+ )}
465
+ {actions && (
466
+ <div className="ax:mt-8 ax:flex ax:items-center ax:justify-center ax:gap-3">{actions}</div>
467
+ )}
468
+ </motion.div>
469
+ {/* Bottom stats grid */}
470
+ {stats && stats.length > 0 && (
471
+ <div
472
+ className="ax:relative ax:grid ax:gap-px ax:rounded-lg ax:overflow-hidden"
473
+ style={{
474
+ gridTemplateColumns: `repeat(${stats.length}, 1fr)`,
475
+ border: col.border ? `1px solid ${col.border}` : undefined,
476
+ }}
477
+ >
478
+ {stats.map((stat, i) => (
479
+ <div
480
+ key={i}
481
+ className="ax:flex ax:flex-col ax:items-center ax:gap-1 ax:px-4 ax:py-3"
482
+ style={{ background: col.bg }}
483
+ >
484
+ <p className="ax:t-h5 ax:font-bold ax:tabular-nums" style={{ color: col.text }}>
485
+ {stat.value}
486
+ </p>
487
+ <p className="ax:text-[10px]" style={{ color: col.dim }}>
488
+ {stat.label}
489
+ </p>
490
+ </div>
491
+ ))}
492
+ </div>
493
+ )}
494
+ </div>
495
+ )
496
+ }
497
+ )
498
+ Hero.displayName = "Hero"
@@ -0,0 +1,19 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { InputButton } from "./input-button"
4
+
5
+ const meta: Meta<typeof InputButton> = {
6
+ title: "Components/InputButton",
7
+ component: InputButton,
8
+ tags: ["autodocs"],
9
+ args: {
10
+ placeholder: "Enter your email",
11
+ buttonLabel: "Subscribe",
12
+ onButtonClick: () => {},
13
+ },
14
+ }
15
+
16
+ export default meta
17
+ type Story = StoryObj<typeof InputButton>
18
+
19
+ export const Default: Story = {}
@@ -0,0 +1,112 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Button, type ButtonProps } from "./button"
5
+ import { Input, type InputProps, type InputSize } from "./input"
6
+ import { cn } from "../lib/cn"
7
+
8
+ /**
9
+ * InputButton - a text Input + Button compactly joined into a single inline group.
10
+ *
11
+ * The sibling of SelectButton: where that pairs a Select with a commit action,
12
+ * this pairs a free-text Input with one (search, submit, apply). Both halves
13
+ * share the same height ladder (sm/md/lg align with Button, Input, Select, and
14
+ * Dropdown) so they line up when placed side-by-side, and they overlap by 1px so
15
+ * the seam reads as a single squared pill.
16
+ *
17
+ * <InputButton
18
+ * size="md"
19
+ * placeholder="e.g. AUH-100482"
20
+ * value={value}
21
+ * onChange={(e) => setValue(e.target.value)}
22
+ * buttonType="submit"
23
+ * buttonLabel="Search"
24
+ * />
25
+ *
26
+ * All standard input attributes (value, onChange, placeholder, disabled, name,
27
+ * aria-*, …) are forwarded to the inner Input. `disabled` disables BOTH halves;
28
+ * use `buttonDisabled` to disable only the button.
29
+ */
30
+
31
+ export type InputButtonSize = InputSize
32
+
33
+ /**
34
+ * Variant whitelist for the right-hand action button - mirrors SelectButton.
35
+ * Excludes Button variants that don't fit the joined-pill composite:
36
+ * ghost - transparent background reads as "no button"
37
+ * accent - wrong semantics for a commit action
38
+ * destructive - wrong semantics for a search/submit toolbar
39
+ * link - underline-on-hover text breaks the joined-pill shape
40
+ */
41
+ export type InputButtonVariant = Exclude<NonNullable<ButtonProps["variant"]>, "ghost" | "accent" | "destructive">
42
+
43
+ export interface InputButtonProps extends Omit<InputProps, "size"> {
44
+ /** Shared height/typography ladder - matches Button, Input, Select. */
45
+ size?: InputButtonSize
46
+ /** Visual treatment of the right-hand action button. */
47
+ variant?: InputButtonVariant
48
+
49
+ // ─── Button half ──────────────────────────────────────────────────────────────
50
+ buttonLabel: React.ReactNode
51
+ onButtonClick?: React.MouseEventHandler<HTMLButtonElement>
52
+ buttonDisabled?: boolean
53
+ buttonType?: "button" | "submit" | "reset"
54
+ /** Extra className appended to the inner Button. */
55
+ buttonClassName?: string
56
+
57
+ /** Extra className appended to the inner Input. */
58
+ inputClassName?: string
59
+
60
+ /** className for the wrapping group (the input fills the remaining width). */
61
+ className?: string
62
+ }
63
+
64
+ export const InputButton = React.forwardRef<HTMLInputElement, InputButtonProps>(
65
+ (
66
+ {
67
+ size = "md",
68
+ variant = "primary",
69
+ buttonLabel,
70
+ onButtonClick,
71
+ buttonDisabled,
72
+ buttonType = "button",
73
+ buttonClassName,
74
+ inputClassName,
75
+ className,
76
+ disabled,
77
+ ...inputProps
78
+ },
79
+ ref
80
+ ) => (
81
+ <div data-slot="input-button" className={cn("ax:isolate ax:flex ax:w-full ax:items-stretch", className)}>
82
+ <Input
83
+ ref={ref}
84
+ size={size}
85
+ disabled={disabled}
86
+ className={cn(
87
+ // Join: square the right corners, overlap the button by 1px, and lift
88
+ // on focus so the focus ring sits above the adjoining button.
89
+ "ax:relative ax:min-w-0 ax:flex-1 ax:-mr-px ax:rounded-r-none ax:focus-visible:z-10",
90
+ inputClassName
91
+ )}
92
+ {...inputProps}
93
+ />
94
+ <Button
95
+ type={buttonType}
96
+ size={size}
97
+ variant={variant}
98
+ onClick={onButtonClick}
99
+ disabled={buttonDisabled || disabled}
100
+ className={cn(
101
+ // Use --input-radius (not --radius) on the right corner so the
102
+ // composite stays squared in pill brands, matches SelectButton.
103
+ "ax:shrink-0 ax:rounded-l-none ax:rounded-r-[var(--input-radius)] ax:focus-visible:z-10",
104
+ buttonClassName
105
+ )}
106
+ >
107
+ {buttonLabel}
108
+ </Button>
109
+ </div>
110
+ )
111
+ )
112
+ InputButton.displayName = "InputButton"
@@ -0,0 +1,38 @@
1
+ import React from "react"
2
+ import type { Meta, StoryObj } from "@storybook/react"
3
+ import { InputGroup } from "./input-group"
4
+ import { SelectItem } from "./select"
5
+
6
+ const meta: Meta<typeof InputGroup> = {
7
+ title: "Components/InputGroup",
8
+ component: InputGroup,
9
+ tags: ["autodocs"],
10
+ args: {
11
+ selectDefaultValue: "mr",
12
+ selectPlaceholder: "Title",
13
+ placeholder: "Full name",
14
+ children: (
15
+ <>
16
+ <SelectItem value="mr">Mr.</SelectItem>
17
+ <SelectItem value="ms">Ms.</SelectItem>
18
+ <SelectItem value="dr">Dr.</SelectItem>
19
+ <SelectItem value="prof">Prof.</SelectItem>
20
+ </>
21
+ ),
22
+ },
23
+ }
24
+
25
+ export default meta
26
+ type Story = StoryObj<typeof InputGroup>
27
+
28
+ export const Default: Story = {}
29
+
30
+ export const Sizes: Story = {
31
+ render: (args) => (
32
+ <div className="ax:flex ax:flex-col ax:gap-md ax:max-w-sm">
33
+ <InputGroup {...args} size="sm" />
34
+ <InputGroup {...args} size="md" />
35
+ <InputGroup {...args} size="lg" />
36
+ </div>
37
+ ),
38
+ }