@enya-learning/nova 0.4.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 (275) hide show
  1. package/Readme.md +418 -0
  2. package/changelog.md +44 -0
  3. package/components.json +23 -0
  4. package/dist/chunks/button-npy7isi3nsvlhf86.js +81 -0
  5. package/dist/chunks/button-npy7isi3nsvlhf86.js.map +1 -0
  6. package/dist/chunks/rolldown-runtime-fifbipqm1abz4ydz.js +30 -0
  7. package/dist/chunks/utils-kyb5nny4y41y7hd8.js +10 -0
  8. package/dist/chunks/utils-kyb5nny4y41y7hd8.js.map +1 -0
  9. package/dist/chunks/vendor-base-ui-efqwzv4oinwg1f0d.js +16049 -0
  10. package/dist/chunks/vendor-base-ui-efqwzv4oinwg1f0d.js.map +1 -0
  11. package/dist/chunks/vendor-styling-eeiy57tyznmxsfky.js +1744 -0
  12. package/dist/chunks/vendor-styling-eeiy57tyznmxsfky.js.map +1 -0
  13. package/dist/components/button.js +6 -0
  14. package/dist/components/enya-logo.js +191 -0
  15. package/dist/components/enya-logo.js.map +1 -0
  16. package/dist/index.js +23499 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/src/components/accordion/accordion.d.ts +6 -0
  19. package/dist/src/components/accordion/index.d.ts +1 -0
  20. package/dist/src/components/alert/alert.d.ts +10 -0
  21. package/dist/src/components/alert/index.d.ts +1 -0
  22. package/dist/src/components/alert-dialog/alert-dialog.d.ts +18 -0
  23. package/dist/src/components/alert-dialog/index.d.ts +1 -0
  24. package/dist/src/components/aspect-ratio/aspect-ratio.d.ts +4 -0
  25. package/dist/src/components/aspect-ratio/index.d.ts +1 -0
  26. package/dist/src/components/avatar/avatar.d.ts +11 -0
  27. package/dist/src/components/avatar/index.d.ts +1 -0
  28. package/dist/src/components/badge/badge.d.ts +7 -0
  29. package/dist/src/components/badge/index.d.ts +1 -0
  30. package/dist/src/components/breadcrumb/breadcrumb.d.ts +10 -0
  31. package/dist/src/components/breadcrumb/index.d.ts +1 -0
  32. package/dist/src/components/button/button.d.ts +8 -0
  33. package/dist/src/components/button/index.d.ts +1 -0
  34. package/dist/src/components/button-group/button-group.d.ts +10 -0
  35. package/dist/src/components/button-group/index.d.ts +1 -0
  36. package/dist/src/components/calendar/calendar.d.ts +10 -0
  37. package/dist/src/components/calendar/index.d.ts +1 -0
  38. package/dist/src/components/card/card.d.ts +11 -0
  39. package/dist/src/components/card/index.d.ts +1 -0
  40. package/dist/src/components/carousel/carousel.d.ts +28 -0
  41. package/dist/src/components/carousel/index.d.ts +1 -0
  42. package/dist/src/components/chart/chart.d.ts +44 -0
  43. package/dist/src/components/chart/index.d.ts +1 -0
  44. package/dist/src/components/checkbox/checkbox.d.ts +3 -0
  45. package/dist/src/components/checkbox/index.d.ts +1 -0
  46. package/dist/src/components/collapsible/collapsible.d.ts +5 -0
  47. package/dist/src/components/collapsible/index.d.ts +1 -0
  48. package/dist/src/components/combobox/combobox.d.ts +24 -0
  49. package/dist/src/components/combobox/index.d.ts +1 -0
  50. package/dist/src/components/command/command.d.ts +19 -0
  51. package/dist/src/components/command/index.d.ts +1 -0
  52. package/dist/src/components/context-menu/context-menu.d.ts +29 -0
  53. package/dist/src/components/context-menu/index.d.ts +1 -0
  54. package/dist/src/components/dialog/dialog.d.ts +17 -0
  55. package/dist/src/components/dialog/index.d.ts +1 -0
  56. package/dist/src/components/direction/direction.d.ts +1 -0
  57. package/dist/src/components/direction/index.d.ts +1 -0
  58. package/dist/src/components/drawer/drawer.d.ts +13 -0
  59. package/dist/src/components/drawer/index.d.ts +1 -0
  60. package/dist/src/components/dropdown-menu/dropdown-menu.d.ts +29 -0
  61. package/dist/src/components/dropdown-menu/index.d.ts +1 -0
  62. package/dist/src/components/empty/empty.d.ts +11 -0
  63. package/dist/src/components/empty/index.d.ts +1 -0
  64. package/dist/src/components/enya-logo/enya-logo.d.ts +121 -0
  65. package/dist/src/components/enya-logo/index.d.ts +1 -0
  66. package/dist/src/components/field/field.d.ts +24 -0
  67. package/dist/src/components/field/index.d.ts +1 -0
  68. package/dist/src/components/hover-card/hover-card.d.ts +5 -0
  69. package/dist/src/components/hover-card/index.d.ts +1 -0
  70. package/dist/src/components/input/index.d.ts +1 -0
  71. package/dist/src/components/input/input.d.ts +3 -0
  72. package/dist/src/components/input-group/index.d.ts +1 -0
  73. package/dist/src/components/input-group/input-group.d.ts +18 -0
  74. package/dist/src/components/input-otp/index.d.ts +1 -0
  75. package/dist/src/components/input-otp/input-otp.d.ts +11 -0
  76. package/dist/src/components/item/index.d.ts +1 -0
  77. package/dist/src/components/item/item.d.ts +22 -0
  78. package/dist/src/components/kbd/index.d.ts +1 -0
  79. package/dist/src/components/kbd/kbd.d.ts +3 -0
  80. package/dist/src/components/label/index.d.ts +1 -0
  81. package/dist/src/components/label/label.d.ts +3 -0
  82. package/dist/src/components/menubar/index.d.ts +1 -0
  83. package/dist/src/components/menubar/menubar.d.ts +29 -0
  84. package/dist/src/components/native-select/index.d.ts +1 -0
  85. package/dist/src/components/native-select/native-select.d.ts +8 -0
  86. package/dist/src/components/navigation-menu/index.d.ts +1 -0
  87. package/dist/src/components/navigation-menu/navigation-menu.d.ts +11 -0
  88. package/dist/src/components/pagination/index.d.ts +1 -0
  89. package/dist/src/components/pagination/pagination.d.ts +17 -0
  90. package/dist/src/components/popover/index.d.ts +1 -0
  91. package/dist/src/components/popover/popover.d.ts +9 -0
  92. package/dist/src/components/progress/index.d.ts +1 -0
  93. package/dist/src/components/progress/progress.d.ts +7 -0
  94. package/dist/src/components/radio-group/index.d.ts +1 -0
  95. package/dist/src/components/radio-group/radio-group.d.ts +5 -0
  96. package/dist/src/components/resizable/index.d.ts +1 -0
  97. package/dist/src/components/resizable/resizable.d.ts +7 -0
  98. package/dist/src/components/scroll-area/index.d.ts +1 -0
  99. package/dist/src/components/scroll-area/scroll-area.d.ts +4 -0
  100. package/dist/src/components/select/index.d.ts +1 -0
  101. package/dist/src/components/select/select.d.ts +15 -0
  102. package/dist/src/components/separator/index.d.ts +1 -0
  103. package/dist/src/components/separator/separator.d.ts +3 -0
  104. package/dist/src/components/sheet/index.d.ts +1 -0
  105. package/dist/src/components/sheet/sheet.d.ts +14 -0
  106. package/dist/src/components/sidebar/index.d.ts +1 -0
  107. package/dist/src/components/sidebar/sidebar.d.ts +63 -0
  108. package/dist/src/components/skeleton/index.d.ts +1 -0
  109. package/dist/src/components/skeleton/skeleton.d.ts +2 -0
  110. package/dist/src/components/slider/index.d.ts +1 -0
  111. package/dist/src/components/slider/slider.d.ts +3 -0
  112. package/dist/src/components/sonner/index.d.ts +1 -0
  113. package/dist/src/components/sonner/sonner.d.ts +3 -0
  114. package/dist/src/components/spinner/index.d.ts +1 -0
  115. package/dist/src/components/spinner/spinner.d.ts +2 -0
  116. package/dist/src/components/switch/index.d.ts +1 -0
  117. package/dist/src/components/switch/switch.d.ts +5 -0
  118. package/dist/src/components/table/index.d.ts +1 -0
  119. package/dist/src/components/table/table.d.ts +10 -0
  120. package/dist/src/components/tabs/index.d.ts +1 -0
  121. package/dist/src/components/tabs/tabs.d.ts +10 -0
  122. package/dist/src/components/textarea/index.d.ts +1 -0
  123. package/dist/src/components/textarea/textarea.d.ts +3 -0
  124. package/dist/src/components/toggle/index.d.ts +1 -0
  125. package/dist/src/components/toggle/toggle.d.ts +8 -0
  126. package/dist/src/components/toggle-group/index.d.ts +1 -0
  127. package/dist/src/components/toggle-group/toggle-group.d.ts +10 -0
  128. package/dist/src/components/tooltip/index.d.ts +1 -0
  129. package/dist/src/components/tooltip/tooltip.d.ts +6 -0
  130. package/dist/src/hooks/use-mobile.d.ts +1 -0
  131. package/dist/src/index.d.ts +57 -0
  132. package/dist/src/lib/utils.d.ts +2 -0
  133. package/dist/src/utils/index.d.ts +1 -0
  134. package/dist/styles/nova-binding.css +290 -0
  135. package/dist/styles/nova.css +58 -0
  136. package/dist/styles/theme-nova.css +201 -0
  137. package/dist/styles/typography.css +215 -0
  138. package/dist/utils.js +5 -0
  139. package/eslint.config.js +30 -0
  140. package/package.json +94 -0
  141. package/plopfile.mjs +49 -0
  142. package/src/components/.gitkeep +0 -0
  143. package/src/components/accordion/accordion.mdx +83 -0
  144. package/src/components/accordion/accordion.tsx +81 -0
  145. package/src/components/accordion/index.ts +1 -0
  146. package/src/components/alert/alert.tsx +76 -0
  147. package/src/components/alert/index.ts +1 -0
  148. package/src/components/alert-dialog/alert-dialog.tsx +187 -0
  149. package/src/components/alert-dialog/index.ts +1 -0
  150. package/src/components/aspect-ratio/aspect-ratio.tsx +22 -0
  151. package/src/components/aspect-ratio/index.ts +1 -0
  152. package/src/components/avatar/avatar.tsx +107 -0
  153. package/src/components/avatar/index.ts +1 -0
  154. package/src/components/badge/badge.tsx +52 -0
  155. package/src/components/badge/index.ts +1 -0
  156. package/src/components/breadcrumb/breadcrumb.tsx +122 -0
  157. package/src/components/breadcrumb/index.ts +1 -0
  158. package/src/components/button/button.mdx +255 -0
  159. package/src/components/button/button.tsx +59 -0
  160. package/src/components/button/index.ts +1 -0
  161. package/src/components/button-group/button-group.tsx +87 -0
  162. package/src/components/button-group/index.ts +1 -0
  163. package/src/components/calendar/calendar.tsx +228 -0
  164. package/src/components/calendar/index.ts +1 -0
  165. package/src/components/card/card.tsx +100 -0
  166. package/src/components/card/index.ts +1 -0
  167. package/src/components/carousel/carousel.tsx +243 -0
  168. package/src/components/carousel/index.ts +1 -0
  169. package/src/components/chart/chart.tsx +371 -0
  170. package/src/components/chart/index.ts +1 -0
  171. package/src/components/checkbox/checkbox.tsx +28 -0
  172. package/src/components/checkbox/index.ts +1 -0
  173. package/src/components/collapsible/collapsible.tsx +19 -0
  174. package/src/components/collapsible/index.ts +1 -0
  175. package/src/components/combobox/combobox.tsx +298 -0
  176. package/src/components/combobox/index.ts +1 -0
  177. package/src/components/command/command.tsx +193 -0
  178. package/src/components/command/index.ts +1 -0
  179. package/src/components/context-menu/context-menu.tsx +272 -0
  180. package/src/components/context-menu/index.ts +1 -0
  181. package/src/components/dialog/dialog.tsx +157 -0
  182. package/src/components/dialog/index.ts +1 -0
  183. package/src/components/direction/direction.tsx +4 -0
  184. package/src/components/direction/index.ts +1 -0
  185. package/src/components/drawer/drawer.tsx +134 -0
  186. package/src/components/drawer/index.ts +1 -0
  187. package/src/components/dropdown-menu/dropdown-menu.tsx +270 -0
  188. package/src/components/dropdown-menu/index.ts +1 -0
  189. package/src/components/empty/empty.tsx +104 -0
  190. package/src/components/empty/index.ts +1 -0
  191. package/src/components/enya-logo/enya-logo.mdx +174 -0
  192. package/src/components/enya-logo/enya-logo.tsx +383 -0
  193. package/src/components/enya-logo/index.ts +1 -0
  194. package/src/components/field/field.tsx +236 -0
  195. package/src/components/field/index.ts +1 -0
  196. package/src/components/hover-card/hover-card.tsx +51 -0
  197. package/src/components/hover-card/index.ts +1 -0
  198. package/src/components/input/index.ts +1 -0
  199. package/src/components/input/input.tsx +20 -0
  200. package/src/components/input-group/index.ts +1 -0
  201. package/src/components/input-group/input-group.tsx +157 -0
  202. package/src/components/input-otp/index.ts +1 -0
  203. package/src/components/input-otp/input-otp.tsx +84 -0
  204. package/src/components/item/index.ts +1 -0
  205. package/src/components/item/item.tsx +201 -0
  206. package/src/components/kbd/index.ts +1 -0
  207. package/src/components/kbd/kbd.tsx +26 -0
  208. package/src/components/label/index.ts +1 -0
  209. package/src/components/label/label.tsx +20 -0
  210. package/src/components/menubar/index.ts +1 -0
  211. package/src/components/menubar/menubar.tsx +281 -0
  212. package/src/components/native-select/index.ts +1 -0
  213. package/src/components/native-select/native-select.tsx +65 -0
  214. package/src/components/navigation-menu/index.ts +1 -0
  215. package/src/components/navigation-menu/navigation-menu.tsx +171 -0
  216. package/src/components/pagination/index.ts +1 -0
  217. package/src/components/pagination/pagination.tsx +133 -0
  218. package/src/components/popover/index.ts +1 -0
  219. package/src/components/popover/popover.tsx +88 -0
  220. package/src/components/progress/index.ts +1 -0
  221. package/src/components/progress/progress.tsx +83 -0
  222. package/src/components/radio-group/index.ts +1 -0
  223. package/src/components/radio-group/radio-group.tsx +36 -0
  224. package/src/components/resizable/index.ts +1 -0
  225. package/src/components/resizable/resizable.tsx +50 -0
  226. package/src/components/scroll-area/index.ts +1 -0
  227. package/src/components/scroll-area/scroll-area.tsx +53 -0
  228. package/src/components/select/index.ts +1 -0
  229. package/src/components/select/select.tsx +205 -0
  230. package/src/components/separator/index.ts +1 -0
  231. package/src/components/separator/separator.tsx +23 -0
  232. package/src/components/sheet/index.ts +1 -0
  233. package/src/components/sheet/sheet.tsx +137 -0
  234. package/src/components/sidebar/index.ts +1 -0
  235. package/src/components/sidebar/sidebar.tsx +722 -0
  236. package/src/components/skeleton/index.ts +1 -0
  237. package/src/components/skeleton/skeleton.tsx +13 -0
  238. package/src/components/slider/index.ts +1 -0
  239. package/src/components/slider/slider.tsx +52 -0
  240. package/src/components/sonner/index.ts +1 -0
  241. package/src/components/sonner/sonner.tsx +45 -0
  242. package/src/components/spinner/index.ts +1 -0
  243. package/src/components/spinner/spinner.tsx +15 -0
  244. package/src/components/switch/index.ts +1 -0
  245. package/src/components/switch/switch.tsx +30 -0
  246. package/src/components/table/index.ts +1 -0
  247. package/src/components/table/table.tsx +116 -0
  248. package/src/components/tabs/index.ts +1 -0
  249. package/src/components/tabs/tabs.tsx +80 -0
  250. package/src/components/textarea/index.ts +1 -0
  251. package/src/components/textarea/textarea.tsx +18 -0
  252. package/src/components/toggle/index.ts +1 -0
  253. package/src/components/toggle/toggle.tsx +45 -0
  254. package/src/components/toggle-group/index.ts +1 -0
  255. package/src/components/toggle-group/toggle-group.tsx +87 -0
  256. package/src/components/tooltip/index.ts +1 -0
  257. package/src/components/tooltip/tooltip.tsx +64 -0
  258. package/src/eslint/index.js +13 -0
  259. package/src/eslint/no-primitive-colors.js +158 -0
  260. package/src/hooks/use-mobile.ts +21 -0
  261. package/src/index.ts +58 -0
  262. package/src/lib/.gitkeep +0 -0
  263. package/src/lib/utils.ts +6 -0
  264. package/src/styles/globals.css +77 -0
  265. package/src/styles/nova-binding.css +290 -0
  266. package/src/styles/nova.css +58 -0
  267. package/src/styles/theme-nova.css +201 -0
  268. package/src/styles/typography.css +215 -0
  269. package/src/utils/index.ts +1 -0
  270. package/templates/component/component.tsx.hbs +16 -0
  271. package/templates/component/index.ts.hbs +1 -0
  272. package/templates/component/stories.tsx.hbs +13 -0
  273. package/tsconfig.json +18 -0
  274. package/tsconfig.lint.json +14 -0
  275. package/vite.config.ts +101 -0
