@codefast/ui 0.3.16-canary.2 → 0.4.0-canary.4

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 (289) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +28 -17
  3. package/dist/components/accordion.d.mts +7 -22
  4. package/dist/components/accordion.mjs +26 -29
  5. package/dist/components/alert-dialog.d.mts +27 -26
  6. package/dist/components/alert-dialog.mjs +53 -45
  7. package/dist/components/alert.d.mts +14 -14
  8. package/dist/components/alert.mjs +17 -28
  9. package/dist/components/aspect-ratio.d.mts +2 -2
  10. package/dist/components/aspect-ratio.mjs +2 -3
  11. package/dist/components/avatar.d.mts +41 -5
  12. package/dist/components/avatar.mjs +40 -10
  13. package/dist/components/badge.d.mts +3 -15
  14. package/dist/components/badge.mjs +6 -48
  15. package/dist/components/breadcrumb.d.mts +1 -0
  16. package/dist/components/breadcrumb.mjs +11 -10
  17. package/dist/components/button-group.d.mts +3 -13
  18. package/dist/components/button-group.mjs +9 -31
  19. package/dist/components/button.d.mts +3 -26
  20. package/dist/components/button.mjs +9 -79
  21. package/dist/components/calendar.d.mts +6 -2
  22. package/dist/components/calendar.mjs +41 -44
  23. package/dist/components/card.d.mts +4 -2
  24. package/dist/components/card.mjs +9 -9
  25. package/dist/components/carousel.d.mts +16 -5
  26. package/dist/components/carousel.mjs +24 -11
  27. package/dist/components/chart.d.mts +9 -6
  28. package/dist/components/chart.mjs +21 -15
  29. package/dist/components/checkbox-cards.mjs +4 -4
  30. package/dist/components/checkbox-group.mjs +3 -4
  31. package/dist/components/checkbox.d.mts +2 -2
  32. package/dist/components/checkbox.mjs +6 -7
  33. package/dist/components/collapsible.d.mts +4 -4
  34. package/dist/components/collapsible.mjs +4 -5
  35. package/dist/components/command.d.mts +11 -1
  36. package/dist/components/command.mjs +35 -32
  37. package/dist/components/context-menu.d.mts +22 -15
  38. package/dist/components/context-menu.mjs +44 -39
  39. package/dist/components/dialog.d.mts +19 -23
  40. package/dist/components/dialog.mjs +48 -47
  41. package/dist/components/direction.d.mts +24 -0
  42. package/dist/components/direction.mjs +18 -0
  43. package/dist/components/drawer.d.mts +3 -21
  44. package/dist/components/drawer.mjs +19 -27
  45. package/dist/components/dropdown-menu.d.mts +22 -15
  46. package/dist/components/dropdown-menu.mjs +41 -37
  47. package/dist/components/empty.d.mts +3 -13
  48. package/dist/components/empty.mjs +8 -23
  49. package/dist/components/field.d.mts +3 -14
  50. package/dist/components/field.mjs +14 -44
  51. package/dist/components/form.d.mts +7 -10
  52. package/dist/components/form.mjs +6 -7
  53. package/dist/components/hover-card.d.mts +5 -5
  54. package/dist/components/hover-card.mjs +14 -12
  55. package/dist/components/input-group.d.mts +4 -31
  56. package/dist/components/input-group.mjs +14 -96
  57. package/dist/components/input-number.d.mts +3 -1
  58. package/dist/components/input-number.mjs +50 -28
  59. package/dist/components/input-otp.mjs +9 -7
  60. package/dist/components/input-password.mjs +1 -4
  61. package/dist/components/input-search.mjs +3 -5
  62. package/dist/components/input.mjs +1 -2
  63. package/dist/components/item.d.mts +4 -29
  64. package/dist/components/item.mjs +12 -65
  65. package/dist/components/kbd.mjs +1 -1
  66. package/dist/components/label.d.mts +2 -2
  67. package/dist/components/label.mjs +3 -4
  68. package/dist/components/menubar.d.mts +22 -16
  69. package/dist/components/menubar.mjs +54 -47
  70. package/dist/components/native-select.d.mts +5 -1
  71. package/dist/components/native-select.mjs +9 -6
  72. package/dist/components/navigation-menu.d.mts +30 -13
  73. package/dist/components/navigation-menu.mjs +35 -32
  74. package/dist/components/pagination.d.mts +7 -1
  75. package/dist/components/pagination.mjs +27 -12
  76. package/dist/components/popover.d.mts +40 -7
  77. package/dist/components/popover.mjs +46 -14
  78. package/dist/components/progress-circle.d.mts +3 -47
  79. package/dist/components/progress-circle.mjs +2 -48
  80. package/dist/components/progress.d.mts +2 -2
  81. package/dist/components/progress.mjs +5 -6
  82. package/dist/components/radio-cards.d.mts +3 -3
  83. package/dist/components/radio-cards.mjs +11 -11
  84. package/dist/components/radio-group.d.mts +3 -3
  85. package/dist/components/radio-group.mjs +9 -9
  86. package/dist/components/radio.mjs +2 -3
  87. package/dist/components/resizable.mjs +3 -8
  88. package/dist/components/scroll-area.d.mts +8 -24
  89. package/dist/components/scroll-area.mjs +16 -70
  90. package/dist/components/select.d.mts +14 -14
  91. package/dist/components/select.mjs +47 -47
  92. package/dist/components/separator.d.mts +4 -19
  93. package/dist/components/separator.mjs +6 -27
  94. package/dist/components/sheet.d.mts +18 -31
  95. package/dist/components/sheet.mjs +46 -87
  96. package/dist/components/sidebar.d.mts +3 -19
  97. package/dist/components/sidebar.mjs +48 -84
  98. package/dist/components/skeleton.mjs +1 -1
  99. package/dist/components/slider.d.mts +2 -2
  100. package/dist/components/slider.mjs +9 -11
  101. package/dist/components/sonner.mjs +11 -3
  102. package/dist/components/spinner.mjs +6 -7
  103. package/dist/components/switch.d.mts +5 -2
  104. package/dist/components/switch.mjs +7 -7
  105. package/dist/components/table.mjs +10 -10
  106. package/dist/components/tabs.d.mts +8 -5
  107. package/dist/components/tabs.mjs +18 -12
  108. package/dist/components/textarea.mjs +1 -1
  109. package/dist/components/toggle-group.d.mts +11 -7
  110. package/dist/components/toggle-group.mjs +20 -21
  111. package/dist/components/toggle.d.mts +4 -24
  112. package/dist/components/toggle.mjs +6 -45
  113. package/dist/components/tooltip.d.mts +7 -6
  114. package/dist/components/tooltip.mjs +19 -17
  115. package/dist/hooks/use-animated-value.mjs +0 -1
  116. package/dist/hooks/use-copy-to-clipboard.mjs +0 -1
  117. package/dist/hooks/use-is-mobile.mjs +0 -1
  118. package/dist/hooks/use-media-query.mjs +0 -1
  119. package/dist/hooks/use-mutation-observer.mjs +0 -1
  120. package/dist/hooks/use-pagination.mjs +0 -1
  121. package/dist/index.d.mts +38 -21
  122. package/dist/index.mjs +40 -23
  123. package/dist/lib/utils.d.mts +1 -12
  124. package/dist/lib/utils.mjs +1 -9
  125. package/dist/primitives/checkbox-group.d.mts +9 -11
  126. package/dist/primitives/checkbox-group.mjs +14 -19
  127. package/dist/primitives/input-number.d.mts +3 -4
  128. package/dist/primitives/input-number.mjs +3 -5
  129. package/dist/primitives/input.d.mts +4 -5
  130. package/dist/primitives/input.mjs +2 -3
  131. package/dist/primitives/progress-circle.d.mts +3 -4
  132. package/dist/primitives/progress-circle.mjs +2 -3
  133. package/dist/variants/alert.d.mts +18 -0
  134. package/dist/variants/alert.mjs +15 -0
  135. package/dist/variants/badge.d.mts +22 -0
  136. package/dist/variants/badge.mjs +19 -0
  137. package/dist/variants/button-group.d.mts +18 -0
  138. package/dist/variants/button-group.mjs +15 -0
  139. package/dist/variants/button.d.mts +32 -0
  140. package/dist/variants/button.mjs +34 -0
  141. package/dist/variants/empty.d.mts +18 -0
  142. package/dist/variants/empty.mjs +15 -0
  143. package/dist/variants/field.d.mts +19 -0
  144. package/dist/variants/field.mjs +16 -0
  145. package/dist/variants/input-group.d.mts +43 -0
  146. package/dist/variants/input-group.mjs +34 -0
  147. package/dist/variants/input-number.d.mts +45 -0
  148. package/dist/variants/input-number.mjs +40 -0
  149. package/dist/variants/item.d.mts +38 -0
  150. package/dist/variants/item.mjs +38 -0
  151. package/dist/variants/navigation-menu.d.mts +13 -0
  152. package/dist/variants/navigation-menu.mjs +8 -0
  153. package/dist/variants/progress-circle.d.mts +52 -0
  154. package/dist/variants/progress-circle.mjs +45 -0
  155. package/dist/variants/scroll-area.d.mts +24 -0
  156. package/dist/variants/scroll-area.mjs +58 -0
  157. package/dist/variants/separator.d.mts +23 -0
  158. package/dist/variants/separator.mjs +25 -0
  159. package/dist/variants/sheet.d.mts +20 -0
  160. package/dist/variants/sheet.mjs +17 -0
  161. package/dist/variants/sidebar.d.mts +23 -0
  162. package/dist/variants/sidebar.mjs +25 -0
  163. package/dist/variants/tabs.d.mts +18 -0
  164. package/dist/variants/tabs.mjs +15 -0
  165. package/dist/variants/toggle.d.mts +23 -0
  166. package/dist/variants/toggle.mjs +25 -0
  167. package/package.json +186 -55
  168. package/src/components/accordion.tsx +114 -0
  169. package/src/components/alert-dialog.tsx +298 -0
  170. package/src/components/alert.tsx +94 -0
  171. package/src/components/aspect-ratio.tsx +25 -0
  172. package/src/components/avatar.tsx +171 -0
  173. package/src/components/badge.tsx +35 -0
  174. package/src/components/breadcrumb.tsx +191 -0
  175. package/src/components/button-group.tsx +97 -0
  176. package/src/components/button.tsx +55 -0
  177. package/src/components/calendar.tsx +222 -0
  178. package/src/components/card.tsx +169 -0
  179. package/src/components/carousel.tsx +349 -0
  180. package/src/components/chart.tsx +536 -0
  181. package/src/components/checkbox-cards.tsx +72 -0
  182. package/src/components/checkbox-group.tsx +60 -0
  183. package/src/components/checkbox.tsx +44 -0
  184. package/src/components/collapsible.tsx +57 -0
  185. package/src/components/command.tsx +298 -0
  186. package/src/components/context-menu.tsx +410 -0
  187. package/src/components/dialog.tsx +243 -0
  188. package/src/components/direction.tsx +32 -0
  189. package/src/components/drawer.tsx +209 -0
  190. package/src/components/dropdown-menu.tsx +419 -0
  191. package/src/components/empty.tsx +155 -0
  192. package/src/components/field.tsx +329 -0
  193. package/src/components/form.tsx +258 -0
  194. package/src/components/hover-card.tsx +93 -0
  195. package/src/components/input-group.tsx +185 -0
  196. package/src/components/input-number.tsx +141 -0
  197. package/src/components/input-otp.tsx +132 -0
  198. package/src/components/input-password.tsx +50 -0
  199. package/src/components/input-search.tsx +81 -0
  200. package/src/components/input.tsx +36 -0
  201. package/src/components/item.tsx +266 -0
  202. package/src/components/kbd.tsx +47 -0
  203. package/src/components/label.tsx +36 -0
  204. package/src/components/menubar.tsx +440 -0
  205. package/src/components/native-select.tsx +87 -0
  206. package/src/components/navigation-menu.tsx +235 -0
  207. package/src/components/pagination.tsx +198 -0
  208. package/src/components/popover.tsx +170 -0
  209. package/src/components/progress-circle.tsx +185 -0
  210. package/src/components/progress.tsx +41 -0
  211. package/src/components/radio-cards.tsx +66 -0
  212. package/src/components/radio-group.tsx +59 -0
  213. package/src/components/radio.tsx +40 -0
  214. package/src/components/resizable.tsx +78 -0
  215. package/src/components/scroll-area.tsx +95 -0
  216. package/src/components/select.tsx +296 -0
  217. package/src/components/separator.tsx +60 -0
  218. package/src/components/sheet.tsx +241 -0
  219. package/src/components/sidebar.tsx +926 -0
  220. package/src/components/skeleton.tsx +35 -0
  221. package/src/components/slider.tsx +66 -0
  222. package/src/components/sonner.tsx +57 -0
  223. package/src/components/spinner.tsx +66 -0
  224. package/src/components/switch.tsx +44 -0
  225. package/src/components/table.tsx +183 -0
  226. package/src/components/tabs.tsx +110 -0
  227. package/src/components/textarea.tsx +35 -0
  228. package/src/components/toggle-group.tsx +137 -0
  229. package/src/components/toggle.tsx +30 -0
  230. package/src/components/tooltip.tsx +115 -0
  231. package/src/css/foundation/base.css +50 -0
  232. package/src/css/foundation/motion.css +36 -0
  233. package/src/css/foundation/source.css +3 -0
  234. package/src/css/foundation/tokens.css +71 -0
  235. package/src/css/foundation/variants.css +113 -0
  236. package/src/css/preset.css +5 -195
  237. package/src/css/style.css +1 -1
  238. package/src/css/{amber.css → themes/amber.css} +59 -22
  239. package/src/css/{blue.css → themes/blue.css} +59 -22
  240. package/src/css/{cyan.css → themes/cyan.css} +59 -22
  241. package/src/css/{emerald.css → themes/emerald.css} +59 -22
  242. package/src/css/{fuchsia.css → themes/fuchsia.css} +59 -22
  243. package/src/css/{gray.css → themes/gray.css} +59 -22
  244. package/src/css/{green.css → themes/green.css} +59 -22
  245. package/src/css/{indigo.css → themes/indigo.css} +59 -22
  246. package/src/css/{lime.css → themes/lime.css} +59 -22
  247. package/src/css/{neutral.css → themes/neutral.css} +59 -22
  248. package/src/css/{orange.css → themes/orange.css} +59 -22
  249. package/src/css/{pink.css → themes/pink.css} +59 -22
  250. package/src/css/{purple.css → themes/purple.css} +59 -22
  251. package/src/css/{red.css → themes/red.css} +59 -22
  252. package/src/css/{rose.css → themes/rose.css} +59 -22
  253. package/src/css/{sky.css → themes/sky.css} +59 -22
  254. package/src/css/{slate.css → themes/slate.css} +59 -22
  255. package/src/css/{stone.css → themes/stone.css} +59 -22
  256. package/src/css/{teal.css → themes/teal.css} +59 -22
  257. package/src/css/{violet.css → themes/violet.css} +59 -22
  258. package/src/css/{yellow.css → themes/yellow.css} +59 -22
  259. package/src/css/{zinc.css → themes/zinc.css} +59 -22
  260. package/src/hooks/use-animated-value.ts +91 -0
  261. package/src/hooks/use-copy-to-clipboard.ts +58 -0
  262. package/src/hooks/use-is-mobile.ts +25 -0
  263. package/src/hooks/use-media-query.ts +69 -0
  264. package/src/hooks/use-mutation-observer.ts +51 -0
  265. package/src/hooks/use-pagination.ts +164 -0
  266. package/src/index.ts +679 -0
  267. package/src/lib/utils.ts +5 -0
  268. package/src/primitives/checkbox-group.tsx +346 -0
  269. package/src/primitives/input-number.tsx +967 -0
  270. package/src/primitives/input.tsx +227 -0
  271. package/src/primitives/progress-circle.tsx +507 -0
  272. package/src/variants/alert.ts +34 -0
  273. package/src/variants/badge.ts +39 -0
  274. package/src/variants/button-group.ts +36 -0
  275. package/src/variants/button.ts +56 -0
  276. package/src/variants/empty.ts +34 -0
  277. package/src/variants/field.ts +37 -0
  278. package/src/variants/input-group.ts +80 -0
  279. package/src/variants/input-number.ts +65 -0
  280. package/src/variants/item.ts +68 -0
  281. package/src/variants/navigation-menu.ts +25 -0
  282. package/src/variants/progress-circle.ts +46 -0
  283. package/src/variants/scroll-area.ts +73 -0
  284. package/src/variants/separator.ts +40 -0
  285. package/src/variants/sheet.ts +37 -0
  286. package/src/variants/sidebar.ts +41 -0
  287. package/src/variants/tabs.ts +34 -0
  288. package/src/variants/toggle.ts +40 -0
  289. package/dist/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.mts +0 -6
