@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,138 @@
1
+ /**
2
+ * Accessibility suite for the marketing tier
3
+ * (@dtsports/design-system/marketing). Same rule config rationale as
4
+ * a11y.test.tsx.
5
+ */
6
+ import { render, screen } from "@testing-library/react";
7
+ import userEvent from "@testing-library/user-event";
8
+ import { axe } from "jest-axe";
9
+ import { describe, expect, it } from "vitest";
10
+
11
+ import { Button } from "../index";
12
+ import {
13
+ Footer,
14
+ Hero,
15
+ LogoCloud,
16
+ MarketingNav,
17
+ Prose,
18
+ Section,
19
+ } from "../marketing";
20
+
21
+ const AXE_RULES = {
22
+ rules: {
23
+ "color-contrast": { enabled: false },
24
+ region: { enabled: false },
25
+ "page-has-heading-one": { enabled: false },
26
+ "heading-order": { enabled: false },
27
+ "landmark-one-main": { enabled: false },
28
+ },
29
+ } as const;
30
+
31
+ describe("marketing tier", () => {
32
+ it("full page composition", async () => {
33
+ const { baseElement } = render(
34
+ <>
35
+ <MarketingNav
36
+ brand={
37
+ <a href="/" aria-label="DT Sports home">
38
+ <span className="font-bold">DT Sports</span>
39
+ </a>
40
+ }
41
+ links={[
42
+ { label: "Games", href: "/games" },
43
+ { label: "Venues", href: "/venues" },
44
+ { label: "Pricing", href: "/pricing" },
45
+ ]}
46
+ cta={<Button variant="accent">Find a game</Button>}
47
+ />
48
+ <Hero
49
+ eyebrow="Pickup soccer, organized"
50
+ title="Book a game in 30 seconds"
51
+ description="Real fields, fair teams, instant refunds."
52
+ actions={
53
+ <>
54
+ <Button variant="accent" size="touch">
55
+ Find a game
56
+ </Button>
57
+ <Button variant="secondary" size="touch">
58
+ Host with us
59
+ </Button>
60
+ </>
61
+ }
62
+ />
63
+ <LogoCloud label="Trusted by leagues across NYC">
64
+ <span aria-label="Astoria League">AL</span>
65
+ <span aria-label="McCarren FC">MFC</span>
66
+ </LogoCloud>
67
+ <Section
68
+ id="how-it-works"
69
+ eyebrow="How it works"
70
+ title="Three taps to kickoff"
71
+ description="Browse, book, play."
72
+ >
73
+ <p>Feature grid goes here.</p>
74
+ </Section>
75
+ <Footer
76
+ brand={<span className="font-bold">DT Sports</span>}
77
+ tagline="Pickup sports, organized."
78
+ columns={[
79
+ {
80
+ title: "Product",
81
+ links: [
82
+ { label: "Games", href: "/games" },
83
+ { label: "Venues", href: "/venues" },
84
+ ],
85
+ },
86
+ {
87
+ title: "Company",
88
+ links: [{ label: "About", href: "/about" }],
89
+ },
90
+ ]}
91
+ legal="© 2026 DT Sports"
92
+ />
93
+ </>
94
+ );
95
+ expect(await axe(baseElement, AXE_RULES)).toHaveNoViolations();
96
+ });
97
+
98
+ it("mobile menu toggle is wired and accessible when open", async () => {
99
+ const user = userEvent.setup();
100
+ const { baseElement } = render(
101
+ <MarketingNav
102
+ brand={<span className="font-bold">DT Sports</span>}
103
+ links={[{ label: "Games", href: "/games" }]}
104
+ cta={<Button variant="accent">Find a game</Button>}
105
+ />
106
+ );
107
+ const toggle = screen.getByRole("button", { name: "Open menu" });
108
+ expect(toggle).toHaveAttribute("aria-expanded", "false");
109
+ await user.click(toggle);
110
+ expect(screen.getByRole("button", { name: "Close menu" })).toHaveAttribute(
111
+ "aria-expanded",
112
+ "true"
113
+ );
114
+ // Panel content is reachable while open.
115
+ expect(
116
+ screen.getAllByRole("link", { name: "Games" }).length
117
+ ).toBeGreaterThan(0);
118
+ expect(await axe(baseElement, AXE_RULES)).toHaveNoViolations();
119
+ });
120
+
121
+ it("Prose long-form content", async () => {
122
+ const { baseElement } = render(
123
+ <Prose>
124
+ <h2>Refund policy</h2>
125
+ <p>
126
+ Cancel up to 2 hours before kickoff for a{" "}
127
+ <a href="/credit">full credit</a>.
128
+ </p>
129
+ <ul>
130
+ <li>Weather cancellations refund automatically.</li>
131
+ <li>Host cancellations refund to the original card.</li>
132
+ </ul>
133
+ <blockquote>No surprise debits, ever.</blockquote>
134
+ </Prose>
135
+ );
136
+ expect(await axe(baseElement, AXE_RULES)).toHaveNoViolations();
137
+ });
138
+ });
@@ -0,0 +1,47 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { cleanup } from "@testing-library/react";
3
+ import { toHaveNoViolations } from "jest-axe";
4
+ import { afterEach, expect, vi } from "vitest";
5
+
6
+ expect.extend(toHaveNoViolations);
7
+
8
+ // RTL auto-cleanup needs vitest globals; we run without them, so clean
9
+ // up explicitly — axe scans document.body and would see leaked renders.
10
+ afterEach(cleanup);
11
+
12
+ // jsdom lacks these browser APIs that Radix and ThemeProvider rely on.
13
+ if (!window.matchMedia) {
14
+ window.matchMedia = vi.fn().mockImplementation((query: string) => ({
15
+ matches: false,
16
+ media: query,
17
+ onchange: null,
18
+ addListener: vi.fn(),
19
+ removeListener: vi.fn(),
20
+ addEventListener: vi.fn(),
21
+ removeEventListener: vi.fn(),
22
+ dispatchEvent: vi.fn(),
23
+ }));
24
+ }
25
+
26
+ if (!window.ResizeObserver) {
27
+ window.ResizeObserver = class ResizeObserver {
28
+ observe() {}
29
+ unobserve() {}
30
+ disconnect() {}
31
+ };
32
+ }
33
+
34
+ if (!Element.prototype.scrollIntoView) {
35
+ Element.prototype.scrollIntoView = vi.fn();
36
+ }
37
+
38
+ // Radix Select/DropdownMenu call these pointer-capture APIs.
39
+ if (!Element.prototype.hasPointerCapture) {
40
+ Element.prototype.hasPointerCapture = vi.fn().mockReturnValue(false);
41
+ }
42
+ if (!Element.prototype.setPointerCapture) {
43
+ Element.prototype.setPointerCapture = vi.fn();
44
+ }
45
+ if (!Element.prototype.releasePointerCapture) {
46
+ Element.prototype.releasePointerCapture = vi.fn();
47
+ }
@@ -0,0 +1,154 @@
1
+ // GENERATED by scripts/build-tokens.mjs from src/tokens/tokens.json — do not edit (platform-agnostic tokens for React Native / plain JS)
2
+
3
+ /** Resolved color palettes. Values are CSS hsl() strings (RN-compatible). */
4
+ export const palette = {
5
+ light: {
6
+ "brand-lime": "hsl(72 65% 52%)",
7
+ "brand-lime-foreground": "hsl(0 0% 4%)",
8
+ "brand-forest": "hsl(147 66% 26%)",
9
+ "brand-forest-foreground": "hsl(0 0% 100%)",
10
+ "background": "hsl(0 0% 100%)",
11
+ "bg-subtle": "hsl(0 0% 98%)",
12
+ "bg-muted": "hsl(240 5% 96%)",
13
+ "border": "hsl(0 0% 90%)",
14
+ "border-strong": "hsl(0 0% 83%)",
15
+ "foreground": "hsl(0 0% 4%)",
16
+ "text-muted": "hsl(0 0% 32%)",
17
+ "text-subtle": "hsl(0 0% 45%)",
18
+ "success": "hsl(142 72% 29%)",
19
+ "success-foreground": "hsl(0 0% 100%)",
20
+ "warning": "hsl(35 90% 33%)",
21
+ "warning-foreground": "hsl(0 0% 100%)",
22
+ "danger": "hsl(0 72% 49%)",
23
+ "danger-foreground": "hsl(0 0% 100%)",
24
+ "info": "hsl(217 91% 50%)",
25
+ "info-foreground": "hsl(0 0% 100%)",
26
+ "ring": "hsl(147 66% 26%)",
27
+ },
28
+ dark: {
29
+ "brand-lime": "hsl(72 65% 52%)",
30
+ "brand-lime-foreground": "hsl(0 0% 4%)",
31
+ "brand-forest": "hsl(147 66% 26%)",
32
+ "brand-forest-foreground": "hsl(0 0% 100%)",
33
+ "background": "hsl(0 0% 4%)",
34
+ "bg-subtle": "hsl(0 0% 8%)",
35
+ "bg-muted": "hsl(0 0% 12%)",
36
+ "border": "hsl(0 0% 15%)",
37
+ "border-strong": "hsl(240 5% 26%)",
38
+ "foreground": "hsl(0 0% 98%)",
39
+ "text-muted": "hsl(240 5% 65%)",
40
+ "text-subtle": "hsl(240 4% 52%)",
41
+ "success": "hsl(142 60% 45%)",
42
+ "success-foreground": "hsl(0 0% 4%)",
43
+ "warning": "hsl(38 92% 50%)",
44
+ "warning-foreground": "hsl(0 0% 4%)",
45
+ "danger": "hsl(0 84% 62%)",
46
+ "danger-foreground": "hsl(0 0% 4%)",
47
+ "info": "hsl(217 91% 62%)",
48
+ "info-foreground": "hsl(0 0% 4%)",
49
+ "ring": "hsl(72 65% 52%)",
50
+ },
51
+ } as const;
52
+
53
+ export type ThemeName = keyof typeof palette;
54
+ export type ColorToken = keyof typeof palette.light;
55
+
56
+ /** Spacing scale in device pixels. */
57
+ export const spacing = {
58
+ "1": 4,
59
+ "2": 8,
60
+ "3": 12,
61
+ "4": 16,
62
+ "5": 20,
63
+ "6": 24,
64
+ "8": 32,
65
+ "10": 40,
66
+ "12": 48,
67
+ "16": 64,
68
+ "20": 80,
69
+ "24": 96,
70
+ "32": 128,
71
+ "40": 160,
72
+ "0.5": 2,
73
+ "1.5": 6,
74
+ "2.5": 10,
75
+ } as const;
76
+
77
+ /** Type scale in device pixels. */
78
+ export const fontSize = {
79
+ "xs": { size: 11, lineHeight: 16 },
80
+ "sm": { size: 13, lineHeight: 18 },
81
+ "base": { size: 14, lineHeight: 20 },
82
+ "md": { size: 15, lineHeight: 22 },
83
+ "lg": { size: 17, lineHeight: 24 },
84
+ "xl": { size: 20, lineHeight: 28 },
85
+ "2xl": { size: 24, lineHeight: 32 },
86
+ "3xl": { size: 30, lineHeight: 36 },
87
+ } as const;
88
+
89
+ export const fontWeight = {
90
+ normal: "400",
91
+ medium: "500",
92
+ semibold: "600",
93
+ bold: "700",
94
+ } as const;
95
+
96
+ /** Marketing display scale — RN gets the min/max bounds (no vw units). */
97
+ export const fontSizeFluid = {
98
+ "display-sm": { min: 32, max: 44, lineHeight: 1.1 },
99
+ "display": { min: 40, max: 64, lineHeight: 1.05 },
100
+ "display-lg": { min: 48, max: 96, lineHeight: 1 },
101
+ } as const;
102
+
103
+ /** CSS gradient strings (web); RN needs its own gradient impl. */
104
+ export const gradient = {
105
+ "brand": "linear-gradient(135deg, hsl(var(--brand-forest)) 0%, hsl(100 55% 38%) 55%, hsl(var(--brand-lime)) 100%)",
106
+ "forest-fade": "linear-gradient(180deg, hsl(var(--brand-forest) / 0.08) 0%, transparent 100%)",
107
+ } as const;
108
+
109
+ export const fontFamily = {
110
+ sans: ['Exo 2', 'system-ui', 'sans-serif'],
111
+ mono: ['ui-monospace', 'SFMono-Regular', 'JetBrains Mono', 'Menlo', 'monospace'],
112
+ } as const;
113
+
114
+ /** Corner radii in device pixels. */
115
+ export const radius = {
116
+ "sm": 2,
117
+ base: 4,
118
+ "md": 6,
119
+ } as const;
120
+
121
+ /** CSS box-shadow strings — map to RN elevation/shadow* as needed. */
122
+ export const shadow = {
123
+ sm: "0 1px 2px rgba(0,0,0,0.04), 0 1px 1px rgba(0,0,0,0.04)",
124
+ md: "0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06)",
125
+ lg: "0 12px 32px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08)",
126
+ } as const;
127
+
128
+ /** Durations in ms. */
129
+ export const duration = {
130
+ base: 150,
131
+ "slow": 200,
132
+ "pulse": 1500,
133
+ } as const;
134
+
135
+ export const easing = {
136
+ out: "cubic-bezier(0.2, 0, 0, 1)",
137
+ } as const;
138
+
139
+ export const zIndex = {
140
+ sticky: 20,
141
+ overlay: 40,
142
+ modal: 50,
143
+ popover: 50,
144
+ toast: 60,
145
+ } as const;
146
+
147
+ /** Breakpoints in px (web); useful for RN tablet/phone splits too. */
148
+ export const breakpoint = {
149
+ "sm": 640,
150
+ "md": 768,
151
+ "lg": 1024,
152
+ "xl": 1280,
153
+ "2xl": 1536,
154
+ } as const;
@@ -0,0 +1,154 @@
1
+ {
2
+ "$description": "DT Sports design tokens — single source of truth. Edit this file, then run `npm run build:tokens`. Never edit the generated blocks in globals.css, src/styles/generated/, or src/tokens/index.ts by hand.",
3
+ "color": {
4
+ "$format": "HSL channel triplets (\"H S% L%\") so CSS can compose hsl(var(--x) / alpha).",
5
+ "light": {
6
+ "brand-lime": "72 65% 52%",
7
+ "brand-lime-foreground": "0 0% 4%",
8
+ "brand-forest": "147 66% 26%",
9
+ "brand-forest-foreground": "0 0% 100%",
10
+ "background": "0 0% 100%",
11
+ "bg-subtle": "0 0% 98%",
12
+ "bg-muted": "240 5% 96%",
13
+ "border": "0 0% 90%",
14
+ "border-strong": "0 0% 83%",
15
+ "foreground": "0 0% 4%",
16
+ "text-muted": "0 0% 32%",
17
+ "text-subtle": "0 0% 45%",
18
+ "success": "142 72% 29%",
19
+ "success-foreground": "0 0% 100%",
20
+ "warning": "35 90% 33%",
21
+ "warning-foreground": "0 0% 100%",
22
+ "danger": "0 72% 49%",
23
+ "danger-foreground": "0 0% 100%",
24
+ "info": "217 91% 50%",
25
+ "info-foreground": "0 0% 100%",
26
+ "ring": "147 66% 26%"
27
+ },
28
+ "dark": {
29
+ "$comment": "Overrides only — anything not listed inherits the light value.",
30
+ "background": "0 0% 4%",
31
+ "bg-subtle": "0 0% 8%",
32
+ "bg-muted": "0 0% 12%",
33
+ "border": "0 0% 15%",
34
+ "border-strong": "240 5% 26%",
35
+ "foreground": "0 0% 98%",
36
+ "text-muted": "240 5% 65%",
37
+ "text-subtle": "240 4% 52%",
38
+ "success": "142 60% 45%",
39
+ "success-foreground": "0 0% 4%",
40
+ "warning": "38 92% 50%",
41
+ "warning-foreground": "0 0% 4%",
42
+ "danger": "0 84% 62%",
43
+ "danger-foreground": "0 0% 4%",
44
+ "info": "217 91% 62%",
45
+ "info-foreground": "0 0% 4%",
46
+ "ring": "72 65% 52%"
47
+ }
48
+ },
49
+ "fontFamily": {
50
+ "sans": ["\"Exo 2\"", "system-ui", "sans-serif"],
51
+ "mono": [
52
+ "ui-monospace",
53
+ "SFMono-Regular",
54
+ "\"JetBrains Mono\"",
55
+ "Menlo",
56
+ "monospace"
57
+ ]
58
+ },
59
+ "fontSize": {
60
+ "xs": { "size": 11, "lineHeight": 16 },
61
+ "sm": { "size": 13, "lineHeight": 18 },
62
+ "base": { "size": 14, "lineHeight": 20 },
63
+ "md": { "size": 15, "lineHeight": 22 },
64
+ "lg": { "size": 17, "lineHeight": 24 },
65
+ "xl": { "size": 20, "lineHeight": 28 },
66
+ "2xl": { "size": 24, "lineHeight": 32 },
67
+ "3xl": { "size": 30, "lineHeight": 36 }
68
+ },
69
+ "fontSizeFluid": {
70
+ "$comment": "Marketing display scale — clamp(minPx, basePx + vw, maxPx). Web gets fluid clamp(); React Native gets {min,max}.",
71
+ "display-sm": {
72
+ "min": 32,
73
+ "max": 44,
74
+ "base": 24,
75
+ "vw": 2.5,
76
+ "lineHeight": 1.1,
77
+ "letterSpacing": "-0.02em"
78
+ },
79
+ "display": {
80
+ "min": 40,
81
+ "max": 64,
82
+ "base": 26,
83
+ "vw": 4,
84
+ "lineHeight": 1.05,
85
+ "letterSpacing": "-0.02em"
86
+ },
87
+ "display-lg": {
88
+ "min": 48,
89
+ "max": 96,
90
+ "base": 28,
91
+ "vw": 6,
92
+ "lineHeight": 1,
93
+ "letterSpacing": "-0.025em"
94
+ }
95
+ },
96
+ "fontWeight": {
97
+ "normal": 400,
98
+ "medium": 500,
99
+ "semibold": 600,
100
+ "bold": 700
101
+ },
102
+ "spacing": {
103
+ "0.5": 2,
104
+ "1": 4,
105
+ "1.5": 6,
106
+ "2": 8,
107
+ "2.5": 10,
108
+ "3": 12,
109
+ "4": 16,
110
+ "5": 20,
111
+ "6": 24,
112
+ "8": 32,
113
+ "10": 40,
114
+ "12": 48,
115
+ "16": 64,
116
+ "20": 80,
117
+ "24": 96,
118
+ "32": 128,
119
+ "40": 160
120
+ },
121
+ "radius": {
122
+ "sm": 2,
123
+ "DEFAULT": 4,
124
+ "md": 6
125
+ },
126
+ "shadow": {
127
+ "sm": "0 1px 2px rgba(0,0,0,0.04), 0 1px 1px rgba(0,0,0,0.04)",
128
+ "md": "0 4px 12px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06)",
129
+ "lg": "0 12px 32px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08)"
130
+ },
131
+ "gradient": {
132
+ "brand": "linear-gradient(135deg, hsl(var(--brand-forest)) 0%, hsl(100 55% 38%) 55%, hsl(var(--brand-lime)) 100%)",
133
+ "forest-fade": "linear-gradient(180deg, hsl(var(--brand-forest) / 0.08) 0%, transparent 100%)"
134
+ },
135
+ "motion": {
136
+ "duration": { "DEFAULT": 150, "slow": 200, "pulse": 1500 },
137
+ "easing": { "out": "cubic-bezier(0.2, 0, 0, 1)" }
138
+ },
139
+ "zIndex": {
140
+ "sticky": 20,
141
+ "overlay": 40,
142
+ "modal": 50,
143
+ "popover": 50,
144
+ "toast": 60
145
+ },
146
+ "breakpoint": {
147
+ "$comment": "Matches Tailwind defaults; declared so RN and marketing share them.",
148
+ "sm": 640,
149
+ "md": 768,
150
+ "lg": 1024,
151
+ "xl": 1280,
152
+ "2xl": 1536
153
+ }
154
+ }