@gv-tech/design-system 1.1.0 → 1.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 (332) hide show
  1. package/.agent/skills/dogfood-components/SKILL.md +34 -0
  2. package/.agent/skills/maintain-component/SKILL.md +42 -0
  3. package/.prettierignore +2 -0
  4. package/CHANGELOG.md +16 -0
  5. package/dist/App.d.ts.map +1 -1
  6. package/dist/components/docs/Footer.d.ts.map +1 -1
  7. package/dist/components/docs/PropsTable.d.ts +13 -0
  8. package/dist/components/docs/PropsTable.d.ts.map +1 -0
  9. package/dist/components/docs/Sidebar.d.ts.map +1 -1
  10. package/dist/components/docs/index.d.ts +1 -0
  11. package/dist/components/docs/index.d.ts.map +1 -1
  12. package/dist/components/ui/accordion.test.d.ts +2 -0
  13. package/dist/components/ui/accordion.test.d.ts.map +1 -0
  14. package/dist/components/ui/alert-dialog.test.d.ts +2 -0
  15. package/dist/components/ui/alert-dialog.test.d.ts.map +1 -0
  16. package/dist/components/ui/alert.test.d.ts +2 -0
  17. package/dist/components/ui/alert.test.d.ts.map +1 -0
  18. package/dist/components/ui/aspect-ratio.test.d.ts +2 -0
  19. package/dist/components/ui/aspect-ratio.test.d.ts.map +1 -0
  20. package/dist/components/ui/avatar.test.d.ts +2 -0
  21. package/dist/components/ui/avatar.test.d.ts.map +1 -0
  22. package/dist/components/ui/badge.test.d.ts +2 -0
  23. package/dist/components/ui/badge.test.d.ts.map +1 -0
  24. package/dist/components/ui/breadcrumb.test.d.ts +2 -0
  25. package/dist/components/ui/breadcrumb.test.d.ts.map +1 -0
  26. package/dist/components/ui/button.test.d.ts +2 -0
  27. package/dist/components/ui/button.test.d.ts.map +1 -0
  28. package/dist/components/ui/calendar.d.ts.map +1 -1
  29. package/dist/components/ui/calendar.test.d.ts +2 -0
  30. package/dist/components/ui/calendar.test.d.ts.map +1 -0
  31. package/dist/components/ui/card.test.d.ts +2 -0
  32. package/dist/components/ui/card.test.d.ts.map +1 -0
  33. package/dist/components/ui/carousel.test.d.ts +2 -0
  34. package/dist/components/ui/carousel.test.d.ts.map +1 -0
  35. package/dist/components/ui/chart.test.d.ts +2 -0
  36. package/dist/components/ui/chart.test.d.ts.map +1 -0
  37. package/dist/components/ui/checkbox.test.d.ts +2 -0
  38. package/dist/components/ui/checkbox.test.d.ts.map +1 -0
  39. package/dist/components/ui/collapsible.test.d.ts +2 -0
  40. package/dist/components/ui/collapsible.test.d.ts.map +1 -0
  41. package/dist/components/ui/command.test.d.ts +2 -0
  42. package/dist/components/ui/command.test.d.ts.map +1 -0
  43. package/dist/components/ui/context-menu.test.d.ts +2 -0
  44. package/dist/components/ui/context-menu.test.d.ts.map +1 -0
  45. package/dist/components/ui/dialog.test.d.ts +2 -0
  46. package/dist/components/ui/dialog.test.d.ts.map +1 -0
  47. package/dist/components/ui/drawer.test.d.ts +2 -0
  48. package/dist/components/ui/drawer.test.d.ts.map +1 -0
  49. package/dist/components/ui/dropdown-menu.test.d.ts +2 -0
  50. package/dist/components/ui/dropdown-menu.test.d.ts.map +1 -0
  51. package/dist/components/ui/form.test.d.ts +2 -0
  52. package/dist/components/ui/form.test.d.ts.map +1 -0
  53. package/dist/components/ui/hover-card.test.d.ts +2 -0
  54. package/dist/components/ui/hover-card.test.d.ts.map +1 -0
  55. package/dist/components/ui/input.test.d.ts +2 -0
  56. package/dist/components/ui/input.test.d.ts.map +1 -0
  57. package/dist/components/ui/label.test.d.ts +2 -0
  58. package/dist/components/ui/label.test.d.ts.map +1 -0
  59. package/dist/components/ui/menubar.test.d.ts +2 -0
  60. package/dist/components/ui/menubar.test.d.ts.map +1 -0
  61. package/dist/components/ui/navigation-menu.test.d.ts +2 -0
  62. package/dist/components/ui/navigation-menu.test.d.ts.map +1 -0
  63. package/dist/components/ui/pagination.test.d.ts +2 -0
  64. package/dist/components/ui/pagination.test.d.ts.map +1 -0
  65. package/dist/components/ui/popover.test.d.ts +2 -0
  66. package/dist/components/ui/popover.test.d.ts.map +1 -0
  67. package/dist/components/ui/progress.d.ts.map +1 -1
  68. package/dist/components/ui/progress.test.d.ts +2 -0
  69. package/dist/components/ui/progress.test.d.ts.map +1 -0
  70. package/dist/components/ui/radio-group.test.d.ts +2 -0
  71. package/dist/components/ui/radio-group.test.d.ts.map +1 -0
  72. package/dist/components/ui/resizable.test.d.ts +2 -0
  73. package/dist/components/ui/resizable.test.d.ts.map +1 -0
  74. package/dist/components/ui/scroll-area.test.d.ts +2 -0
  75. package/dist/components/ui/scroll-area.test.d.ts.map +1 -0
  76. package/dist/components/ui/select.test.d.ts +2 -0
  77. package/dist/components/ui/select.test.d.ts.map +1 -0
  78. package/dist/components/ui/separator.test.d.ts +2 -0
  79. package/dist/components/ui/separator.test.d.ts.map +1 -0
  80. package/dist/components/ui/sheet.test.d.ts +2 -0
  81. package/dist/components/ui/sheet.test.d.ts.map +1 -0
  82. package/dist/components/ui/skeleton.test.d.ts +2 -0
  83. package/dist/components/ui/skeleton.test.d.ts.map +1 -0
  84. package/dist/components/ui/slider.test.d.ts +2 -0
  85. package/dist/components/ui/slider.test.d.ts.map +1 -0
  86. package/dist/components/ui/sonner.test.d.ts +2 -0
  87. package/dist/components/ui/sonner.test.d.ts.map +1 -0
  88. package/dist/components/ui/switch.test.d.ts +2 -0
  89. package/dist/components/ui/switch.test.d.ts.map +1 -0
  90. package/dist/components/ui/table.test.d.ts +2 -0
  91. package/dist/components/ui/table.test.d.ts.map +1 -0
  92. package/dist/components/ui/tabs.test.d.ts +2 -0
  93. package/dist/components/ui/tabs.test.d.ts.map +1 -0
  94. package/dist/components/ui/textarea.test.d.ts +2 -0
  95. package/dist/components/ui/textarea.test.d.ts.map +1 -0
  96. package/dist/components/ui/theme-toggle.d.ts +17 -0
  97. package/dist/components/ui/theme-toggle.d.ts.map +1 -0
  98. package/dist/components/ui/toast.test.d.ts +2 -0
  99. package/dist/components/ui/toast.test.d.ts.map +1 -0
  100. package/dist/components/ui/toggle-group.test.d.ts +2 -0
  101. package/dist/components/ui/toggle-group.test.d.ts.map +1 -0
  102. package/dist/components/ui/toggle.test.d.ts +2 -0
  103. package/dist/components/ui/toggle.test.d.ts.map +1 -0
  104. package/dist/components/ui/tooltip.test.d.ts +2 -0
  105. package/dist/components/ui/tooltip.test.d.ts.map +1 -0
  106. package/dist/index.cjs.js +3 -3
  107. package/dist/index.cjs.js.map +1 -1
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.es.js +978 -860
  111. package/dist/index.es.js.map +1 -1
  112. package/dist/lib/tokens.d.ts +54 -0
  113. package/dist/lib/tokens.d.ts.map +1 -0
  114. package/dist/pages/ColorTokensDocs.d.ts +2 -0
  115. package/dist/pages/ColorTokensDocs.d.ts.map +1 -0
  116. package/dist/pages/GettingStarted.d.ts.map +1 -1
  117. package/dist/pages/components/AccordionDocs.d.ts.map +1 -1
  118. package/dist/pages/components/AlertDialogDocs.d.ts.map +1 -1
  119. package/dist/pages/components/AlertDocs.d.ts.map +1 -1
  120. package/dist/pages/components/AspectRatioDocs.d.ts.map +1 -1
  121. package/dist/pages/components/AvatarDocs.d.ts.map +1 -1
  122. package/dist/pages/components/BadgeDocs.d.ts.map +1 -1
  123. package/dist/pages/components/BreadcrumbDocs.d.ts.map +1 -1
  124. package/dist/pages/components/ButtonDocs.d.ts.map +1 -1
  125. package/dist/pages/components/CalendarDocs.d.ts.map +1 -1
  126. package/dist/pages/components/CardDocs.d.ts.map +1 -1
  127. package/dist/pages/components/CarouselDocs.d.ts.map +1 -1
  128. package/dist/pages/components/ChartDocs.d.ts.map +1 -1
  129. package/dist/pages/components/CheckboxDocs.d.ts.map +1 -1
  130. package/dist/pages/components/CollapsibleDocs.d.ts.map +1 -1
  131. package/dist/pages/components/CommandDocs.d.ts.map +1 -1
  132. package/dist/pages/components/ContextMenuDocs.d.ts.map +1 -1
  133. package/dist/pages/components/DialogDocs.d.ts.map +1 -1
  134. package/dist/pages/components/DrawerDocs.d.ts.map +1 -1
  135. package/dist/pages/components/DropdownMenuDocs.d.ts.map +1 -1
  136. package/dist/pages/components/FormDocs.d.ts.map +1 -1
  137. package/dist/pages/components/HoverCardDocs.d.ts.map +1 -1
  138. package/dist/pages/components/InputDocs.d.ts.map +1 -1
  139. package/dist/pages/components/LabelDocs.d.ts.map +1 -1
  140. package/dist/pages/components/MenubarDocs.d.ts.map +1 -1
  141. package/dist/pages/components/NavigationMenuDocs.d.ts.map +1 -1
  142. package/dist/pages/components/PaginationDocs.d.ts.map +1 -1
  143. package/dist/pages/components/PopoverDocs.d.ts.map +1 -1
  144. package/dist/pages/components/ProgressDocs.d.ts.map +1 -1
  145. package/dist/pages/components/RadioGroupDocs.d.ts.map +1 -1
  146. package/dist/pages/components/ResizableDocs.d.ts.map +1 -1
  147. package/dist/pages/components/ScrollAreaDocs.d.ts.map +1 -1
  148. package/dist/pages/components/SelectDocs.d.ts.map +1 -1
  149. package/dist/pages/components/SeparatorDocs.d.ts.map +1 -1
  150. package/dist/pages/components/SheetDocs.d.ts.map +1 -1
  151. package/dist/pages/components/SkeletonDocs.d.ts.map +1 -1
  152. package/dist/pages/components/SliderDocs.d.ts.map +1 -1
  153. package/dist/pages/components/SonnerDocs.d.ts.map +1 -1
  154. package/dist/pages/components/SwitchDocs.d.ts.map +1 -1
  155. package/dist/pages/components/TableDocs.d.ts.map +1 -1
  156. package/dist/pages/components/TabsDocs.d.ts.map +1 -1
  157. package/dist/pages/components/TextareaDocs.d.ts.map +1 -1
  158. package/dist/pages/components/ThemeToggleDocs.d.ts +2 -0
  159. package/dist/pages/components/ThemeToggleDocs.d.ts.map +1 -0
  160. package/dist/pages/components/ToastDocs.d.ts.map +1 -1
  161. package/dist/pages/components/ToggleDocs.d.ts.map +1 -1
  162. package/dist/pages/components/ToggleGroupDocs.d.ts.map +1 -1
  163. package/dist/pages/components/TooltipDocs.d.ts.map +1 -1
  164. package/dist/pages/index.d.ts +2 -0
  165. package/dist/pages/index.d.ts.map +1 -1
  166. package/dist/registry/accordion.test.json +13 -0
  167. package/dist/registry/alert-dialog.test.json +13 -0
  168. package/dist/registry/alert.test.json +13 -0
  169. package/dist/registry/aspect-ratio.test.json +13 -0
  170. package/dist/registry/avatar.test.json +13 -0
  171. package/dist/registry/badge.test.json +13 -0
  172. package/dist/registry/breadcrumb.test.json +13 -0
  173. package/dist/registry/button.test.json +13 -0
  174. package/dist/registry/calendar.json +1 -1
  175. package/dist/registry/calendar.test.json +13 -0
  176. package/dist/registry/card.test.json +13 -0
  177. package/dist/registry/carousel.test.json +13 -0
  178. package/dist/registry/chart.test.json +13 -0
  179. package/dist/registry/checkbox.test.json +13 -0
  180. package/dist/registry/collapsible.test.json +13 -0
  181. package/dist/registry/command.test.json +13 -0
  182. package/dist/registry/context-menu.test.json +13 -0
  183. package/dist/registry/dialog.test.json +13 -0
  184. package/dist/registry/drawer.test.json +13 -0
  185. package/dist/registry/dropdown-menu.test.json +13 -0
  186. package/dist/registry/form.test.json +13 -0
  187. package/dist/registry/hover-card.test.json +13 -0
  188. package/dist/registry/index.json +322 -0
  189. package/dist/registry/input.test.json +13 -0
  190. package/dist/registry/label.test.json +13 -0
  191. package/dist/registry/menubar.test.json +13 -0
  192. package/dist/registry/navigation-menu.test.json +13 -0
  193. package/dist/registry/pagination.test.json +13 -0
  194. package/dist/registry/popover.test.json +13 -0
  195. package/dist/registry/progress.json +1 -1
  196. package/dist/registry/progress.test.json +13 -0
  197. package/dist/registry/radio-group.test.json +13 -0
  198. package/dist/registry/resizable.test.json +13 -0
  199. package/dist/registry/scroll-area.test.json +13 -0
  200. package/dist/registry/select.test.json +13 -0
  201. package/dist/registry/separator.test.json +13 -0
  202. package/dist/registry/sheet.test.json +13 -0
  203. package/dist/registry/skeleton.test.json +13 -0
  204. package/dist/registry/slider.test.json +13 -0
  205. package/dist/registry/sonner.test.json +13 -0
  206. package/dist/registry/switch.test.json +13 -0
  207. package/dist/registry/table.test.json +13 -0
  208. package/dist/registry/tabs.test.json +13 -0
  209. package/dist/registry/textarea.test.json +13 -0
  210. package/dist/registry/theme-toggle.json +13 -0
  211. package/dist/registry/toast.test.json +13 -0
  212. package/dist/registry/toggle-group.test.json +13 -0
  213. package/dist/registry/toggle.test.json +13 -0
  214. package/dist/registry/tooltip.test.json +13 -0
  215. package/dist/setupTests.d.ts +2 -0
  216. package/dist/setupTests.d.ts.map +1 -0
  217. package/dist/{vendor-ZhQmrf1h.mjs → vendor-CAF5bxO5.mjs} +2451 -2415
  218. package/dist/vendor-CAF5bxO5.mjs.map +1 -0
  219. package/dist/{vendor-CMSUBoIg.js → vendor-Hw1BQGd3.js} +17 -17
  220. package/dist/vendor-Hw1BQGd3.js.map +1 -0
  221. package/eslint.config.mjs +8 -81
  222. package/package.json +36 -38
  223. package/src/App.tsx +37 -7
  224. package/src/components/docs/Footer.tsx +51 -30
  225. package/src/components/docs/PropsTable.tsx +43 -0
  226. package/src/components/docs/Sidebar.tsx +42 -71
  227. package/src/components/docs/index.ts +1 -0
  228. package/src/components/ui/accordion.test.tsx +86 -0
  229. package/src/components/ui/alert-dialog.test.tsx +89 -0
  230. package/src/components/ui/alert.test.tsx +33 -0
  231. package/src/components/ui/aspect-ratio.test.tsx +34 -0
  232. package/src/components/ui/avatar.test.tsx +33 -0
  233. package/src/components/ui/badge.test.tsx +24 -0
  234. package/src/components/ui/breadcrumb.test.tsx +55 -0
  235. package/src/components/ui/button.test.tsx +62 -0
  236. package/src/components/ui/calendar.test.tsx +23 -0
  237. package/src/components/ui/calendar.tsx +14 -10
  238. package/src/components/ui/card.test.tsx +35 -0
  239. package/src/components/ui/carousel.test.tsx +37 -0
  240. package/src/components/ui/chart.test.tsx +62 -0
  241. package/src/components/ui/checkbox.test.tsx +30 -0
  242. package/src/components/ui/collapsible.test.tsx +51 -0
  243. package/src/components/ui/command.test.tsx +79 -0
  244. package/src/components/ui/context-menu.test.tsx +37 -0
  245. package/src/components/ui/dialog.test.tsx +66 -0
  246. package/src/components/ui/drawer.test.tsx +68 -0
  247. package/src/components/ui/dropdown-menu.test.tsx +93 -0
  248. package/src/components/ui/form.test.tsx +85 -0
  249. package/src/components/ui/hover-card.test.tsx +48 -0
  250. package/src/components/ui/input.test.tsx +33 -0
  251. package/src/components/ui/label.test.tsx +27 -0
  252. package/src/components/ui/menubar.test.tsx +92 -0
  253. package/src/components/ui/navigation-menu.test.tsx +53 -0
  254. package/src/components/ui/pagination.test.tsx +57 -0
  255. package/src/components/ui/popover.test.tsx +31 -0
  256. package/src/components/ui/progress.test.tsx +18 -0
  257. package/src/components/ui/progress.tsx +1 -0
  258. package/src/components/ui/radio-group.test.tsx +39 -0
  259. package/src/components/ui/resizable.test.tsx +23 -0
  260. package/src/components/ui/scroll-area.test.tsx +15 -0
  261. package/src/components/ui/select.test.tsx +42 -0
  262. package/src/components/ui/separator.test.tsx +16 -0
  263. package/src/components/ui/sheet.test.tsx +48 -0
  264. package/src/components/ui/skeleton.test.tsx +13 -0
  265. package/src/components/ui/slider.test.tsx +18 -0
  266. package/src/components/ui/sonner.test.tsx +13 -0
  267. package/src/components/ui/switch.test.tsx +22 -0
  268. package/src/components/ui/table.test.tsx +29 -0
  269. package/src/components/ui/tabs.test.tsx +43 -0
  270. package/src/components/ui/textarea.test.tsx +21 -0
  271. package/src/components/ui/theme-toggle.tsx +108 -0
  272. package/src/components/ui/toast.test.tsx +42 -0
  273. package/src/components/ui/toggle-group.test.tsx +40 -0
  274. package/src/components/ui/toggle.test.tsx +21 -0
  275. package/src/components/ui/tooltip.test.tsx +25 -0
  276. package/src/globals.css +39 -34
  277. package/src/index.ts +2 -0
  278. package/src/lib/tokens.ts +54 -0
  279. package/src/pages/ColorTokensDocs.tsx +181 -0
  280. package/src/pages/GettingStarted.tsx +55 -35
  281. package/src/pages/components/AccordionDocs.tsx +109 -0
  282. package/src/pages/components/AlertDialogDocs.tsx +88 -0
  283. package/src/pages/components/AlertDocs.tsx +20 -0
  284. package/src/pages/components/AspectRatioDocs.tsx +21 -0
  285. package/src/pages/components/AvatarDocs.tsx +48 -0
  286. package/src/pages/components/BadgeDocs.tsx +20 -0
  287. package/src/pages/components/BreadcrumbDocs.tsx +33 -0
  288. package/src/pages/components/ButtonDocs.tsx +43 -0
  289. package/src/pages/components/CalendarDocs.tsx +43 -0
  290. package/src/pages/components/CardDocs.tsx +20 -0
  291. package/src/pages/components/CarouselDocs.tsx +31 -0
  292. package/src/pages/components/ChartDocs.tsx +131 -101
  293. package/src/pages/components/CheckboxDocs.tsx +58 -0
  294. package/src/pages/components/CollapsibleDocs.tsx +51 -0
  295. package/src/pages/components/CommandDocs.tsx +109 -0
  296. package/src/pages/components/ContextMenuDocs.tsx +65 -0
  297. package/src/pages/components/DialogDocs.tsx +98 -11
  298. package/src/pages/components/DrawerDocs.tsx +210 -15
  299. package/src/pages/components/DropdownMenuDocs.tsx +273 -11
  300. package/src/pages/components/FormDocs.tsx +149 -70
  301. package/src/pages/components/HoverCardDocs.tsx +82 -5
  302. package/src/pages/components/InputDocs.tsx +51 -20
  303. package/src/pages/components/LabelDocs.tsx +40 -9
  304. package/src/pages/components/MenubarDocs.tsx +191 -18
  305. package/src/pages/components/NavigationMenuDocs.tsx +147 -49
  306. package/src/pages/components/PaginationDocs.tsx +27 -2
  307. package/src/pages/components/PopoverDocs.tsx +124 -2
  308. package/src/pages/components/ProgressDocs.tsx +54 -24
  309. package/src/pages/components/RadioGroupDocs.tsx +95 -1
  310. package/src/pages/components/ResizableDocs.tsx +102 -75
  311. package/src/pages/components/ScrollAreaDocs.tsx +64 -51
  312. package/src/pages/components/SelectDocs.tsx +119 -48
  313. package/src/pages/components/SeparatorDocs.tsx +37 -2
  314. package/src/pages/components/SheetDocs.tsx +112 -38
  315. package/src/pages/components/SkeletonDocs.tsx +16 -20
  316. package/src/pages/components/SliderDocs.tsx +96 -10
  317. package/src/pages/components/SonnerDocs.tsx +89 -61
  318. package/src/pages/components/SwitchDocs.tsx +65 -10
  319. package/src/pages/components/TableDocs.tsx +89 -14
  320. package/src/pages/components/TabsDocs.tsx +149 -37
  321. package/src/pages/components/TextareaDocs.tsx +38 -32
  322. package/src/pages/components/ThemeToggleDocs.tsx +50 -0
  323. package/src/pages/components/ToastDocs.tsx +104 -65
  324. package/src/pages/components/ToggleDocs.tsx +55 -38
  325. package/src/pages/components/ToggleGroupDocs.tsx +96 -58
  326. package/src/pages/components/TooltipDocs.tsx +112 -3
  327. package/src/pages/index.ts +2 -0
  328. package/src/setupTests.ts +47 -0
  329. package/temp.md +292 -0
  330. package/vitest.config.ts +4 -0
  331. package/dist/vendor-CMSUBoIg.js.map +0 -1
  332. package/dist/vendor-ZhQmrf1h.mjs.map +0 -1