@@ -1,9 +1,4 @@
1
1
  :root {
2
- --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
3
- --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
4
-
5
- --ring: oklch(0.702 0.183 293.541); /* --color-violet-400 */
6
-
7
2
  --background: oklch(1 0 0); /* --color-zinc-50 */
8
3
  --foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
9
4
 
@@ -15,27 +10,28 @@
15
10
 
16
11
  --destructive: oklch(0.577 0.245 27.325); /* --color-red-600 */
17
12
 
18
- --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
19
- --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
20
-
21
13
  --muted: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
22
14
  --muted-foreground: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
23
15
 
16
+ --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
17
+ --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
18
+
24
19
  --popover: oklch(1 0 0); /* --color-zinc-50 */
25
20
  --popover-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
26
21
 
27
22
  --card: oklch(1 0 0); /* --color-zinc-50 */
28
23
  --card-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
29
24
 
25
+ --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
26
+ --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
27
+ --ring: oklch(0.702 0.183 293.541); /* --color-violet-400 */
28
+
30
29
  --sidebar: oklch(0.985 0 0); /* --color-zinc-50 */
31
30
  --sidebar-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
32
-
33
31
  --sidebar-primary: oklch(0.541 0.281 293.009); /* --color-violet-600 */
34
32
  --sidebar-primary-foreground: oklch(0.969 0.016 293.756); /* --color-violet-50 */
35
-
36
33
  --sidebar-accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
37
34
  --sidebar-accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
38
-
39
35
  --sidebar-border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
40
36
  --sidebar-ring: oklch(0.702 0.183 293.541); /* --color-violet-400 */
41
37
 
@@ -47,11 +43,6 @@
47
43
  }
