@dtsports/design-system 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/components/accordion.d.ts +19 -0
  4. package/dist/components/accordion.d.ts.map +1 -0
  5. package/dist/components/accordion.js +26 -0
  6. package/dist/components/accordion.js.map +1 -0
  7. package/dist/components/alert.d.ts +26 -0
  8. package/dist/components/alert.d.ts.map +1 -0
  9. package/dist/components/alert.js.map +1 -0
  10. package/dist/components/app-shell.d.ts +21 -0
  11. package/dist/components/app-shell.d.ts.map +1 -0
  12. package/dist/components/app-shell.js +25 -0
  13. package/dist/components/app-shell.js.map +1 -0
  14. package/dist/components/aspect-ratio.d.ts +16 -0
  15. package/dist/components/aspect-ratio.d.ts.map +1 -0
  16. package/dist/components/aspect-ratio.js +17 -0
  17. package/dist/components/aspect-ratio.js.map +1 -0
  18. package/dist/components/avatar-group.d.ts +31 -0
  19. package/dist/components/avatar-group.d.ts.map +1 -0
  20. package/dist/components/avatar.d.ts +15 -0
  21. package/dist/components/avatar.d.ts.map +1 -0
  22. package/dist/components/avatar.js +20 -0
  23. package/dist/components/avatar.js.map +1 -0
  24. package/dist/components/badge.d.ts +27 -0
  25. package/dist/components/badge.d.ts.map +1 -0
  26. package/dist/components/badge.js +51 -0
  27. package/dist/components/badge.js.map +1 -0
  28. package/dist/components/breadcrumb.js.map +1 -0
  29. package/dist/components/button.d.ts +33 -0
  30. package/dist/components/button.d.ts.map +1 -0
  31. package/dist/components/button.js +58 -0
  32. package/dist/components/button.js.map +1 -0
  33. package/dist/components/calendar.d.ts +15 -0
  34. package/dist/components/calendar.d.ts.map +1 -0
  35. package/dist/components/calendar.js +33 -0
  36. package/dist/components/calendar.js.map +1 -0
  37. package/dist/components/capacity-bar.d.ts +32 -0
  38. package/dist/components/capacity-bar.d.ts.map +1 -0
  39. package/dist/components/capacity-bar.js +29 -0
  40. package/dist/components/capacity-bar.js.map +1 -0
  41. package/dist/components/card.d.ts +26 -0
  42. package/dist/components/card.d.ts.map +1 -0
  43. package/dist/components/card.js +20 -0
  44. package/dist/components/checkbox.d.ts +11 -0
  45. package/dist/components/checkbox.d.ts.map +1 -0
  46. package/dist/components/checkbox.js +19 -0
  47. package/dist/components/chip.d.ts +23 -0
  48. package/dist/components/chip.d.ts.map +1 -0
  49. package/dist/components/chip.js +32 -0
  50. package/dist/components/chip.js.map +1 -0
  51. package/dist/components/collapsible.d.ts +16 -0
  52. package/dist/components/collapsible.js +22 -0
  53. package/dist/components/collapsible.js.map +1 -0
  54. package/dist/components/combobox.d.ts +38 -0
  55. package/dist/components/combobox.d.ts.map +1 -0
  56. package/dist/components/combobox.js +17 -0
  57. package/dist/components/combobox.js.map +1 -0
  58. package/dist/components/confirm-action.d.ts +36 -0
  59. package/dist/components/confirm-action.d.ts.map +1 -0
  60. package/dist/components/confirm-action.js +21 -0
  61. package/dist/components/confirm-action.js.map +1 -0
  62. package/dist/components/container.js.map +1 -0
  63. package/dist/components/date-picker.d.ts.map +1 -0
  64. package/dist/components/date-picker.js +22 -0
  65. package/dist/components/date-picker.js.map +1 -0
  66. package/dist/components/dialog.d.ts +28 -0
  67. package/dist/components/dialog.d.ts.map +1 -0
  68. package/dist/components/dialog.js.map +1 -0
  69. package/dist/components/drawer.js +55 -0
  70. package/dist/components/drawer.js.map +1 -0
  71. package/dist/components/dropdown-menu.d.ts +39 -0
  72. package/dist/components/dropdown-menu.d.ts.map +1 -0
  73. package/dist/components/dropdown-menu.js +47 -0
  74. package/dist/components/empty-state.d.ts +15 -0
  75. package/dist/components/empty-state.d.ts.map +1 -0
  76. package/dist/components/empty-state.js +6 -0
  77. package/dist/components/form-field.d.ts +32 -0
  78. package/dist/components/form-field.d.ts.map +1 -0
  79. package/dist/components/form-field.js +31 -0
  80. package/dist/components/form-field.js.map +1 -0
  81. package/dist/components/input.d.ts +10 -0
  82. package/dist/components/input.d.ts.map +1 -0
  83. package/dist/components/input.js +6 -0
  84. package/dist/components/input.js.map +1 -0
  85. package/dist/components/label.d.ts +11 -0
  86. package/dist/components/label.d.ts.map +1 -0
  87. package/dist/components/label.js +13 -0
  88. package/dist/components/label.js.map +1 -0
  89. package/dist/components/live-pill.d.ts +14 -0
  90. package/dist/components/live-pill.js +15 -0
  91. package/dist/components/live-pill.js.map +1 -0
  92. package/dist/components/metric-tile.d.ts +23 -0
  93. package/dist/components/metric-tile.d.ts.map +1 -0
  94. package/dist/components/money-breakdown.d.ts +36 -0
  95. package/dist/components/money-breakdown.d.ts.map +1 -0
  96. package/dist/components/money-breakdown.js +12 -0
  97. package/dist/components/money-breakdown.js.map +1 -0
  98. package/dist/components/number-input.d.ts +16 -0
  99. package/dist/components/number-input.d.ts.map +1 -0
  100. package/dist/components/number-input.js +30 -0
  101. package/dist/components/number-input.js.map +1 -0
  102. package/dist/components/pagination.d.ts +27 -0
  103. package/dist/components/pagination.d.ts.map +1 -0
  104. package/dist/components/pagination.js +52 -0
  105. package/dist/components/pagination.js.map +1 -0
  106. package/dist/components/popover.d.ts +24 -0
  107. package/dist/components/popover.d.ts.map +1 -0
  108. package/dist/components/popover.js +28 -0
  109. package/dist/components/popover.js.map +1 -0
  110. package/dist/components/progress.d.ts +13 -0
  111. package/dist/components/progress.d.ts.map +1 -0
  112. package/dist/components/progress.js +17 -0
  113. package/dist/components/radio-group.d.ts +10 -0
  114. package/dist/components/radio-group.d.ts.map +1 -0
  115. package/dist/components/radio-group.js +17 -0
  116. package/dist/components/radio-group.js.map +1 -0
  117. package/dist/components/scroll-area.d.ts.map +1 -0
  118. package/dist/components/scroll-area.js +22 -0
  119. package/dist/components/scroll-area.js.map +1 -0
  120. package/dist/components/select.d.ts +27 -0
  121. package/dist/components/select.d.ts.map +1 -0
  122. package/dist/components/select.js +40 -0
  123. package/dist/components/select.js.map +1 -0
  124. package/dist/components/separator.d.ts +10 -0
  125. package/dist/components/separator.d.ts.map +1 -0
  126. package/dist/components/separator.js +14 -0
  127. package/dist/components/separator.js.map +1 -0
  128. package/dist/components/sidebar.d.ts.map +1 -0
  129. package/dist/components/sidebar.js +48 -0
  130. package/dist/components/sidebar.js.map +1 -0
  131. package/dist/components/skeleton.d.ts +23 -0
  132. package/dist/components/skeleton.js +25 -0
  133. package/dist/components/skeleton.js.map +1 -0
  134. package/dist/components/slider.d.ts +14 -0
  135. package/dist/components/slider.d.ts.map +1 -0
  136. package/dist/components/slider.js +30 -0
  137. package/dist/components/slider.js.map +1 -0
  138. package/dist/components/spec.d.ts +35 -0
  139. package/dist/components/spec.js +31 -0
  140. package/dist/components/spec.js.map +1 -0
  141. package/dist/components/spinner.d.ts +20 -0
  142. package/dist/components/spinner.d.ts.map +1 -0
  143. package/dist/components/spinner.js +18 -0
  144. package/dist/components/spinner.js.map +1 -0
  145. package/dist/components/stack.d.ts +48 -0
  146. package/dist/components/stack.d.ts.map +1 -0
  147. package/dist/components/stack.js +43 -0
  148. package/dist/components/stack.js.map +1 -0
  149. package/dist/components/status-pill.js +34 -0
  150. package/dist/components/status-pill.js.map +1 -0
  151. package/dist/components/switch.d.ts +9 -0
  152. package/dist/components/switch.d.ts.map +1 -0
  153. package/dist/components/switch.js +15 -0
  154. package/dist/components/switch.js.map +1 -0
  155. package/dist/components/table.d.ts +31 -0
  156. package/dist/components/table.d.ts.map +1 -0
  157. package/dist/components/tabs.d.ts +22 -0
  158. package/dist/components/tabs.d.ts.map +1 -0
  159. package/dist/components/tabs.js +28 -0
  160. package/dist/components/tabs.js.map +1 -0
  161. package/dist/components/textarea.d.ts +8 -0
  162. package/dist/components/textarea.d.ts.map +1 -0
  163. package/dist/components/textarea.js +6 -0
  164. package/dist/components/textarea.js.map +1 -0
  165. package/dist/components/theme-provider.d.ts +35 -0
  166. package/dist/components/theme-provider.d.ts.map +1 -0
  167. package/dist/components/theme-provider.js.map +1 -0
  168. package/dist/components/theme-toggle.d.ts +10 -0
  169. package/dist/components/theme-toggle.d.ts.map +1 -0
  170. package/dist/components/theme-toggle.js.map +1 -0
  171. package/dist/components/three-state.d.ts +39 -0
  172. package/dist/components/three-state.d.ts.map +1 -0
  173. package/dist/components/three-state.js +24 -0
  174. package/dist/components/three-state.js.map +1 -0
  175. package/dist/components/timestamp.d.ts +24 -0
  176. package/dist/components/timestamp.js +64 -0
  177. package/dist/components/timestamp.js.map +1 -0
  178. package/dist/components/toaster.d.ts +27 -0
  179. package/dist/components/toaster.d.ts.map +1 -0
  180. package/dist/components/toaster.js +21 -0
  181. package/dist/components/toaster.js.map +1 -0
  182. package/dist/components/tooltip.d.ts +28 -0
  183. package/dist/components/tooltip.d.ts.map +1 -0
  184. package/dist/components/tooltip.js.map +1 -0
  185. package/dist/components/top-bar.d.ts +13 -0
  186. package/dist/components/top-bar.d.ts.map +1 -0
  187. package/dist/components/top-bar.js +16 -0
  188. package/dist/index.d.ts +76 -0
  189. package/dist/index.d.ts.map +1 -0
  190. package/dist/index.js +86 -0
  191. package/dist/index.js.map +1 -0
  192. package/dist/lib/utils.d.ts +7 -0
  193. package/dist/lib/utils.d.ts.map +1 -0
  194. package/dist/lib/utils.js +10 -0
  195. package/dist/lib/utils.js.map +1 -0
  196. package/dist/marketing/footer.d.ts +31 -0
  197. package/dist/marketing/footer.d.ts.map +1 -0
  198. package/dist/marketing/footer.js +9 -0
  199. package/dist/marketing/footer.js.map +1 -0
  200. package/dist/marketing/hero.d.ts +29 -0
  201. package/dist/marketing/hero.d.ts.map +1 -0
  202. package/dist/marketing/hero.js +7 -0
  203. package/dist/marketing/hero.js.map +1 -0
  204. package/dist/marketing/index.d.ts +21 -0
  205. package/dist/marketing/index.d.ts.map +1 -0
  206. package/dist/marketing/index.js +21 -0
  207. package/dist/marketing/index.js.map +1 -0
  208. package/dist/marketing/logo-cloud.d.ts +15 -0
  209. package/dist/marketing/logo-cloud.d.ts.map +1 -0
  210. package/dist/marketing/logo-cloud.js +6 -0
  211. package/dist/marketing/logo-cloud.js.map +1 -0
  212. package/dist/marketing/nav.d.ts +33 -0
  213. package/dist/marketing/nav.d.ts.map +1 -0
  214. package/dist/marketing/nav.js +14 -0
  215. package/dist/marketing/nav.js.map +1 -0
  216. package/dist/marketing/prose.d.ts +12 -0
  217. package/dist/marketing/prose.d.ts.map +1 -0
  218. package/dist/marketing/prose.js +18 -0
  219. package/dist/marketing/prose.js.map +1 -0
  220. package/dist/marketing/section.d.ts +22 -0
  221. package/dist/marketing/section.d.ts.map +1 -0
  222. package/dist/marketing/section.js +7 -0
  223. package/dist/marketing/section.js.map +1 -0
  224. package/dist/showcase/components/code-block.d.ts +11 -0
  225. package/dist/showcase/components/code-block.d.ts.map +1 -0
  226. package/dist/showcase/components/code-block.js +20 -0
  227. package/dist/showcase/components/code-block.js.map +1 -0
  228. package/dist/showcase/components/example.d.ts +16 -0
  229. package/dist/showcase/components/example.d.ts.map +1 -0
  230. package/dist/showcase/components/example.js +14 -0
  231. package/dist/showcase/components/example.js.map +1 -0
  232. package/dist/showcase/components/section.d.ts +21 -0
  233. package/dist/showcase/components/section.d.ts.map +1 -0
  234. package/dist/showcase/components/section.js +10 -0
  235. package/dist/showcase/components/section.js.map +1 -0
  236. package/dist/showcase/components/swatch.d.ts +17 -0
  237. package/dist/showcase/components/swatch.d.ts.map +1 -0
  238. package/dist/showcase/components/swatch.js +6 -0
  239. package/dist/showcase/components/swatch.js.map +1 -0
  240. package/dist/showcase/index.d.ts +17 -0
  241. package/dist/showcase/index.d.ts.map +1 -0
  242. package/dist/showcase/index.js +17 -0
  243. package/dist/showcase/index.js.map +1 -0
  244. package/dist/showcase/page.d.ts +2 -0
  245. package/dist/showcase/page.d.ts.map +1 -0
  246. package/dist/showcase/page.js +1085 -0
  247. package/dist/showcase/page.js.map +1 -0
  248. package/dist/styles/generated/tokens.d.ts +131 -0
  249. package/dist/styles/generated/tokens.d.ts.map +1 -0
  250. package/dist/styles/generated/tokens.js +107 -0
  251. package/dist/styles/generated/tokens.js.map +1 -0
  252. package/dist/styles/globals.css +131 -0
  253. package/dist/styles/tailwind.preset.d.ts +181 -0
  254. package/dist/styles/tailwind.preset.d.ts.map +1 -0
  255. package/dist/styles/tailwind.preset.js +114 -0
  256. package/dist/styles/tailwind.preset.js.map +1 -0
  257. package/dist/tokens/index.d.ts +176 -0
  258. package/dist/tokens/index.d.ts.map +1 -0
  259. package/dist/tokens/index.js +139 -0
  260. package/dist/tokens/index.js.map +1 -0
  261. package/dist/tokens/tokens.json +154 -0
  262. package/package.json +124 -0
  263. package/src/components/accordion.tsx +76 -0
  264. package/src/components/alert.tsx +113 -0
  265. package/src/components/app-shell.tsx +43 -0
  266. package/src/components/aspect-ratio.tsx +18 -0
  267. package/src/components/avatar-group.tsx +73 -0
  268. package/src/components/avatar.tsx +81 -0
  269. package/src/components/badge.tsx +77 -0
  270. package/src/components/breadcrumb.tsx +121 -0
  271. package/src/components/button.tsx +83 -0
  272. package/src/components/calendar.tsx +85 -0
  273. package/src/components/capacity-bar.tsx +80 -0
  274. package/src/components/card.tsx +104 -0
  275. package/src/components/checkbox.tsx +46 -0
  276. package/src/components/chip.tsx +74 -0
  277. package/src/components/collapsible.tsx +54 -0
  278. package/src/components/combobox.tsx +140 -0
  279. package/src/components/confirm-action.tsx +111 -0
  280. package/src/components/container.tsx +39 -0
  281. package/src/components/date-picker.tsx +98 -0
  282. package/src/components/dialog.tsx +174 -0
  283. package/src/components/drawer.tsx +157 -0
  284. package/src/components/dropdown-menu.tsx +200 -0
  285. package/src/components/empty-state.tsx +46 -0
  286. package/src/components/form-field.tsx +118 -0
  287. package/src/components/input.tsx +29 -0
  288. package/src/components/label.tsx +35 -0
  289. package/src/components/live-pill.tsx +35 -0
  290. package/src/components/metric-tile.tsx +83 -0
  291. package/src/components/money-breakdown.tsx +92 -0
  292. package/src/components/number-input.tsx +113 -0
  293. package/src/components/pagination.tsx +157 -0
  294. package/src/components/popover.tsx +46 -0
  295. package/src/components/progress.tsx +34 -0
  296. package/src/components/radio-group.tsx +46 -0
  297. package/src/components/scroll-area.tsx +55 -0
  298. package/src/components/select.tsx +169 -0
  299. package/src/components/separator.tsx +34 -0
  300. package/src/components/sidebar.tsx +168 -0
  301. package/src/components/skeleton.tsx +85 -0
  302. package/src/components/slider.tsx +58 -0
  303. package/src/components/spec.tsx +59 -0
  304. package/src/components/spinner.tsx +36 -0
  305. package/src/components/stack.tsx +80 -0
  306. package/src/components/status-pill.tsx +100 -0
  307. package/src/components/switch.tsx +37 -0
  308. package/src/components/table.tsx +129 -0
  309. package/src/components/tabs.tsx +72 -0
  310. package/src/components/textarea.tsx +27 -0
  311. package/src/components/theme-provider.tsx +131 -0
  312. package/src/components/theme-toggle.tsx +34 -0
  313. package/src/components/three-state.tsx +90 -0
  314. package/src/components/timestamp.tsx +107 -0
  315. package/src/components/toaster.tsx +55 -0
  316. package/src/components/tooltip.tsx +49 -0
  317. package/src/components/top-bar.tsx +29 -0
  318. package/src/index.ts +221 -0
  319. package/src/lib/utils.ts +10 -0
  320. package/src/marketing/footer.tsx +93 -0
  321. package/src/marketing/hero.tsx +92 -0
  322. package/src/marketing/index.ts +24 -0
  323. package/src/marketing/logo-cloud.tsx +35 -0
  324. package/src/marketing/nav.tsx +122 -0
  325. package/src/marketing/prose.tsx +45 -0
  326. package/src/marketing/section.tsx +77 -0
  327. package/src/showcase/components/code-block.tsx +66 -0
  328. package/src/showcase/components/example.tsx +47 -0
  329. package/src/showcase/components/section.tsx +70 -0
  330. package/src/showcase/components/swatch.tsx +52 -0
  331. package/src/showcase/index.tsx +16 -0
  332. package/src/showcase/page.tsx +4216 -0
  333. package/src/styles/generated/tokens.ts +119 -0
  334. package/src/styles/globals.css +131 -0
  335. package/src/styles/tailwind.preset.ts +134 -0
  336. package/src/test/a11y.test.tsx +638 -0
  337. package/src/test/marketing-a11y.test.tsx +138 -0
  338. package/src/test/setup.ts +47 -0
  339. package/src/tokens/index.ts +154 -0
  340. package/src/tokens/tokens.json +154 -0
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ /**
5
+ * LogoCloud — "trusted by" strip. Children are the logos (imgs or SVGs);
6
+ * give each an accessible name (alt text or aria-label).
7
+ *
8
+ * <LogoCloud label="Trusted by leagues across NYC">
9
+ * <img src="/logos/x.svg" alt="X League" className="h-6" />
10
+ * …
11
+ * </LogoCloud>
12
+ */
13
+ export interface LogoCloudProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ label?: React.ReactNode;
15
+ }
16
+
17
+ export function LogoCloud({
18
+ label,
19
+ className,
20
+ children,
21
+ ...props
22
+ }: LogoCloudProps) {
23
+ return (
24
+ <div className={cn("py-10 text-center", className)} {...props}>
25
+ {label && (
26
+ <p className="mb-6 text-xs font-semibold uppercase tracking-[0.08em] text-text-subtle">
27
+ {label}
28
+ </p>
29
+ )}
30
+ <div className="flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale">
31
+ {children}
32
+ </div>
33
+ </div>
34
+ );
35
+ }
@@ -0,0 +1,122 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Menu, X } from "lucide-react";
5
+ import { cn } from "../lib/utils";
6
+
7
+ /**
8
+ * MarketingNav — sticky marketing header: brand left, links center,
9
+ * CTA right, hamburger disclosure on mobile (44px targets, aria-expanded
10
+ * wired). Non-modal on purpose — the page stays scrollable behind it.
11
+ *
12
+ * <MarketingNav
13
+ * brand={<a href="/" aria-label="DT Sports home"><Logo /></a>}
14
+ * links={[
15
+ * { label: "Games", href: "/games" },
16
+ * { label: "Venues", href: "/venues" },
17
+ * { label: "Pricing", href: "/pricing" },
18
+ * ]}
19
+ * cta={<Button variant="accent">Find a game</Button>}
20
+ * />
21
+ *
22
+ * Render links as your router's component via `renderLink` when needed
23
+ * (defaults to a plain <a>).
24
+ */
25
+ export interface MarketingNavLink {
26
+ label: string;
27
+ href: string;
28
+ }
29
+
30
+ export interface MarketingNavProps extends React.HTMLAttributes<HTMLElement> {
31
+ brand: React.ReactNode;
32
+ links: MarketingNavLink[];
33
+ /** Right-hand CTA (keep to one accent action). */
34
+ cta?: React.ReactNode;
35
+ /** Custom link renderer, e.g. Next.js Link. */
36
+ renderLink?: (link: MarketingNavLink, className: string) => React.ReactNode;
37
+ }
38
+
39
+ const desktopLinkClass = cn(
40
+ "text-sm font-medium text-text-muted hover:text-foreground transition-colors rounded-sm px-1",
41
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
42
+ );
43
+
44
+ const mobileLinkClass = cn(
45
+ "flex items-center min-h-11 px-2 text-md font-medium text-foreground rounded-sm",
46
+ "hover:bg-bg-subtle transition-colors",
47
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
48
+ );
49
+
50
+ export function MarketingNav({
51
+ brand,
52
+ links,
53
+ cta,
54
+ renderLink,
55
+ className,
56
+ ...props
57
+ }: MarketingNavProps) {
58
+ const [open, setOpen] = React.useState(false);
59
+ const panelId = React.useId();
60
+
61
+ const link = (l: MarketingNavLink, cls: string) =>
62
+ renderLink ? (
63
+ <React.Fragment key={l.href}>{renderLink(l, cls)}</React.Fragment>
64
+ ) : (
65
+ <a key={l.href} href={l.href} className={cls}>
66
+ {l.label}
67
+ </a>
68
+ );
69
+
70
+ return (
71
+ <header
72
+ className={cn(
73
+ "sticky top-0 z-sticky border-b border-border bg-background/90 backdrop-blur",
74
+ className
75
+ )}
76
+ {...props}
77
+ >
78
+ <nav
79
+ aria-label="Main"
80
+ className="mx-auto flex h-14 max-w-6xl items-center justify-between gap-6 px-6 lg:px-8"
81
+ >
82
+ <div className="flex items-center gap-8">
83
+ {brand}
84
+ <div className="hidden md:flex items-center gap-6">
85
+ {links.map((l) => link(l, desktopLinkClass))}
86
+ </div>
87
+ </div>
88
+ <div className="flex items-center gap-3">
89
+ {cta && <div className="hidden md:block">{cta}</div>}
90
+ <button
91
+ type="button"
92
+ aria-expanded={open}
93
+ aria-controls={panelId}
94
+ aria-label={open ? "Close menu" : "Open menu"}
95
+ onClick={() => setOpen((v) => !v)}
96
+ className={cn(
97
+ "md:hidden inline-flex h-11 w-11 items-center justify-center rounded-sm text-foreground",
98
+ "hover:bg-bg-subtle transition-colors",
99
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
100
+ )}
101
+ >
102
+ {open ? (
103
+ <X className="h-5 w-5" strokeWidth={1.75} aria-hidden="true" />
104
+ ) : (
105
+ <Menu className="h-5 w-5" strokeWidth={1.75} aria-hidden="true" />
106
+ )}
107
+ </button>
108
+ </div>
109
+ </nav>
110
+ <div
111
+ id={panelId}
112
+ hidden={!open}
113
+ className="md:hidden border-t border-border bg-background px-4 py-3"
114
+ >
115
+ <div className="flex flex-col gap-1">
116
+ {links.map((l) => link(l, mobileLinkClass))}
117
+ </div>
118
+ {cta && <div className="mt-3 border-t border-border pt-3">{cta}</div>}
119
+ </div>
120
+ </header>
121
+ );
122
+ }
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ /**
5
+ * Prose — typographic wrapper for long-form marketing/blog/legal content
6
+ * (rendered markdown, CMS HTML). Styles descendant headings, paragraphs,
7
+ * lists, links, and code without requiring @tailwindcss/typography.
8
+ *
9
+ * <Prose dangerouslySetInnerHTML={{ __html: post.html }} />
10
+ * <Prose>{children}</Prose>
11
+ */
12
+ export type ProseProps = React.HTMLAttributes<HTMLDivElement>;
13
+
14
+ export function Prose({ className, ...props }: ProseProps) {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ "max-w-2xl text-md leading-relaxed text-foreground",
19
+ // Headings
20
+ "[&_h1]:text-display-sm [&_h1]:font-bold [&_h1]:mt-10 [&_h1]:mb-4",
21
+ "[&_h2]:text-2xl [&_h2]:font-semibold [&_h2]:mt-10 [&_h2]:mb-3",
22
+ "[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-8 [&_h3]:mb-2",
23
+ "[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2",
24
+ // Body
25
+ "[&_p]:my-4 [&_p]:text-text-muted",
26
+ "[&_strong]:font-semibold [&_strong]:text-foreground",
27
+ // Links — underlined (color is not the only signal)
28
+ "[&_a]:text-brand-forest [&_a]:underline [&_a]:underline-offset-4 hover:[&_a]:text-brand-forest/80",
29
+ // Lists
30
+ "[&_ul]:my-4 [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:text-text-muted",
31
+ "[&_ol]:my-4 [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:text-text-muted",
32
+ "[&_li]:my-1.5",
33
+ // Quotes, rules, code
34
+ "[&_blockquote]:my-6 [&_blockquote]:border-l-2 [&_blockquote]:border-brand-forest [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:text-text-muted",
35
+ "[&_hr]:my-8 [&_hr]:border-border",
36
+ "[&_code]:font-mono [&_code]:text-sm [&_code]:bg-bg-muted [&_code]:rounded-sm [&_code]:px-1 [&_code]:py-0.5",
37
+ "[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded [&_pre]:border [&_pre]:border-border [&_pre]:bg-bg-subtle [&_pre]:p-4 [&_pre_code]:bg-transparent [&_pre_code]:p-0",
38
+ // Media
39
+ "[&_img]:my-6 [&_img]:rounded [&_img]:border [&_img]:border-border [&_img]:max-w-full",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ );
45
+ }
@@ -0,0 +1,77 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ /**
5
+ * Section — a marketing page band with the generous spacing scale.
6
+ * Provides the id/heading wiring for skip-navigation and deep links.
7
+ *
8
+ * <Section id="how-it-works" eyebrow="How it works"
9
+ * title="Three taps to kickoff">
10
+ * …cards / feature grid…
11
+ * </Section>
12
+ */
13
+ export interface SectionProps extends Omit<
14
+ React.HTMLAttributes<HTMLElement>,
15
+ "title"
16
+ > {
17
+ id: string;
18
+ eyebrow?: React.ReactNode;
19
+ title?: React.ReactNode;
20
+ description?: React.ReactNode;
21
+ /** Center the header block (default start-aligned). */
22
+ centered?: boolean;
23
+ /** Subtle alternating background. */
24
+ tone?: "default" | "subtle";
25
+ }
26
+
27
+ export function Section({
28
+ id,
29
+ eyebrow,
30
+ title,
31
+ description,
32
+ centered = false,
33
+ tone = "default",
34
+ className,
35
+ children,
36
+ ...props
37
+ }: SectionProps) {
38
+ const headingId = title ? `${id}-title` : undefined;
39
+ return (
40
+ <section
41
+ id={id}
42
+ aria-labelledby={headingId}
43
+ className={cn(
44
+ "px-6 lg:px-8 py-20 md:py-24 scroll-mt-16",
45
+ tone === "subtle" && "bg-bg-subtle",
46
+ className
47
+ )}
48
+ {...props}
49
+ >
50
+ <div className="mx-auto max-w-5xl">
51
+ {(eyebrow || title || description) && (
52
+ <div
53
+ className={cn("mb-12 max-w-2xl", centered && "mx-auto text-center")}
54
+ >
55
+ {eyebrow && (
56
+ <p className="mb-3 text-sm font-semibold uppercase tracking-[0.08em] text-brand-forest">
57
+ {eyebrow}
58
+ </p>
59
+ )}
60
+ {title && (
61
+ <h2
62
+ id={headingId}
63
+ className="text-display-sm font-bold text-foreground text-balance"
64
+ >
65
+ {title}
66
+ </h2>
67
+ )}
68
+ {description && (
69
+ <p className="mt-4 text-lg text-text-muted">{description}</p>
70
+ )}
71
+ </div>
72
+ )}
73
+ {children}
74
+ </div>
75
+ </section>
76
+ );
77
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Check, Copy } from "lucide-react";
5
+ import { cn } from "../../lib/utils";
6
+
7
+ /**
8
+ * Code block with copy-to-clipboard. No syntax highlighting (intentional —
9
+ * the design system stays decoration-free; we can layer Shiki later if needed).
10
+ */
11
+ export interface CodeBlockProps {
12
+ code: string;
13
+ language?: string;
14
+ className?: string;
15
+ }
16
+
17
+ export function CodeBlock({ code, language, className }: CodeBlockProps) {
18
+ const [copied, setCopied] = React.useState(false);
19
+
20
+ const onCopy = async () => {
21
+ try {
22
+ await navigator.clipboard.writeText(code);
23
+ setCopied(true);
24
+ setTimeout(() => setCopied(false), 1500);
25
+ } catch {
26
+ /* ignore */
27
+ }
28
+ };
29
+
30
+ return (
31
+ <div
32
+ className={cn(
33
+ "relative rounded border border-border bg-bg-subtle",
34
+ className
35
+ )}
36
+ >
37
+ {language && (
38
+ <div className="flex items-center justify-between border-b border-border px-3 py-1.5">
39
+ <span className="text-[10px] font-medium uppercase tracking-[0.04em] text-text-muted">
40
+ {language}
41
+ </span>
42
+ </div>
43
+ )}
44
+ <button
45
+ type="button"
46
+ onClick={onCopy}
47
+ aria-label={copied ? "Copied" : "Copy code"}
48
+ className={cn(
49
+ "absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded-sm text-text-muted",
50
+ "transition-colors hover:bg-bg-muted hover:text-foreground",
51
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
52
+ language && "top-7"
53
+ )}
54
+ >
55
+ {copied ? (
56
+ <Check className="h-3.5 w-3.5" strokeWidth={2} />
57
+ ) : (
58
+ <Copy className="h-3.5 w-3.5" strokeWidth={1.75} />
59
+ )}
60
+ </button>
61
+ <pre className="overflow-x-auto scrollbar-thin px-3 py-2.5 text-xs leading-relaxed">
62
+ <code className="font-mono text-foreground">{code}</code>
63
+ </pre>
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ /**
5
+ * Example renders a labeled preview + code block side-by-side (or stacked
6
+ * on narrow screens). Used throughout the design system docs.
7
+ */
8
+ export function Example({
9
+ label,
10
+ children,
11
+ className,
12
+ }: {
13
+ label?: string;
14
+ children: React.ReactNode;
15
+ className?: string;
16
+ }) {
17
+ return (
18
+ <div className={cn("space-y-2", className)}>
19
+ {label && (
20
+ <div className="text-xs font-medium uppercase tracking-[0.04em] text-text-muted">
21
+ {label}
22
+ </div>
23
+ )}
24
+ <div className="rounded border border-border bg-background p-5">
25
+ {children}
26
+ </div>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ /** Two-column grid: preview on the left, code on the right. */
32
+ export function PreviewCode({
33
+ preview,
34
+ code,
35
+ }: {
36
+ preview: React.ReactNode;
37
+ code: React.ReactNode;
38
+ }) {
39
+ return (
40
+ <div className="grid gap-3 md:grid-cols-2">
41
+ <div className="rounded border border-border bg-background p-5 flex items-center justify-center min-h-[120px]">
42
+ {preview}
43
+ </div>
44
+ <div>{code}</div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,70 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ /**
5
+ * Section wrapper for the design system docs.
6
+ * Renders an id-anchored heading with description and content body.
7
+ */
8
+ export interface DocSectionProps {
9
+ id: string;
10
+ title: string;
11
+ description?: string;
12
+ children: React.ReactNode;
13
+ className?: string;
14
+ }
15
+
16
+ export function DocSection({
17
+ id,
18
+ title,
19
+ description,
20
+ children,
21
+ className,
22
+ }: DocSectionProps) {
23
+ return (
24
+ <section
25
+ id={id}
26
+ aria-labelledby={`${id}-title`}
27
+ className={cn("scroll-mt-16 py-10 border-t border-border", className)}
28
+ >
29
+ <div className="mb-6">
30
+ <h2
31
+ id={`${id}-title`}
32
+ className="text-xl font-semibold tracking-[-0.01em] text-foreground"
33
+ >
34
+ <a href={`#${id}`} className="hover:text-brand-forest no-underline">
35
+ {title}
36
+ </a>
37
+ </h2>
38
+ {description && (
39
+ <p className="mt-1.5 text-sm text-text-muted max-w-2xl">
40
+ {description}
41
+ </p>
42
+ )}
43
+ </div>
44
+ <div className="space-y-6">{children}</div>
45
+ </section>
46
+ );
47
+ }
48
+
49
+ /** Subsection within a DocSection. */
50
+ export function DocSubsection({
51
+ title,
52
+ description,
53
+ children,
54
+ className,
55
+ }: {
56
+ title: string;
57
+ description?: string;
58
+ children: React.ReactNode;
59
+ className?: string;
60
+ }) {
61
+ return (
62
+ <div className={cn("", className)}>
63
+ <h3 className="text-md font-semibold mb-1.5">{title}</h3>
64
+ {description && (
65
+ <p className="text-sm text-text-muted mb-3 max-w-2xl">{description}</p>
66
+ )}
67
+ {children}
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,52 @@
1
+ import { cn } from "../../lib/utils";
2
+
3
+ /**
4
+ * Color swatch used in the design system docs.
5
+ * Renders a color tile with token name + value below.
6
+ */
7
+ export interface SwatchProps {
8
+ token: string;
9
+ /** Tailwind class for the background (e.g. "bg-brand-lime") */
10
+ bg: string;
11
+ /** Color value to display (e.g. "#B4D334" or "hsl(72 65% 52%)") */
12
+ value: string;
13
+ /** Optional: text color sample to show on top of the swatch */
14
+ textSample?: "white" | "black" | "none";
15
+ /** Optional: contrast ratio note */
16
+ contrast?: string;
17
+ }
18
+
19
+ export function Swatch({
20
+ token,
21
+ bg,
22
+ value,
23
+ textSample = "none",
24
+ contrast,
25
+ }: SwatchProps) {
26
+ return (
27
+ <div className="flex flex-col">
28
+ <div
29
+ className={cn(
30
+ "h-16 rounded border border-border flex items-center justify-center",
31
+ bg
32
+ )}
33
+ >
34
+ {textSample !== "none" && (
35
+ <span
36
+ className={cn(
37
+ "text-sm font-medium",
38
+ textSample === "white" ? "text-white" : "text-black"
39
+ )}
40
+ >
41
+ Aa
42
+ </span>
43
+ )}
44
+ </div>
45
+ <div className="mt-1.5 text-xs">
46
+ <div className="font-medium text-foreground">{token}</div>
47
+ <div className="text-text-muted font-mono tabular">{value}</div>
48
+ {contrast && <div className="text-text-subtle mt-0.5">{contrast}</div>}
49
+ </div>
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Mountable design-system documentation page.
3
+ *
4
+ * Default export is the page component itself, so consumers can wire it
5
+ * up as a Next.js page in one line:
6
+ *
7
+ * // apps/<your-app>/app/design-system/page.tsx
8
+ * export { default } from "@dtsports/design-system/showcase";
9
+ *
10
+ * Or used as a regular component anywhere:
11
+ *
12
+ * import { DesignSystemPage } from "@dtsports/design-system/showcase";
13
+ * <DesignSystemPage />
14
+ */
15
+ export { default } from "./page";
16
+ export { default as DesignSystemPage } from "./page";