@@ -0,0 +1,108 @@
1
+ import { Button } from '@/components/ui/button';
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuTrigger,
7
+ } from '@/components/ui/dropdown-menu';
8
+ import { cn } from '@/lib/utils';
9
+ import { Moon, Sun, SunMoon } from 'lucide-react';
10
+ import { useTheme } from 'next-themes';
11
+
12
+ export interface ThemeToggleProps {
13
+ /**
14
+ * The mode of the theme toggle. 'binary' allows toggling between light and dark. 'ternary' allows choosing between
15
+ * light, dark, and system.
16
+ *
17
+ * @default 'binary'
18
+ */
19
+ variant?: 'binary' | 'ternary';
20
+ /** Optional callback when the theme changes. */
21
+ onThemeChange?: (theme: string) => void;
22
+ /** Optional current theme value for external control. */
23
+ customTheme?: string;
24
+ /** Optional className for the button. */
25
+ className?: string;
26
+ }
27
+
28
+ export function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, className }: ThemeToggleProps) {
29
+ const { theme: nextTheme, setTheme: setNextTheme, resolvedTheme } = useTheme();
30
+
31
+ // Use customTheme if provided, otherwise fallback to next-themes
32
+ const currentTheme = customTheme ?? nextTheme;
33
+
34
+ // Determine the effective theme for icon rendering
35
+ const effectiveTheme = customTheme ? customTheme : resolvedTheme;
36
+ const isDark = effectiveTheme === 'dark';
37
+ const isSystem = currentTheme === 'system';
38
+
39
+ const handleThemeChange = (newTheme: string) => {
40
+ if (onThemeChange) {
41
+ onThemeChange(newTheme);
42
+ } else {
43
+ setNextTheme(newTheme);
44
+ }
45
+ };
46
+
47
+ const IconToggle = () => (
48
+ <>
49
+ <Sun
50
+ className={cn(
51
+ 'h-[1.2rem] w-[1.2rem] transition-all',
52
+ !isSystem && !isDark ? 'rotate-0 scale-100' : '-rotate-90 scale-0',
53
+ )}
54
+ />
55
+ <Moon
56
+ className={cn(
57
+ 'absolute h-[1.2rem] w-[1.2rem] transition-all',
58
+ !isSystem && isDark ? 'rotate-0 scale-100' : 'rotate-90 scale-0',
59
+ )}
60
+ />
61
+ <SunMoon
62
+ className={cn(
63
+ 'absolute h-[1.2rem] w-[1.2rem] transition-all',
64
+ isSystem ? 'rotate-0 scale-100' : 'rotate-90 scale-0',
65
+ )}
66
+ />
67
+ <span className="sr-only">Toggle theme</span>
68
+ </>
69
+ );
70
+
71
+ if (variant === 'ternary') {
72
+ return (
73
+ <DropdownMenu>
74
+ <DropdownMenuTrigger asChild>
75
+ <Button variant="ghost" size="icon" className={cn('h-9 w-9', className)}>
76
+ <IconToggle />
77
+ </Button>
78
+ </DropdownMenuTrigger>
79
+ <DropdownMenuContent align="end">
80
+ <DropdownMenuItem onClick={() => handleThemeChange('light')}>
81
+ <Sun className="mr-2 h-4 w-4" />
82
+ <span>Light</span>
83
+ </DropdownMenuItem>
84
+ <DropdownMenuItem onClick={() => handleThemeChange('dark')}>
85
+ <Moon className="mr-2 h-4 w-4" />
86
+ <span>Dark</span>
87
+ </DropdownMenuItem>
88
+ <DropdownMenuItem onClick={() => handleThemeChange('system')}>
89
+ <SunMoon className="mr-2 h-4 w-4" />
90
+ <span>System</span>
91
+ </DropdownMenuItem>
92
+ </DropdownMenuContent>
93
+ </DropdownMenu>
94
+ );
95
+ }
96
+
97
+ return (
98
+ <Button
99
+ variant="ghost"
100
+ size="icon"
101
+ className={cn('h-9 w-9', className)}
102
+ onClick={() => handleThemeChange(currentTheme === 'dark' ? 'light' : 'dark')}
103
+ aria-label="Toggle theme"
104
+ >
105
+ <IconToggle />
106
+ </Button>
107
+ );
108
+ }
@@ -0,0 +1,42 @@
1
+ import { useToast } from '@/hooks/use-toast';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { Toaster } from './toaster';
6
+
7
+ // Test component to trigger toasts
8
+ const ToastTrigger = () => {
9
+ const { toast } = useToast();
10
+ return (
11
+ <button
12
+ onClick={() =>
13
+ toast({
14
+ title: 'Test Toast',
15
+ description: 'This is a test message',
16
+ })
17
+ }
18
+ >
19
+ Show Toast
20
+ </button>
21
+ );
22
+ };
23
+
24
+ describe('Toast', () => {
25
+ it('shows toast when triggered', async () => {
26
+ const user = userEvent.setup();
27
+ render(
28
+ <>
29
+ <Toaster />
30
+ <ToastTrigger />
31
+ </>,
32
+ );
33
+
34
+ expect(screen.queryByText('Test Toast')).not.toBeInTheDocument();
35
+
36
+ await user.click(screen.getByText('Show Toast'));
37
+
38
+ // Toast might take a moment to appear (animation)
39
+ expect(await screen.findByText('Test Toast')).toBeInTheDocument();
40
+ expect(screen.getByText('This is a test message')).toBeInTheDocument();
41
+ });
42
+ });
@@ -0,0 +1,40 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { ToggleGroup, ToggleGroupItem } from './toggle-group';
5
+
6
+ describe('ToggleGroup', () => {
7
+ it('renders correctly', () => {
8
+ render(
9
+ <ToggleGroup type="single">
10
+ <ToggleGroupItem value="a" aria-label="Toggle A">
11
+ A
12
+ </ToggleGroupItem>
13
+ <ToggleGroupItem value="b" aria-label="Toggle B">
14
+ B
15
+ </ToggleGroupItem>
16
+ </ToggleGroup>,
17
+ );
18
+
19
+ expect(screen.getByLabelText('Toggle A')).toBeInTheDocument();
20
+ expect(screen.getByLabelText('Toggle B')).toBeInTheDocument();
21
+ });
22
+
23
+ it('toggles selection', async () => {
24
+ const user = userEvent.setup();
25
+ render(
26
+ <ToggleGroup type="single">
27
+ <ToggleGroupItem value="a" aria-label="Toggle A">
28
+ A
29
+ </ToggleGroupItem>
30
+ <ToggleGroupItem value="b" aria-label="Toggle B">
31
+ B
32
+ </ToggleGroupItem>
33
+ </ToggleGroup>,
34
+ );
35
+
36
+ const toggleA = screen.getByLabelText('Toggle A');
37
+ await user.click(toggleA);
38
+ expect(toggleA).toHaveAttribute('aria-checked', 'true');
39
+ });
40
+ });
@@ -0,0 +1,21 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { Toggle } from './toggle';
5
+
6
+ describe('Toggle', () => {
7
+ it('renders correctly', () => {
8
+ render(<Toggle aria-label="Toggle bold">B</Toggle>);
9
+ expect(screen.getByLabelText('Toggle bold')).toBeInTheDocument();
10
+ });
11
+
12
+ it('toggles state', async () => {
13
+ const user = userEvent.setup();
14
+ render(<Toggle aria-label="Toggle italic" />);
15
+ const toggle = screen.getByLabelText('Toggle italic');
16
+
17
+ expect(toggle).toHaveAttribute('aria-pressed', 'false');
18
+ await user.click(toggle);
19
+ expect(toggle).toHaveAttribute('aria-pressed', 'true');
20
+ });
21
+ });
@@ -0,0 +1,25 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip';
5
+
6
+ describe('Tooltip', () => {
7
+ it('shows content on hover', async () => {
8
+ const user = userEvent.setup();
9
+ render(
10
+ <TooltipProvider>
11
+ <Tooltip>
12
+ <TooltipTrigger>Hover me</TooltipTrigger>
13
+ <TooltipContent>Tooltip content</TooltipContent>
14
+ </Tooltip>
15
+ </TooltipProvider>,
16
+ );
17
+
18
+ expect(screen.queryByText('Tooltip content')).not.toBeInTheDocument();
19
+
20
+ await user.hover(screen.getByText('Hover me'));
21
+ const tooltip = await screen.findByRole('tooltip');
22
+ expect(tooltip).toBeInTheDocument();
23
+ expect(tooltip).toHaveTextContent('Tooltip content');
24
+ });
25
+ });
package/src/globals.css CHANGED
@@ -34,47 +34,52 @@
34
34
 