48
44
 
49
45
  .dark {
50
- --input: oklch(1 0 0 / 15%); /* --color-white */
51
- --border: oklch(1 0 0 / 10%); /* --color-white */
52
-
53
- --ring: oklch(0.38 0.189 293.745); /* --color-violet-900 */
54
-
55
46
  --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
56
47
  --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
57
48
 
@@ -63,27 +54,28 @@
63
54
 
64
55
  --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
65
56
 
66
- --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
67
- --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
68
-
69
57
  --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
70
58
  --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
71
59
 
60
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
61
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
62
+
72
63
  --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
73
64
  --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
74
65
 
75
66
  --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
76
67
  --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
77
68
 
69
+ --border: oklch(1 0 0 / 10%); /* --color-white */
70
+ --input: oklch(1 0 0 / 15%); /* --color-white */
71
+ --ring: oklch(0.38 0.189 293.745); /* --color-violet-900 */
72
+
78
73
  --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
79
74
  --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
80
-
81
75
  --sidebar-primary: oklch(0.606 0.25 292.717); /* --color-violet-500 */
82
76
  --sidebar-primary-foreground: oklch(0.969 0.016 293.756); /* --color-violet-50 */
83
-
84
77
  --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
85
78
  --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
86
-
87
79
  --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
88
80
  --sidebar-ring: oklch(0.38 0.189 293.745); /* --color-violet-900 */
89
81
 
@@ -93,3 +85,48 @@
93
85
  --chart-4: oklch(0.491 0.27 292.581); /* --color-violet-700 */
94
86
  --chart-5: oklch(0.432 0.232 292.759); /* --color-violet-800 */
95
87
  }