@@ -0,0 +1,255 @@
1
+ ---
2
+ title: Button
3
+ description: Triggers an action or event when activated.
4
+ ---
5
+
6
+ import { Meta, Canvas, Controls } from '@storybook/blocks';
7
+ import * as ButtonStories from './button.stories';
8
+ import { Button, buttonVariants } from './button';
9
+ import { Spinner } from '../spinner';
10
+ import { ButtonGroup } from '../button-group';
11
+ import { MailIcon, ArrowRightIcon, PlusIcon } from 'lucide-react';
12
+
13
+ <Meta of={ButtonStories} />
14
+
15
+ # Button
16
+
17
+ Triggers an action or event when activated. Use buttons for form submissions, confirmations, and primary user actions.
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { Button } from "@enya-learning/nova/components/button";
23
+ ```
24
+
25
+ ```tsx
26
+ <Button variant="outline">Button</Button>
27
+ ```
28
+
29
+ ## Examples
30
+
31
+ ### Variants
32
+
33
+ Seven visual variants for different levels of emphasis.
34
+
35
+ <div className="my-4 flex flex-wrap items-center gap-2 rounded-xl border border-border bg-muted/30 p-8">
36
+ <Button variant="default">Default</Button>
37
+ <Button variant="accent">Accent</Button>
38
+ <Button variant="outline">Outline</Button>
39
+ <Button variant="secondary">Secondary</Button>
40
+ <Button variant="ghost">Ghost</Button>
41
+ <Button variant="destructive">Destructive</Button>
42
+ <Button variant="link">Link</Button>
43
+ </div>
44
+
45
+ <Canvas of={ButtonStories.AllVariants} />
46
+
47
+ ### Default
48
+
49
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
50
+ <Button>Button</Button>
51
+ </div>
52
+
53
+ <Canvas of={ButtonStories.Default} />
54
+
55
+ ### Accent
56
+
57
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
58
+ <Button variant="accent">Button</Button>
59
+ </div>
60
+
61
+ <Canvas of={ButtonStories.Accent} />
62
+
63
+ ### Outline
64
+
65
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
66
+ <Button variant="outline">Button</Button>
67
+ </div>
68
+
69
+ <Canvas of={ButtonStories.Outline} />
70
+
71
+ ### Secondary
72
+
73
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
74
+ <Button variant="secondary">Button</Button>
75
+ </div>
76
+
77
+ <Canvas of={ButtonStories.Secondary} />
78
+
79
+ ### Ghost
80
+
81
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
82
+ <Button variant="ghost">Button</Button>
83
+ </div>
84
+
85
+ <Canvas of={ButtonStories.Ghost} />
86
+
87
+ ### Destructive
88
+
89
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
90
+ <Button variant="destructive">Button</Button>
91
+ </div>
92
+
93
+ <Canvas of={ButtonStories.Destructive} />
94
+
95
+ ### Link
96
+
97
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
98
+ <Button variant="link">Button</Button>
99
+ </div>
100
+
101
+ <Canvas of={ButtonStories.Link} />
102
+
103
+ ### Size
104
+
105
+ Use the `size` prop to change the size of the button.
106
+
107
+ <div className="my-4 flex flex-wrap items-center gap-3 rounded-xl border border-border bg-muted/30 p-8">
108
+ <Button size="xs">Extra Small</Button>
109
+ <Button size="sm">Small</Button>
110
+ <Button size="default">Default</Button>
111
+ <Button size="lg">Large</Button>
112
+ <Button size="xl">Extra Large</Button>
113
+ </div>
114
+
115
+ <Canvas of={ButtonStories.AllSizes} />
116
+
117
+ ### Icon
118
+
119
+ Use `size="icon"` for square icon-only buttons. Always include an accessible `aria-label`.
120
+
121
+ ```tsx
122
+ <Button size="icon" aria-label="Add">
123
+ <PlusIcon />
124
+ </Button>
125
+ ```
126
+
127
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
128
+ <Button size="icon" aria-label="Add">
129
+ <PlusIcon />
130
+ </Button>
131
+ </div>
132
+
133
+ <Canvas of={ButtonStories.Icon} />
134
+
135
+ ### With Icon
136
+
137
+ Add `data-icon="inline-start"` or `data-icon="inline-end"` to the icon for correct spacing.
138
+
139
+ ```tsx
140
+ <Button>
141
+ <MailIcon data-icon="inline-start" />
142
+ Send email
143
+ </Button>
144
+
145
+ <Button>
146
+ Continue
147
+ <ArrowRightIcon data-icon="inline-end" />
148
+ </Button>
149
+ ```
150
+
151
+ <div className="my-4 flex flex-wrap items-center gap-3 rounded-xl border border-border bg-muted/30 p-8">
152
+ <Button><MailIcon data-icon="inline-start" /> Send email</Button>
153
+ <Button>Continue <ArrowRightIcon data-icon="inline-end" /></Button>
154
+ </div>
155
+
156
+ <Canvas of={ButtonStories.WithIconStart} />
157
+
158
+ <Canvas of={ButtonStories.WithIconEnd} />
159
+
160
+ ### Rounded
161
+
162
+ Add `className="rounded-full"` to make the button fully rounded.
163
+
164
+ ```tsx
165
+ <Button className="rounded-full">Rounded</Button>
166
+ ```
167
+
168
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
169
+ <Button className="rounded-full">Button</Button>
170
+ </div>
171
+
172
+ <Canvas of={ButtonStories.Rounded} />
173
+
174
+ ### Spinner
175
+
176
+ Render a `<Spinner />` inside the button to show a loading state. Add `data-icon="inline-start"` or `data-icon="inline-end"` for correct spacing.
177
+
178
+ ```tsx
179
+ <Button disabled>
180
+ <Spinner data-icon="inline-start" />
181
+ Processing
182
+ </Button>
183
+ ```
184
+
185
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
186
+ <Button disabled><Spinner data-icon="inline-start" /> Processing</Button>
187
+ </div>
188
+
189
+ <Canvas of={ButtonStories.WithSpinner} />
190
+
191
+ ### Disabled
192
+
193
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
194
+ <Button disabled>Button</Button>
195
+ </div>
196
+
197
+ <Canvas of={ButtonStories.Disabled} />
198
+
199
+ ### Button Group
200
+
201
+ To create a button group, use the `ButtonGroup` component. See the [Button Group](?path=/docs/components-button-group--docs) story for details.
202
+
203
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
204
+ <ButtonGroup>
205
+ <Button variant="outline">Back</Button>
206
+ <Button variant="outline">Cancel</Button>
207
+ <Button>Save</Button>
208
+ </ButtonGroup>
209
+ </div>
210
+
211
+ <Canvas of={ButtonStories.ButtonGroupDemo} />
212
+
213
+ ### As Link
214
+
215
+ Use the `buttonVariants` helper to style any element as a button. This is the correct approach for links — **do not** use `<Button render={<a />} />` because Base UI always applies `role="button"`, which overrides the semantic link role on `<a>` elements.
216
+
217
+ ```tsx
218
+ import { buttonVariants } from "@enya-learning/nova/components/button";
219
+
220
+ <a href="/dashboard" className={buttonVariants({ variant: "default" })}>
221
+ Go to Dashboard
222
+ </a>
223
+ ```
224
+
225
+ <div className="my-4 flex items-center justify-center rounded-xl border border-border bg-muted/30 p-8">
226
+ <a href="#" className={buttonVariants({ variant: "default" })}>Link as Button</a>
227
+ </div>
228
+
229
+ <Canvas of={ButtonStories.AsLink} />
230
+
231
+ ## API Reference
232
+
233
+ ### Button
234
+
235
+ | Prop | Type | Default |
236
+ |------|------|---------|
237
+ | `variant` | `"default" \| "accent" \| "outline" \| "secondary" \| "ghost" \| "destructive" \| "link"` | `"default"` |
238
+ | `size` | `"default" \| "xs" \| "sm" \| "lg" \| "xl" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg" \| "icon-xl"` | `"default"` |
239
+ | `disabled` | `boolean` | `false` |
240
+ | `className` | `string` | — |
241
+ | `children` | `React.ReactNode` | — |
242
+
243
+ All other props are forwarded to the underlying [Base UI Button](https://base-ui.com/components/button) element.
244
+
245
+ ### buttonVariants
246
+
247
+ ```tsx
248
+ import { buttonVariants } from "@enya-learning/nova/components/button";
249
+
250
+ buttonVariants({ variant?: ButtonVariant, size?: ButtonSize }): string
251
+ ```
252
+
253
+ Returns the computed class string for the given variant and size. Use this to style non-`<button>` elements (e.g. `<a>`, `<Link>`) with button appearance.
254
+
255
+ <Controls of={ButtonStories.Default} />
@@ -0,0 +1,59 @@
1
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "../../lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-4xl border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all duration-150 outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 active:not-aria-[haspopup]:scale-[0.97] disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ accent: "bg-accent text-primary-foreground hover:bg-accent/80",
12
+ default: "bg-foreground text-primary-foreground hover:bg-foreground/80",
13
+ outline:
14
+ "border-border bg-nova-white hover:bg-input/30 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
15
+ secondary:
16
+ "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
17
+ ghost:
18
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
19
+ destructive:
20
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default:
25
+ "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pe-2.5 has-data-[icon=inline-start]:ps-2.5",
26
+ xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2",
28
+ lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3",
29
+ xl: "h-15 gap-2 rounded-full px-5 text-2xl has-data-[icon=inline-end]:pe-4 has-data-[icon=inline-start]:ps-4",
30
+ icon: "size-9",
31
+ "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
32
+ "icon-sm": "size-8",
33
+ "icon-lg": "size-10",
34
+ "icon-xl": "size-12",
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ variant: "default",
39
+ size: "default",
40
+ },
41
+ }
42
+ )
43
+
44
+ function Button({
45
+ className,
46
+ variant = "default",
47
+ size = "default",
48
+ ...props
49
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
50
+ return (
51
+ <ButtonPrimitive
52
+ data-slot="button"
53
+ className={cn(buttonVariants({ variant, size, className }))}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ export { Button, buttonVariants }
@@ -0,0 +1 @@
1
+ export * from "./button"
@@ -0,0 +1,87 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "../../lib/utils"
6
+ import { Separator } from "../separator"
7
+
8
+ const buttonGroupVariants = cva(
9
+ "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-e-4xl [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
10
+ {
11
+ variants: {
12
+ orientation: {
13
+ horizontal:
14
+ "*:data-slot:rounded-e-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-e-4xl! [&>[data-slot]~[data-slot]]:rounded-s-none [&>[data-slot]~[data-slot]]:border-s-0",
15
+ vertical:
16
+ "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-4xl! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ orientation: "horizontal",
21
+ },
22
+ }
23
+ )
24
+
25
+ function ButtonGroup({
26
+ className,
27
+ orientation,
28
+ ...props
29
+ }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
30
+ return (
31
+ <div
32
+ role="group"
33
+ data-slot="button-group"
34
+ data-orientation={orientation}
35
+ className={cn(buttonGroupVariants({ orientation }), className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function ButtonGroupText({
42
+ className,
43
+ render,
44
+ ...props
45
+ }: useRender.ComponentProps<"div">) {
46
+ return useRender({
47
+ defaultTagName: "div",
48
+ props: mergeProps<"div">(
49
+ {
50
+ className: cn(
51
+ "flex items-center gap-2 rounded-4xl border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
52
+ className
53
+ ),
54
+ },
55
+ props
56
+ ),
57
+ render,
58
+ state: {
59
+ slot: "button-group-text",
60
+ },
61
+ })
62
+ }
63
+
64
+ function ButtonGroupSeparator({
65
+ className,
66
+ orientation = "vertical",
67
+ ...props
68
+ }: React.ComponentProps<typeof Separator>) {
69
+ return (
70
+ <Separator
71
+ data-slot="button-group-separator"
72
+ orientation={orientation}
73
+ className={cn(
74
+ "relative self-stretch bg-input data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
75
+ className
76
+ )}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ export {
83
+ ButtonGroup,
84
+ ButtonGroupSeparator,
85
+ ButtonGroupText,
86
+ buttonGroupVariants,
87
+ }
@@ -0,0 +1 @@
1
+ export * from "./button-group"
@@ -0,0 +1,228 @@
1
+ import * as React from "react"
2
+ import {
3
+ DayPicker,
4
+ getDefaultClassNames,
5
+ type DayButton,
6
+ type Locale,
7
+ } from "react-day-picker"
8
+
9
+ import { cn } from "../../lib/utils"
10
+ import { Button, buttonVariants } from "../button"
11
+ import {
12
+ ChevronLeftIcon,
13
+ ChevronRightIcon,
14
+ ChevronDownIcon,
15
+ } from "lucide-react"
16
+
17
+ function Calendar({
18
+ className,
19
+ classNames,
20
+ showOutsideDays = true,
21
+ captionLayout = "label",
22
+ buttonVariant = "ghost",
23
+ locale,
24
+ formatters,
25
+ components,
26
+ ...props
27
+ }: React.ComponentProps<typeof DayPicker> & {
28
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"]
29
+ }) {
30
+ const defaultClassNames = getDefaultClassNames()
31
+
32
+ return (
33
+ <DayPicker
34
+ showOutsideDays={showOutsideDays}
35
+ className={cn(
36
+ "group/calendar bg-background p-3 [--cell-radius:var(--radius-4xl)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
37
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
38
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
39
+ className
40
+ )}
41
+ captionLayout={captionLayout}
42
+ locale={locale}
43
+ formatters={{
44
+ formatMonthDropdown: (date) =>
45
+ date.toLocaleString(locale?.code, { month: "short" }),
46
+ ...formatters,
47
+ }}
48
+ classNames={{
49
+ root: cn("w-fit", defaultClassNames.root),
50
+ months: cn(
51
+ "relative flex flex-col gap-4 md:flex-row",
52
+ defaultClassNames.months
53
+ ),
54
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
55
+ nav: cn(
56
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
57
+ defaultClassNames.nav
58
+ ),
59
+ button_previous: cn(
60
+ buttonVariants({ variant: buttonVariant }),
61
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
62
+ defaultClassNames.button_previous
63
+ ),
64
+ button_next: cn(
65
+ buttonVariants({ variant: buttonVariant }),
66
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
67
+ defaultClassNames.button_next
68
+ ),
69
+ month_caption: cn(
70
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
71
+ defaultClassNames.month_caption
72
+ ),
73
+ dropdowns: cn(
74
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
75
+ defaultClassNames.dropdowns
76
+ ),
77
+ dropdown_root: cn(
78
+ "relative rounded-(--cell-radius)",
79
+ defaultClassNames.dropdown_root
80
+ ),
81
+ dropdown: cn(
82
+ "absolute inset-0 bg-popover opacity-0",
83
+ defaultClassNames.dropdown
84
+ ),
85
+ caption_label: cn(
86
+ "font-medium select-none",
87
+ captionLayout === "label"
88
+ ? "text-sm"
89
+ : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
90
+ defaultClassNames.caption_label
91
+ ),
92
+ weekdays: cn("flex", defaultClassNames.weekdays),
93
+ weekday: cn(
94
+ "flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
95
+ defaultClassNames.weekday
96
+ ),
97
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
98
+ week_number_header: cn(
99
+ "w-(--cell-size) select-none",
100
+ defaultClassNames.week_number_header
101
+ ),
102
+ week_number: cn(
103
+ "text-[0.8rem] text-muted-foreground select-none",
104
+ defaultClassNames.week_number
105
+ ),
106
+ day: cn(
107
+ "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)",
108
+ props.showWeekNumber
109
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)"
110
+ : "[&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)",
111
+ defaultClassNames.day
112
+ ),
113
+ range_start: cn(
114
+ "relative isolate z-0 rounded-s-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:end-0 after:w-4 after:bg-muted",
115
+ defaultClassNames.range_start
116
+ ),
117
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
118
+ range_end: cn(
119
+ "relative isolate z-0 rounded-e-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:start-0 after:w-4 after:bg-muted",
120
+ defaultClassNames.range_end
121
+ ),
122
+ today: cn(
123
+ "rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
124
+ defaultClassNames.today
125
+ ),
126
+ outside: cn(
127
+ "text-muted-foreground aria-selected:text-muted-foreground",
128
+ defaultClassNames.outside
129
+ ),
130
+ disabled: cn(
131
+ "text-muted-foreground opacity-50",
132
+ defaultClassNames.disabled
133
+ ),
134
+ hidden: cn("invisible", defaultClassNames.hidden),
135
+ ...classNames,
136
+ }}
137
+ components={{
138
+ Root: ({ className, rootRef, ...props }) => {
139
+ return (
140
+ <div
141
+ data-slot="calendar"
142
+ ref={rootRef}
143
+ className={cn(className)}
144
+ {...props}
145
+ />
146
+ )
147
+ },
148
+ Chevron: ({ className, orientation, ...props }) => {
149
+ if (orientation === "left") {
150
+ return (
151
+ <ChevronLeftIcon
152
+ className={cn("size-4 rtl:rotate-180", className)}
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ if (orientation === "right") {
159
+ return (
160
+ <ChevronRightIcon
161
+ className={cn("size-4 rtl:rotate-180", className)}
162
+ {...props}
163
+ />
164
+ )
165
+ }
166
+
167
+ return (
168
+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
169
+ )
170
+ },
171
+ DayButton: ({ ...props }) => (
172
+ <CalendarDayButton locale={locale} {...props} />
173
+ ),
174
+ WeekNumber: ({ children, ...props }) => {
175
+ return (
176
+ <td {...props}>
177
+ <div className="flex size-(--cell-size) items-center justify-center text-center">
178
+ {children}
179
+ </div>
180
+ </td>
181
+ )
182
+ },
183
+ ...components,
184
+ }}
185
+ {...props}
186
+ />
187
+ )
188
+ }
189
+
190
+ function CalendarDayButton({
191
+ className,
192
+ day,
193
+ modifiers,
194
+ locale,
195
+ ...props
196
+ }: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
197
+ const defaultClassNames = getDefaultClassNames()
198
+
199
+ const ref = React.useRef<HTMLButtonElement>(null)
200
+ React.useEffect(() => {
201
+ if (modifiers.focused) ref.current?.focus()
202
+ }, [modifiers.focused])
203
+
204
+ return (
205
+ <Button
206
+ variant="ghost"
207
+ size="icon"
208
+ data-day={day.date.toLocaleDateString(locale?.code)}
209
+ data-selected-single={
210
+ modifiers.selected &&
211
+ !modifiers.range_start &&
212
+ !modifiers.range_end &&
213
+ !modifiers.range_middle
214
+ }
215
+ data-range-start={modifiers.range_start}
216
+ data-range-end={modifiers.range_end}
217
+ data-range-middle={modifiers.range_middle}
218
+ className={cn(
219
+ "relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-e-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-s-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70",
220
+ defaultClassNames.day,
221
+ className
222
+ )}
223
+ {...props}
224
+ />
225
+ )
226
+ }
227
+
228
+ export { Calendar, CalendarDayButton }
@@ -0,0 +1 @@
1
+ export * from "./calendar"