35
35
  @layer base {
36
36
  :root {
37
- --background: 0 0% 100%;
38
- --foreground: 222.2 84% 4.9%;
37
+ --background: 0 0% 96%;
38
+ --foreground: 222 47% 11%;
39
39
  --card: 0 0% 100%;
40
- --card-foreground: 222.2 84% 4.9%;
40
+ --card-foreground: 222 47% 11%;
41
41
  --popover: 0 0% 100%;
42
- --popover-foreground: 222.2 84% 4.9%;
43
- --primary: 222.2 47.4% 11.2%;
44
- --primary-foreground: 210 40% 98%;
45
- --secondary: 210 40% 96.1%;
46
- --secondary-foreground: 222.2 47.4% 11.2%;
47
- --muted: 210 40% 96.1%;
48
- --muted-foreground: 215.4 16.3% 46.9%;
49
- --accent: 210 40% 96.1%;
50
- --accent-foreground: 222.2 47.4% 11.2%;
42
+ --popover-foreground: 222 47% 11%;
43
+ --primary: 225 73% 57%;
44
+ --primary-foreground: 0 0% 100%;
45
+ --secondary: 93 28% 54%;
46
+ --secondary-foreground: 0 0% 100%;
47
+ --muted: 0 0% 92%;
48
+ --muted-foreground: 215 16% 47%;
49
+ --accent: 0 0% 88%;
50
+ --accent-foreground: 222 47% 11%;
51
51
  --destructive: 0 84.2% 60.2%;
52
- --destructive-foreground: 210 40% 98%;
53
- --border: 214.3 31.8% 91.4%;
54
- --input: 214.3 31.8% 91.4%;
55
- --ring: 222.2 84% 4.9%;
52
+ --destructive-foreground: 0 0% 100%;
53
+ --border: 0 0% 89%;
54
+ --input: 0 0% 89%;
55
+ --ring: 222 47% 11%;
56
+ --radius: 0.5rem;
57
+
58
+ /* Brand Tokens */
59
+ --brand-green: 151 66% 27%;
60
+ --brand-blue: 225 73% 57%;
56
61
  }
57
62
 
58
63
  .dark {
59
- --background: 222.2 84% 4.9%;
60
- --foreground: 210 40% 98%;
61
- --card: 222.2 84% 4.9%;
62
- --card-foreground: 210 40% 98%;
63
- --popover: 222.2 84% 4.9%;
64
- --popover-foreground: 210 40% 98%;
65
- --primary: 210 40% 98%;
66
- --primary-foreground: 222.2 47.4% 11.2%;
67
- --secondary: 217.2 32.6% 17.5%;
68
- --secondary-foreground: 210 40% 98%;
69
- --muted: 217.2 32.6% 17.5%;
70
- --muted-foreground: 215 20.2% 65.1%;
71
- --accent: 217.2 32.6% 17.5%;
72
- --accent-foreground: 210 40% 98%;
64
+ --background: 0 0% 9%;
65
+ --foreground: 0 0% 100%;
66
+ --card: 0 0% 14%;
67
+ --card-foreground: 0 0% 100%;
68
+ --popover: 0 0% 11%;
69
+ --popover-foreground: 0 0% 100%;
70
+ --primary: 227 96% 71%;
71
+ --primary-foreground: 0 0% 9%;
72
+ --secondary: 96 44% 61%;
73
+ --secondary-foreground: 0 0% 9%;
74
+ --muted: 0 0% 6%;
75
+ --muted-foreground: 0 0% 70%;
76
+ --accent: 0 0% 15%;
77
+ --accent-foreground: 0 0% 100%;
73
78
  --destructive: 0 62.8% 30.6%;
74
- --destructive-foreground: 210 40% 98%;
75
- --border: 217.2 32.6% 17.5%;
76
- --input: 217.2 32.6% 17.5%;
77
- --ring: 212.7 26.8% 83.9%;
79
+ --destructive-foreground: 0 0% 100%;
80
+ --border: 0 0% 18%;
81
+ --input: 0 0% 18%;
82
+ --ring: 0 0% 90%;
78
83
  }
79
84
  }