88
+ @media (prefers-color-scheme: dark) {
89
+ :root:not(.light) {
90
+ --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
91
+ --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
92
+
93
+ --primary: oklch(0.606 0.25 292.717); /* --color-violet-500 */
94
+ --primary-foreground: oklch(0.969 0.016 293.756); /* --color-violet-50 */
95
+
96
+ --secondary: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
97
+ --secondary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
98
+
99
+ --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
100
+
101
+ --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
102
+ --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
103
+
104
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
105
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
106
+
107
+ --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
108
+ --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
109
+
110
+ --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
111
+ --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
112
+
113
+ --border: oklch(1 0 0 / 10%); /* --color-white */
114
+ --input: oklch(1 0 0 / 15%); /* --color-white */
115
+ --ring: oklch(0.38 0.189 293.745); /* --color-violet-900 */
116
+
117
+ --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
118
+ --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
119
+ --sidebar-primary: oklch(0.606 0.25 292.717); /* --color-violet-500 */
120
+ --sidebar-primary-foreground: oklch(0.969 0.016 293.756); /* --color-violet-50 */
121
+ --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
122
+ --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
123
+ --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
124
+ --sidebar-ring: oklch(0.38 0.189 293.745); /* --color-violet-900 */
125
+
126
+ --chart-1: oklch(0.811 0.111 293.571); /* --color-violet-300 */
127
+ --chart-2: oklch(0.606 0.25 292.717); /* --color-violet-500 */
128
+ --chart-3: oklch(0.541 0.281 293.009); /* --color-violet-600 */
129
+ --chart-4: oklch(0.491 0.27 292.581); /* --color-violet-700 */
130
+ --chart-5: oklch(0.432 0.232 292.759); /* --color-violet-800 */
131
+ }
132
+ }
@@ -1,9 +1,4 @@
1
1
  :root {
2
- --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
3
- --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
4
-
5
- --ring: oklch(0.852 0.199 91.936); /* --color-yellow-400 */
6
-
7
2
  --background: oklch(1 0 0); /* --color-zinc-50 */
8
3
  --foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
9
4
 
@@ -15,27 +10,28 @@
15
10
 
16
11
  --destructive: oklch(0.577 0.245 27.325); /* --color-red-600 */
17
12
 
18
- --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
19
- --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
20
-
21
13
  --muted: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
22
14
  --muted-foreground: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
23
15
 
16
+ --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
17
+ --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
18
+
24
19
  --popover: oklch(1 0 0); /* --color-zinc-50 */
25
20
  --popover-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
26
21
 
27
22
  --card: oklch(1 0 0); /* --color-zinc-50 */
28
23
  --card-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
29
24
 
25
+ --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
26
+ --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
27
+ --ring: oklch(0.852 0.199 91.936); /* --color-yellow-400 */
28
+
30
29
  --sidebar: oklch(0.985 0 0); /* --color-zinc-50 */
31
30
  --sidebar-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
32
-
33
31
  --sidebar-primary: oklch(0.681 0.162 75.834); /* --color-yellow-600 */
34
32
  --sidebar-primary-foreground: oklch(0.987 0.026 102.212); /* --color-yellow-50 */
35
-
36
33
  --sidebar-accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
37
34
  --sidebar-accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
38
-
39
35
  --sidebar-border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
40
36
  --sidebar-ring: oklch(0.852 0.199 91.936); /* --color-yellow-400 */
41
37
 
@@ -47,11 +43,6 @@
47
43
  }
48
44
 
49
45
  .dark {
50
- --input: oklch(1 0 0 / 15%); /* --color-white */
51
- --border: oklch(1 0 0 / 10%); /* --color-white */
52
-
53
- --ring: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
54
-
55
46
  --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
56
47
  --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
57
48
 
@@ -63,27 +54,28 @@
63
54
 
64
55
  --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
65
56
 
66
- --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
67
- --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
68
-
69
57
  --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
70
58
  --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
71
59
 
60
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
61
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
62
+
72
63
  --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
73
64
  --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
74
65
 
75
66
  --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
76
67
  --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
77
68
 
69
+ --border: oklch(1 0 0 / 10%); /* --color-white */
70
+ --input: oklch(1 0 0 / 15%); /* --color-white */
71
+ --ring: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
72
+
78
73
  --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
79
74
  --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
80
-
81
75
  --sidebar-primary: oklch(0.795 0.184 86.047); /* --color-yellow-500 */
82
76
  --sidebar-primary-foreground: oklch(0.987 0.026 102.212); /* --color-yellow-50 */
83
-
84
77
  --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
85
78
  --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
86
-
87
79
  --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
88
80
  --sidebar-ring: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
89
81
 
@@ -93,3 +85,48 @@
93
85
  --chart-4: oklch(0.554 0.135 66.442); /* --color-yellow-700 */
94
86
  --chart-5: oklch(0.476 0.114 61.907); /* --color-yellow-800 */
95
87
  }