80
85
 
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './lib/tokens';
1
2
  export * from './lib/utils';
2
3
 
3
4
  // UI Components
@@ -42,6 +43,7 @@ export * from './components/ui/switch';
42
43
  export * from './components/ui/table';
43
44
  export * from './components/ui/tabs';
44
45
  export * from './components/ui/textarea';
46
+ export * from './components/ui/theme-toggle';
45
47
  export * from './components/ui/toast';
46
48
  export { Toaster as ToasterToast } from './components/ui/toaster';
47
49
  export * from './components/ui/toggle';
@@ -0,0 +1,54 @@
1
+ /** Design system color tokens for Garcia Ventures. */
2
+ export const tokens = {
3
+ colors: {
4
+ light: {
5
+ background: 'hsl(0 0% 96%)',
6
+ foreground: 'hsl(222 47% 11%)',
7
+ card: 'hsl(0 0% 100%)',
8
+ cardForeground: 'hsl(222 47% 11%)',
9
+ popover: 'hsl(0 0% 100%)',
10
+ popoverForeground: 'hsl(222 47% 11%)',
11
+ primary: 'hsl(225 73% 57%)',
12
+ primaryForeground: 'hsl(0 0% 100%)',
13
+ secondary: 'hsl(93 28% 54%)',
14
+ secondaryForeground: 'hsl(0 0% 100%)',
15
+ muted: 'hsl(0 0% 92%)',
16
+ mutedForeground: 'hsl(215 16% 47%)',
17
+ accent: 'hsl(0 0% 88%)',
18
+ accentForeground: 'hsl(222 47% 11%)',
19
+ destructive: 'hsl(0 84.2% 60.2%)',
20
+ destructiveForeground: 'hsl(0 0% 100%)',
21
+ border: 'hsl(0 0% 89%)',
22
+ input: 'hsl(0 0% 89%)',
23
+ ring: 'hsl(222 47% 11%)',
24
+ },
25
+ dark: {
26
+ background: 'hsl(0 0% 9%)',
27
+ foreground: 'hsl(0 0% 100%)',
28
+ card: 'hsl(0 0% 14%)',
29
+ cardForeground: 'hsl(0 0% 100%)',
30
+ popover: 'hsl(0 0% 11%)',
31
+ popoverForeground: 'hsl(0 0% 100%)',
32
+ primary: 'hsl(227 96% 71%)',
33
+ primaryForeground: 'hsl(0 0% 9%)',
34
+ secondary: 'hsl(96 44% 61%)',
35
+ secondaryForeground: 'hsl(0 0% 9%)',
36
+ muted: 'hsl(0 0% 6%)',
37
+ mutedForeground: 'hsl(0 0% 70%)',
38
+ accent: 'hsl(0 0% 15%)',
39
+ accentForeground: 'hsl(0 0% 100%)',
40
+ destructive: 'hsl(0 62.8% 30.6%)',
41
+ destructiveForeground: 'hsl(0 0% 100%)',
42
+ border: 'hsl(0 0% 18%)',
43
+ input: 'hsl(0 0% 18%)',
44
+ ring: 'hsl(0 0% 90%)',
45
+ },
46
+ brand: {
47
+ green: 'hsl(151 66% 27%)',
48
+ blue: 'hsl(225 73% 57%)',
49
+ },
50
+ },
51
+ } as const;
52
+
53
+ export type ThemeTokens = typeof tokens.colors.light;
54
+ export type BrandTokens = typeof tokens.colors.brand;
@@ -0,0 +1,181 @@
1
+ import { CodeBlock } from '@/components/docs/CodeBlock';
2
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
4
+ import { tokens } from '@/lib/tokens';
5
+
6
+ export function ColorTokensDocs() {
7
+ return (
8
+ <div className="space-y-8">
9
+ <div className="space-y-2">
10
+ <h1 className="text-3xl font-bold tracking-tight">Color Tokens</h1>
11
+ <p className="text-lg text-muted-foreground">
12
+ A comprehensive token library for consistent branding across Garcia Ventures applications.
13
+ </p>
14
+ </div>
15
+
16
+ <section className="space-y-4">
17
+ <h2 className="text-2xl font-semibold tracking-tight">The Color Theme</h2>
18
+ <p className="text-muted-foreground">
19
+ Our theme is designed to signal stability, intellect, and transparency. It follows a consistent hierarchy
20
+ across light and dark modes to ensure accessibility and professional aesthetics.
21
+ </p>
22
+
23
+ <div className="grid gap-6 md:grid-cols-2">
24
+ <Card>
25
+ <CardHeader>
26
+ <CardTitle className="text-sm font-medium">Core Palette</CardTitle>
27
+ </CardHeader>
28
+ <CardContent className="grid gap-4">
29
+ <div className="flex items-center gap-4">
30
+ <div className="h-10 w-10 rounded-md border" style={{ backgroundColor: 'hsl(225 73% 57%)' }} />
31
+ <div>
32
+ <p className="text-sm font-medium">Primary (Blue)</p>
33
+ <p className="text-xs text-muted-foreground">Royal Blue / Cornflower</p>
34
+ </div>
35
+ </div>
36
+ <div className="flex items-center gap-4">
37
+ <div className="h-10 w-10 rounded-md border" style={{ backgroundColor: 'hsl(93 28% 54%)' }} />
38
+ <div>
39
+ <p className="text-sm font-medium">Secondary (Green)</p>
40
+ <p className="text-xs text-muted-foreground">Asparagus / Pistachio</p>
41
+ </div>
42
+ </div>
43
+ <div className="flex items-center gap-4">
44
+ <div className="h-10 w-10 rounded-md border" style={{ backgroundColor: 'hsl(210 29% 98%)' }} />
45
+ <div>
46
+ <p className="text-sm font-medium">Neutral (Off-White)</p>
47
+ <p className="text-xs text-muted-foreground">Light Neutral / Floral White</p>
48
+ </div>
49
+ </div>
50
+ </CardContent>
51
+ </Card>
52
+
53
+ <Card>
54
+ <CardHeader>
55
+ <CardTitle className="text-sm font-medium">Brand Specifics</CardTitle>
56
+ </CardHeader>
57
+ <CardContent className="grid gap-4">
58
+ <div className="flex items-center gap-4">
59
+ <div className="h-10 w-10 rounded-md border" style={{ backgroundColor: 'hsl(151 66% 27%)' }} />
60
+ <div>
61
+ <p className="text-sm font-medium">Brand Green</p>
62
+ <p className="text-xs text-muted-foreground">#177245 (Stability)</p>
63
+ </div>
64
+ </div>
65
+ <div className="flex items-center gap-4">
66
+ <div className="h-10 w-10 rounded-md border" style={{ backgroundColor: 'hsl(225 73% 57%)' }} />
67
+ <div>
68
+ <p className="text-sm font-medium">Brand Blue</p>
69
+ <p className="text-xs text-muted-foreground">#4169e1 (Intellect)</p>
70
+ </div>
71
+ </div>
72
+ </CardContent>
73
+ </Card>
74
+ </div>
75
+ </section>
76
+
77
+ <section className="space-y-4">
78
+ <h2 className="text-2xl font-semibold tracking-tight">Usage: Tailwind CSS</h2>
79
+ <p className="text-muted-foreground">
80
+ All color tokens are mapped to standard Tailwind colors via CSS variables in our design system.
81
+ </p>
82
+ <CodeBlock
83
+ language="tsx"
84
+ code={`<div className="bg-primary text-primary-foreground">
85
+ Primary Content
86
+ </div>
87
+ <div className="text-secondary">
88
+ Secondary Accent
89
+ </div>
90
+ <div className="border-border bg-muted/30">
91
+ Muted Container
92
+ </div>`}
93
+ />
94
+ </section>
95
+
96
+ <section className="space-y-4">
97
+ <h2 className="text-2xl font-semibold tracking-tight">Usage: TypeScript Tokens</h2>
98
+ <p className="text-muted-foreground">
99
+ For programmatic usage or non-Tailwind applications, you can import the raw token values.
100
+ </p>
101
+ <CodeBlock
102
+ language="tsx"
103
+ code={`import { tokens } from '@gv-tech/design-system';
104
+
105
+ // Accessing HSL values
106
+ const primary = tokens.colors.light.primary;
107
+ const brandGreen = tokens.colors.brand.green;`}
108
+ />
109
+ </section>
110
+
111
+ <section className="space-y-4">
112
+ <h2 className="text-2xl font-semibold tracking-tight">CSS Variables Reference</h2>
113
+ <p className="text-muted-foreground">
114
+ Below is a complete reference of all semantic color tokens available in the design system.
115
+ </p>
116
+ <div className="rounded-md border overflow-hidden">
117
+ <Table>
118
+ <TableHeader>
119
+ <TableRow className="bg-muted/50 hover:bg-muted/50">
120
+ <TableHead>Variable</TableHead>
121
+ <TableHead>Light Mode</TableHead>
122
+ <TableHead>Dark Mode</TableHead>
123
+ </TableRow>
124
+ </TableHeader>
125
+ <TableBody>
126
+ {(Object.keys(tokens.colors.light) as Array<keyof typeof tokens.colors.light>).map((key) => (
127
+ <TableRow key={key}>
128
+ <TableCell className="font-mono text-xs">
129
+ --{key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}
130
+ </TableCell>
131
+ <TableCell>
132
+ <div className="flex items-center gap-2">
133
+ <div
134
+ className="h-4 w-4 rounded-sm border"
135
+ style={{ backgroundColor: tokens.colors.light[key] }}
136
+ />
137
+ <span className="text-muted-foreground">{tokens.colors.light[key]}</span>
138
+ </div>
139
+ </TableCell>
140
+ <TableCell>
141
+ <div className="flex items-center gap-2">
142
+ <div className="h-4 w-4 rounded-sm border" style={{ backgroundColor: tokens.colors.dark[key] }} />
143
+ <span className="text-muted-foreground">{tokens.colors.dark[key]}</span>
144
+ </div>
145
+ </TableCell>
146
+ </TableRow>
147
+ ))}
148
+ </TableBody>
149
+ </Table>
150
+ </div>
151
+ </section>
152
+
153
+ <section className="space-y-4">
154
+ <h2 className="text-2xl font-semibold tracking-tight">Brand Colors</h2>
155
+ <div className="rounded-md border overflow-hidden">
156
+ <Table>
157
+ <TableHeader>
158
+ <TableRow className="bg-muted/50 hover:bg-muted/50">
159
+ <TableHead>Token</TableHead>
160
+ <TableHead>Value</TableHead>
161
+ </TableRow>
162
+ </TableHeader>
163
+ <TableBody>
164
+ {Object.entries(tokens.colors.brand).map(([key, value]) => (
165
+ <TableRow key={key}>
166
+ <TableCell className="font-mono text-xs">tokens.colors.brand.{key}</TableCell>
167
+ <TableCell>
168
+ <div className="flex items-center gap-2">
169
+ <div className="h-4 w-4 rounded-sm border" style={{ backgroundColor: value }} />
170
+ <span className="text-muted-foreground">{value}</span>
171
+ </div>
172
+ </TableCell>
173
+ </TableRow>
174
+ ))}
175
+ </TableBody>
176
+ </Table>
177
+ </div>
178
+ </section>
179
+ </div>
180
+ );
181
+ }