88
+ @media (prefers-color-scheme: dark) {
89
+ :root:not(.light) {
90
+ --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
91
+ --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
92
+
93
+ --primary: oklch(0.795 0.184 86.047); /* --color-yellow-500 */
94
+ --primary-foreground: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
95
+
96
+ --secondary: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
97
+ --secondary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
98
+
99
+ --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
100
+
101
+ --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
102
+ --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
103
+
104
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
105
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
106
+
107
+ --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
108
+ --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
109
+
110
+ --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
111
+ --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
112
+
113
+ --border: oklch(1 0 0 / 10%); /* --color-white */
114
+ --input: oklch(1 0 0 / 15%); /* --color-white */
115
+ --ring: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
116
+
117
+ --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
118
+ --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
119
+ --sidebar-primary: oklch(0.795 0.184 86.047); /* --color-yellow-500 */
120
+ --sidebar-primary-foreground: oklch(0.987 0.026 102.212); /* --color-yellow-50 */
121
+ --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
122
+ --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
123
+ --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
124
+ --sidebar-ring: oklch(0.421 0.095 57.708); /* --color-yellow-900 */
125
+
126
+ --chart-1: oklch(0.905 0.182 98.111); /* --color-yellow-300 */
127
+ --chart-2: oklch(0.795 0.184 86.047); /* --color-yellow-500 */
128
+ --chart-3: oklch(0.681 0.162 75.834); /* --color-yellow-600 */
129
+ --chart-4: oklch(0.554 0.135 66.442); /* --color-yellow-700 */
130
+ --chart-5: oklch(0.476 0.114 61.907); /* --color-yellow-800 */
131
+ }
132
+ }
@@ -1,9 +1,4 @@
1
1
  :root {
2
- --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
3
- --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
4
-
5
- --ring: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
6
-
7
2
  --background: oklch(1 0 0); /* --color-zinc-50 */
8
3
  --foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
9
4
 
@@ -15,27 +10,28 @@
15
10
 
16
11
  --destructive: oklch(0.577 0.245 27.325); /* --color-red-600 */
17
12
 
18
- --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
19
- --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
20
-
21
13
  --muted: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
22
14
  --muted-foreground: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
23
15
 
16
+ --accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
17
+ --accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
18
+
24
19
  --popover: oklch(1 0 0); /* --color-zinc-50 */
25
20
  --popover-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
26
21
 
27
22
  --card: oklch(1 0 0); /* --color-zinc-50 */
28
23
  --card-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
29
24
 
25
+ --border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
26
+ --input: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
27
+ --ring: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
28
+
30
29
  --sidebar: oklch(0.985 0 0); /* --color-zinc-50 */
31
30
  --sidebar-foreground: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
32
-
33
31
  --sidebar-primary: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
34
32
  --sidebar-primary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
35
-
36
33
  --sidebar-accent: oklch(0.967 0.001 286.375); /* --color-zinc-100 */
37
34
  --sidebar-accent-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
38
-
39
35
  --sidebar-border: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
40
36
  --sidebar-ring: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
41
37
 
@@ -47,11 +43,6 @@
47
43
  }
48
44
 
49
45
  .dark {
50
- --input: oklch(1 0 0 / 15%); /* --color-white */
51
- --border: oklch(1 0 0 / 10%); /* --color-white */
52
-
53
- --ring: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
54
-
55
46
  --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
56
47
  --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
57
48
 
@@ -63,27 +54,28 @@
63
54
 
64
55
  --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
65
56
 
66
- --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
67
- --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
68
-
69
57
  --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
70
58
  --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
71
59
 
60
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
61
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
62
+
72
63
  --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
73
64
  --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
74
65
 
75
66
  --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
76
67
  --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
77
68
 
69
+ --border: oklch(1 0 0 / 10%); /* --color-white */
70
+ --input: oklch(1 0 0 / 15%); /* --color-white */
71
+ --ring: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
72
+
78
73
  --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
79
74
  --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
80
-
81
75
  --sidebar-primary: oklch(0.488 0.243 264.376); /* --color-blue-700 */
82
76
  --sidebar-primary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
83
-
84
77
  --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
85
78
  --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
86
-
87
79
  --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
88
80
  --sidebar-ring: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
89
81
 
@@ -93,3 +85,48 @@
93
85
  --chart-4: oklch(0.627 0.265 303.9); /* --color-purple-500 */
94
86
  --chart-5: oklch(0.645 0.246 16.439); /* --color-rose-500 */
95
87
  }
88
+ @media (prefers-color-scheme: dark) {
89
+ :root:not(.light) {
90
+ --background: oklch(0.141 0.005 285.823); /* --color-zinc-950 */
91
+ --foreground: oklch(0.985 0 0); /* --color-zinc-50 */
92
+
93
+ --primary: oklch(0.92 0.004 286.32); /* --color-zinc-200 */
94
+ --primary-foreground: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
95
+
96
+ --secondary: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
97
+ --secondary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
98
+
99
+ --destructive: oklch(0.704 0.191 22.216); /* --color-red-400 */
100
+
101
+ --muted: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
102
+ --muted-foreground: oklch(0.705 0.015 286.067); /* --color-zinc-400 */
103
+
104
+ --accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
105
+ --accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
106
+
107
+ --popover: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
108
+ --popover-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
109
+
110
+ --card: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
111
+ --card-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
112
+
113
+ --border: oklch(1 0 0 / 10%); /* --color-white */
114
+ --input: oklch(1 0 0 / 15%); /* --color-white */
115
+ --ring: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
116
+
117
+ --sidebar: oklch(0.21 0.006 285.885); /* --color-zinc-900 */
118
+ --sidebar-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
119
+ --sidebar-primary: oklch(0.488 0.243 264.376); /* --color-blue-700 */
120
+ --sidebar-primary-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
121
+ --sidebar-accent: oklch(0.274 0.006 286.033); /* --color-zinc-800 */
122
+ --sidebar-accent-foreground: oklch(0.985 0 0); /* --color-zinc-50 */
123
+ --sidebar-border: oklch(1 0 0 / 10%); /* --color-white */
124
+ --sidebar-ring: oklch(0.552 0.016 285.938); /* --color-zinc-500 */
125
+
126
+ --chart-1: oklch(0.488 0.243 264.376); /* --color-blue-700 */
127
+ --chart-2: oklch(0.696 0.17 162.48); /* --color-emerald-500 */
128
+ --chart-3: oklch(0.769 0.188 70.08); /* --color-amber-500 */
129
+ --chart-4: oklch(0.627 0.265 303.9); /* --color-purple-500 */
130
+ --chart-5: oklch(0.645 0.246 16.439); /* --color-rose-500 */
131
+ }
132
+ }
@@ -0,0 +1,91 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+
3
+ /**
4
+ * Produce a smoothly animated numeric value in response to changes.
5
+ *
6
+ * Applies a time-based easing (easeOutQuad) between the current and target values
7
+ * over the specified duration. When disabled, the value updates immediately.
8
+ *
9
+ * @param targetValue - Target number to animate toward; null resolves to 0.
10
+ * @param duration - Animation duration in milliseconds.
11
+ * @param animate - When false, bypasses animation and sets the value directly.
12
+ * @returns The current (rounded) animated value.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const value = useAnimatedValue(75, 1000, true);
17
+ * return <div>{value}%</div>;
18
+ * ```
19
+ *
20
+ * @since 0.3.16-canary.0
21
+ */
22
+ export function useAnimatedValue(targetValue: null | number, duration: number, animate?: boolean): number {
23
+ // Default to 0 when targetValue is null
24
+ const actualTargetValue = targetValue ?? 0;
25
+
26
+ // Current animated output
27
+ const [animatedValue, setAnimatedValue] = useState(actualTargetValue);
28
+
29
+ // Prevent stale closures inside RAF loop
30
+ const animatedValueRef = useRef(actualTargetValue);
31
+
32
+ useEffect(() => {
33
+ animatedValueRef.current = animatedValue;
34
+ }, [animatedValue]);
35
+
36
+ useEffect(() => {
37
+ if (!animate) {
38
+ setAnimatedValue(actualTargetValue);
39
+
40
+ return;
41
+ }
42
+
43
+ // Starting value
44
+ const currentValue = animatedValueRef.current;
45
+
46
+ // Total delta across the animation
47
+ const valueRange = actualTargetValue - currentValue;
48
+
49
+ // Start timestamp
50
+ const startTime = performance.now();
51
+
52
+ if (duration <= 0 || valueRange === 0) {
53
+ setAnimatedValue(actualTargetValue);
54
+
55
+ return;
56
+ }
57
+
58
+ // requestAnimationFrame id for cleanup
59
+ let animationFrame: number;
60
+
61
+ // RAF step
62
+ const animateValue = (currentTime: number): void => {
63
+ // Elapsed milliseconds
64
+ const elapsedTime = currentTime - startTime;
65
+
66
+ if (elapsedTime >= duration) {
67
+ setAnimatedValue(actualTargetValue);
68
+ } else {
69
+ // Linear progress (0..1)
70
+ const progress = elapsedTime / duration;
71
+
72
+ // easeOutQuad easing
73
+ const easeProgress = 1 - (1 - progress) * (1 - progress);
74
+
75
+ // Interpolated value
76
+ const nextValue = currentValue + valueRange * easeProgress;
77
+
78
+ setAnimatedValue(nextValue);
79
+ animationFrame = requestAnimationFrame(animateValue);
80
+ }
81
+ };
82
+
83
+ animationFrame = requestAnimationFrame(animateValue);
84
+
85
+ return (): void => {
86
+ cancelAnimationFrame(animationFrame);
87
+ };
88
+ }, [actualTargetValue, duration, animate]);
89
+
90
+ return Math.round(animatedValue);
91
+ }
@@ -0,0 +1,58 @@
1
+ import { useState } from "react";
2
+
3
+ /**
4
+ * Provide clipboard copy capability with a transient copied state.
5
+ *
6
+ * Internally uses the Clipboard API when available and sets a temporary
7
+ * `isCopied` flag for UI feedback. A custom callback may be invoked upon copy.
8
+ *
9
+ * @param options - Configuration options.
10
+ * - onCopy: Callback invoked after a successful copy.
11
+ * - timeout: Duration in milliseconds to keep `isCopied` true. Defaults to 2000.
12
+ * @returns An object with a `copyToClipboard` function and an `isCopied` flag.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const { copyToClipboard, isCopied } = useCopyToClipboard({ timeout: 1500 });
17
+ * <button onClick={() => copyToClipboard("Hello")}>{isCopied ? "Copied" : "Copy"}</button>
18
+ * ```
19
+ *
20
+ * @since 0.3.16-canary.0
21
+ */
22
+ export function useCopyToClipboard({ onCopy, timeout = 2000 }: { onCopy?: () => void; timeout?: number } = {}): {
23
+ copyToClipboard: (value: string) => Promise<void>;
24
+ isCopied: boolean;
25
+ } {
26
+ const [isCopied, setIsCopied] = useState(false);
27
+
28
+ const copyToClipboard = async (value: string): Promise<void> => {
29
+ if (
30
+ typeof window === "undefined" ||
31
+ !("clipboard" in navigator) ||
32
+ typeof navigator.clipboard.writeText !== "function"
33
+ ) {
34
+ return;
35
+ }
36
+
37
+ if (!value) {
38
+ return;
39
+ }
40
+
41
+ try {
42
+ await navigator.clipboard.writeText(value);
43
+ setIsCopied(true);
44
+
45
+ if (onCopy) {
46
+ onCopy();
47
+ }
48
+
49
+ setTimeout(() => {
50
+ setIsCopied(false);
51
+ }, timeout);
52
+ } catch (error) {
53
+ console.error(error);
54
+ }
55
+ };
56
+
57
+ return { copyToClipboard, isCopied };
58
+ }
@@ -0,0 +1,25 @@
1
+ import { useMediaQuery } from "#/hooks/use-media-query";
2
+
3
+ /**
4
+ * Determine whether the current viewport should be treated as mobile.
5
+ *
6
+ * Uses {@link useMediaQuery} to evaluate a max-width media query derived from the
7
+ * provided breakpoint. By default, widths below 768px are considered mobile.
8
+ *
9
+ * @param mobileBreakpoint - Pixel width used as the mobile breakpoint. Values strictly
10
+ * less than this breakpoint are treated as mobile. Defaults to 768.
11
+ * @returns true when the viewport width is less than the given breakpoint; otherwise false.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const isMobile = useIsMobile();
16
+ * if (isMobile) {
17
+ * // Render compact layout
18
+ * }
19
+ * ```
20
+ *
21
+ * @since 0.3.16-canary.0
22
+ */
23
+ export function useIsMobile(mobileBreakpoint = 768): boolean {
24
+ return useMediaQuery(`(max-width: ${(mobileBreakpoint - 1).toString()}px)`);
25
+ }
@@ -0,0 +1,69 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ /**
4
+ * Event handler for MediaQueryList changes.
5
+ *
6
+ * @param event - Media query change event providing the updated match status.
7
+ */
8
+ type MediaQueryChangeHandler = (event: MediaQueryListEvent) => void;
9
+
10
+ /**
11
+ * Subscribe to a CSS media query and receive its match state.
12
+ *
13
+ * Evaluates the query immediately (when supported) and updates on changes
14
+ * via an event listener.
15
+ *
16
+ * @param query - A valid media query string (e.g., "(max-width: 768px)").
17
+ * @returns true when the media query currently matches; otherwise false.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const isNarrow = useMediaQuery("(max-width: 768px)");
22
+ * ```
23
+ *
24
+ * @since 0.3.16-canary.0
25
+ */
26
+ export function useMediaQuery(query: string): boolean {
27
+ /**
28
+ * State to store whether the query matches.
29
+ * The initial value is calculated based on the current window state.
30
+ */
31
+ const [matches, setMatches] = useState<boolean>(() => {
32
+ // Ensure initial state matches current media query status
33
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
34
+ return window.matchMedia(query).matches;
35
+ }
36
+
37
+ return false;
38
+ });
39
+
40
+ useEffect(() => {
41
+ // Only run in a browser environment where matchMedia is available
42
+ if (typeof window === "undefined") {
43
+ return;
44
+ }
45
+
46
+ /**
47
+ * MediaQueryList to evaluate and observe the provided query.
48
+ */
49
+ const mediaQueryList = window.matchMedia(query);
50
+
51
+ /**
52
+ * Update state when the media query status changes.
53
+ */
54
+ const onChange: MediaQueryChangeHandler = (event): void => {
55
+ setMatches(event.matches);
56
+ };
57
+
58
+ mediaQueryList.addEventListener("change", onChange);
59
+
60
+ /**
61
+ * Remove the event listener on unmount or when the query changes.
62
+ */
63
+ return (): void => {
64
+ mediaQueryList.removeEventListener("change", onChange);
65
+ };
66
+ }, [query]);
67
+
68
+ return matches;
69
+ }