@pickle-ui/react 0.0.0-stage → 1.0.0-beta.3

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 (560) hide show
  1. package/README.md +490 -2
  2. package/dist/components/Alert.d.ts +38 -0
  3. package/dist/components/Alert.d.ts.map +1 -0
  4. package/dist/components/Alert.js +54 -0
  5. package/dist/components/Alert.js.map +1 -0
  6. package/dist/components/AlertDialog.d.ts +41 -0
  7. package/dist/components/AlertDialog.d.ts.map +1 -0
  8. package/dist/components/AlertDialog.js +47 -0
  9. package/dist/components/AlertDialog.js.map +1 -0
  10. package/dist/components/Avatar.d.ts +18 -0
  11. package/dist/components/Avatar.d.ts.map +1 -0
  12. package/dist/components/Avatar.js +34 -0
  13. package/dist/components/Avatar.js.map +1 -0
  14. package/dist/components/Badge.d.ts +12 -0
  15. package/dist/components/Badge.d.ts.map +1 -0
  16. package/dist/components/Badge.js +37 -0
  17. package/dist/components/Badge.js.map +1 -0
  18. package/dist/components/Button.d.ts +19 -0
  19. package/dist/components/Button.d.ts.map +1 -0
  20. package/dist/components/Button.js +66 -0
  21. package/dist/components/Button.js.map +1 -0
  22. package/dist/components/Card.d.ts +27 -0
  23. package/dist/components/Card.d.ts.map +1 -0
  24. package/dist/components/Card.js +34 -0
  25. package/dist/components/Card.js.map +1 -0
  26. package/dist/components/Dialog.d.ts +38 -0
  27. package/dist/components/Dialog.d.ts.map +1 -0
  28. package/dist/components/Dialog.js +41 -0
  29. package/dist/components/Dialog.js.map +1 -0
  30. package/dist/components/Flex.d.ts +22 -0
  31. package/dist/components/Flex.d.ts.map +1 -0
  32. package/dist/components/Flex.js +20 -0
  33. package/dist/components/Flex.js.map +1 -0
  34. package/dist/components/Grid.d.ts +66 -0
  35. package/dist/components/Grid.d.ts.map +1 -0
  36. package/dist/components/Grid.js +63 -0
  37. package/dist/components/Grid.js.map +1 -0
  38. package/dist/components/Group.d.ts +20 -0
  39. package/dist/components/Group.d.ts.map +1 -0
  40. package/dist/components/Group.js +43 -0
  41. package/dist/components/Group.js.map +1 -0
  42. package/dist/components/Menu.d.ts +53 -0
  43. package/dist/components/Menu.d.ts.map +1 -0
  44. package/dist/components/Menu.js +78 -0
  45. package/dist/components/Menu.js.map +1 -0
  46. package/dist/components/Popover.d.ts +27 -0
  47. package/dist/components/Popover.d.ts.map +1 -0
  48. package/dist/components/Popover.js +34 -0
  49. package/dist/components/Popover.js.map +1 -0
  50. package/dist/components/Progress.d.ts +23 -0
  51. package/dist/components/Progress.d.ts.map +1 -0
  52. package/dist/components/Progress.js +26 -0
  53. package/dist/components/Progress.js.map +1 -0
  54. package/dist/components/ScrollArea.d.ts +32 -0
  55. package/dist/components/ScrollArea.d.ts.map +1 -0
  56. package/dist/components/ScrollArea.js +36 -0
  57. package/dist/components/ScrollArea.js.map +1 -0
  58. package/dist/components/Separator.d.ts +7 -0
  59. package/dist/components/Separator.d.ts.map +1 -0
  60. package/dist/components/Separator.js +8 -0
  61. package/dist/components/Separator.js.map +1 -0
  62. package/dist/components/Sidebar.d.ts +35 -0
  63. package/dist/components/Sidebar.d.ts.map +1 -0
  64. package/dist/components/Sidebar.js +45 -0
  65. package/dist/components/Sidebar.js.map +1 -0
  66. package/dist/components/Spinner.d.ts +11 -0
  67. package/dist/components/Spinner.d.ts.map +1 -0
  68. package/dist/components/Spinner.js +13 -0
  69. package/dist/components/Spinner.js.map +1 -0
  70. package/dist/components/Table.d.ts +32 -0
  71. package/dist/components/Table.d.ts.map +1 -0
  72. package/dist/components/Table.js +37 -0
  73. package/dist/components/Table.js.map +1 -0
  74. package/dist/components/Text.d.ts +14 -0
  75. package/dist/components/Text.d.ts.map +1 -0
  76. package/dist/components/Text.js +50 -0
  77. package/dist/components/Text.js.map +1 -0
  78. package/dist/components/Toast.d.ts +39 -0
  79. package/dist/components/Toast.d.ts.map +1 -0
  80. package/dist/components/Toast.js +97 -0
  81. package/dist/components/Toast.js.map +1 -0
  82. package/dist/components/Tooltip.d.ts +18 -0
  83. package/dist/components/Tooltip.d.ts.map +1 -0
  84. package/dist/components/Tooltip.js +22 -0
  85. package/dist/components/Tooltip.js.map +1 -0
  86. package/dist/components/XStack.d.ts +6 -0
  87. package/dist/components/XStack.d.ts.map +1 -0
  88. package/dist/components/XStack.js +8 -0
  89. package/dist/components/XStack.js.map +1 -0
  90. package/dist/components/YStack.d.ts +6 -0
  91. package/dist/components/YStack.d.ts.map +1 -0
  92. package/dist/components/YStack.js +8 -0
  93. package/dist/components/YStack.js.map +1 -0
  94. package/dist/components/_internal/VisuallyHiddenInput.d.ts +11 -0
  95. package/dist/components/_internal/VisuallyHiddenInput.d.ts.map +1 -0
  96. package/dist/components/_internal/VisuallyHiddenInput.js +105 -0
  97. package/dist/components/_internal/VisuallyHiddenInput.js.map +1 -0
  98. package/dist/components/_internal/dialog-styles.d.ts +15 -0
  99. package/dist/components/_internal/dialog-styles.d.ts.map +1 -0
  100. package/dist/components/_internal/dialog-styles.js +27 -0
  101. package/dist/components/_internal/dialog-styles.js.map +1 -0
  102. package/dist/components/_internal/layout.d.ts +46 -0
  103. package/dist/components/_internal/layout.d.ts.map +1 -0
  104. package/dist/components/_internal/layout.js +72 -0
  105. package/dist/components/_internal/layout.js.map +1 -0
  106. package/dist/components/form/Checkbox.d.ts +19 -0
  107. package/dist/components/form/Checkbox.d.ts.map +1 -0
  108. package/dist/components/form/Checkbox.js +27 -0
  109. package/dist/components/form/Checkbox.js.map +1 -0
  110. package/dist/components/form/ColorPicker.d.ts +25 -0
  111. package/dist/components/form/ColorPicker.d.ts.map +1 -0
  112. package/dist/components/form/ColorPicker.js +14 -0
  113. package/dist/components/form/ColorPicker.js.map +1 -0
  114. package/dist/components/form/ColorPickerPrimitive.d.ts +65 -0
  115. package/dist/components/form/ColorPickerPrimitive.d.ts.map +1 -0
  116. package/dist/components/form/ColorPickerPrimitive.js +486 -0
  117. package/dist/components/form/ColorPickerPrimitive.js.map +1 -0
  118. package/dist/components/form/Combobox.d.ts +86 -0
  119. package/dist/components/form/Combobox.d.ts.map +1 -0
  120. package/dist/components/form/Combobox.js +198 -0
  121. package/dist/components/form/Combobox.js.map +1 -0
  122. package/dist/components/form/Form.context.d.ts +28 -0
  123. package/dist/components/form/Form.context.d.ts.map +1 -0
  124. package/dist/components/form/Form.context.js +56 -0
  125. package/dist/components/form/Form.context.js.map +1 -0
  126. package/dist/components/form/Form.d.ts +68 -0
  127. package/dist/components/form/Form.d.ts.map +1 -0
  128. package/dist/components/form/Form.js +32 -0
  129. package/dist/components/form/Form.js.map +1 -0
  130. package/dist/components/form/FormCheckbox.d.ts +10 -0
  131. package/dist/components/form/FormCheckbox.d.ts.map +1 -0
  132. package/dist/components/form/FormCheckbox.js +24 -0
  133. package/dist/components/form/FormCheckbox.js.map +1 -0
  134. package/dist/components/form/FormCombobox.d.ts +28 -0
  135. package/dist/components/form/FormCombobox.d.ts.map +1 -0
  136. package/dist/components/form/FormCombobox.js +50 -0
  137. package/dist/components/form/FormCombobox.js.map +1 -0
  138. package/dist/components/form/FormDescription.d.ts +7 -0
  139. package/dist/components/form/FormDescription.d.ts.map +1 -0
  140. package/dist/components/form/FormDescription.js +9 -0
  141. package/dist/components/form/FormDescription.js.map +1 -0
  142. package/dist/components/form/FormError.d.ts +7 -0
  143. package/dist/components/form/FormError.d.ts.map +1 -0
  144. package/dist/components/form/FormError.js +9 -0
  145. package/dist/components/form/FormError.js.map +1 -0
  146. package/dist/components/form/FormField.d.ts +13 -0
  147. package/dist/components/form/FormField.d.ts.map +1 -0
  148. package/dist/components/form/FormField.js +45 -0
  149. package/dist/components/form/FormField.js.map +1 -0
  150. package/dist/components/form/FormInput.d.ts +8 -0
  151. package/dist/components/form/FormInput.d.ts.map +1 -0
  152. package/dist/components/form/FormInput.js +17 -0
  153. package/dist/components/form/FormInput.js.map +1 -0
  154. package/dist/components/form/FormInputGroup.d.ts +7 -0
  155. package/dist/components/form/FormInputGroup.d.ts.map +1 -0
  156. package/dist/components/form/FormInputGroup.js +30 -0
  157. package/dist/components/form/FormInputGroup.js.map +1 -0
  158. package/dist/components/form/FormLabel.d.ts +7 -0
  159. package/dist/components/form/FormLabel.d.ts.map +1 -0
  160. package/dist/components/form/FormLabel.js +9 -0
  161. package/dist/components/form/FormLabel.js.map +1 -0
  162. package/dist/components/form/FormRadioGroup.d.ts +14 -0
  163. package/dist/components/form/FormRadioGroup.d.ts.map +1 -0
  164. package/dist/components/form/FormRadioGroup.js +25 -0
  165. package/dist/components/form/FormRadioGroup.js.map +1 -0
  166. package/dist/components/form/FormRoot.d.ts +10 -0
  167. package/dist/components/form/FormRoot.d.ts.map +1 -0
  168. package/dist/components/form/FormRoot.js +10 -0
  169. package/dist/components/form/FormRoot.js.map +1 -0
  170. package/dist/components/form/FormSelect.d.ts +19 -0
  171. package/dist/components/form/FormSelect.d.ts.map +1 -0
  172. package/dist/components/form/FormSelect.js +34 -0
  173. package/dist/components/form/FormSelect.js.map +1 -0
  174. package/dist/components/form/FormSwitch.d.ts +10 -0
  175. package/dist/components/form/FormSwitch.d.ts.map +1 -0
  176. package/dist/components/form/FormSwitch.js +24 -0
  177. package/dist/components/form/FormSwitch.js.map +1 -0
  178. package/dist/components/form/FormTextarea.d.ts +8 -0
  179. package/dist/components/form/FormTextarea.d.ts.map +1 -0
  180. package/dist/components/form/FormTextarea.js +13 -0
  181. package/dist/components/form/FormTextarea.js.map +1 -0
  182. package/dist/components/form/Input.d.ts +14 -0
  183. package/dist/components/form/Input.d.ts.map +1 -0
  184. package/dist/components/form/Input.js +22 -0
  185. package/dist/components/form/Input.js.map +1 -0
  186. package/dist/components/form/InputGroup.d.ts +26 -0
  187. package/dist/components/form/InputGroup.d.ts.map +1 -0
  188. package/dist/components/form/InputGroup.js +58 -0
  189. package/dist/components/form/InputGroup.js.map +1 -0
  190. package/dist/components/form/RadioGroup.d.ts +23 -0
  191. package/dist/components/form/RadioGroup.d.ts.map +1 -0
  192. package/dist/components/form/RadioGroup.js +30 -0
  193. package/dist/components/form/RadioGroup.js.map +1 -0
  194. package/dist/components/form/Select.d.ts +42 -0
  195. package/dist/components/form/Select.d.ts.map +1 -0
  196. package/dist/components/form/Select.js +68 -0
  197. package/dist/components/form/Select.js.map +1 -0
  198. package/dist/components/form/Slider.d.ts +37 -0
  199. package/dist/components/form/Slider.d.ts.map +1 -0
  200. package/dist/components/form/Slider.js +35 -0
  201. package/dist/components/form/Slider.js.map +1 -0
  202. package/dist/components/form/Switch.d.ts +23 -0
  203. package/dist/components/form/Switch.d.ts.map +1 -0
  204. package/dist/components/form/Switch.js +21 -0
  205. package/dist/components/form/Switch.js.map +1 -0
  206. package/dist/components/form/Textarea.d.ts +16 -0
  207. package/dist/components/form/Textarea.d.ts.map +1 -0
  208. package/dist/components/form/Textarea.js +12 -0
  209. package/dist/components/form/Textarea.js.map +1 -0
  210. package/dist/entries/alert-dialog.d.ts +3 -0
  211. package/dist/entries/alert-dialog.d.ts.map +1 -0
  212. package/dist/entries/alert-dialog.js +2 -0
  213. package/dist/entries/alert-dialog.js.map +1 -0
  214. package/dist/entries/alert.d.ts +3 -0
  215. package/dist/entries/alert.d.ts.map +1 -0
  216. package/dist/entries/alert.js +2 -0
  217. package/dist/entries/alert.js.map +1 -0
  218. package/dist/entries/avatar.d.ts +3 -0
  219. package/dist/entries/avatar.d.ts.map +1 -0
  220. package/dist/entries/avatar.js +2 -0
  221. package/dist/entries/avatar.js.map +1 -0
  222. package/dist/entries/badge.d.ts +3 -0
  223. package/dist/entries/badge.d.ts.map +1 -0
  224. package/dist/entries/badge.js +2 -0
  225. package/dist/entries/badge.js.map +1 -0
  226. package/dist/entries/button.d.ts +3 -0
  227. package/dist/entries/button.d.ts.map +1 -0
  228. package/dist/entries/button.js +2 -0
  229. package/dist/entries/button.js.map +1 -0
  230. package/dist/entries/card.d.ts +3 -0
  231. package/dist/entries/card.d.ts.map +1 -0
  232. package/dist/entries/card.js +2 -0
  233. package/dist/entries/card.js.map +1 -0
  234. package/dist/entries/checkbox.d.ts +3 -0
  235. package/dist/entries/checkbox.d.ts.map +1 -0
  236. package/dist/entries/checkbox.js +2 -0
  237. package/dist/entries/checkbox.js.map +1 -0
  238. package/dist/entries/cn.d.ts +3 -0
  239. package/dist/entries/cn.d.ts.map +1 -0
  240. package/dist/entries/cn.js +2 -0
  241. package/dist/entries/cn.js.map +1 -0
  242. package/dist/entries/color-picker.d.ts +3 -0
  243. package/dist/entries/color-picker.d.ts.map +1 -0
  244. package/dist/entries/color-picker.js +2 -0
  245. package/dist/entries/color-picker.js.map +1 -0
  246. package/dist/entries/combobox.d.ts +3 -0
  247. package/dist/entries/combobox.d.ts.map +1 -0
  248. package/dist/entries/combobox.js +2 -0
  249. package/dist/entries/combobox.js.map +1 -0
  250. package/dist/entries/dialog.d.ts +3 -0
  251. package/dist/entries/dialog.d.ts.map +1 -0
  252. package/dist/entries/dialog.js +2 -0
  253. package/dist/entries/dialog.js.map +1 -0
  254. package/dist/entries/flex.d.ts +3 -0
  255. package/dist/entries/flex.d.ts.map +1 -0
  256. package/dist/entries/flex.js +2 -0
  257. package/dist/entries/flex.js.map +1 -0
  258. package/dist/entries/form.d.ts +3 -0
  259. package/dist/entries/form.d.ts.map +1 -0
  260. package/dist/entries/form.js +2 -0
  261. package/dist/entries/form.js.map +1 -0
  262. package/dist/entries/grid.d.ts +3 -0
  263. package/dist/entries/grid.d.ts.map +1 -0
  264. package/dist/entries/grid.js +2 -0
  265. package/dist/entries/grid.js.map +1 -0
  266. package/dist/entries/group.d.ts +3 -0
  267. package/dist/entries/group.d.ts.map +1 -0
  268. package/dist/entries/group.js +2 -0
  269. package/dist/entries/group.js.map +1 -0
  270. package/dist/entries/input-group.d.ts +3 -0
  271. package/dist/entries/input-group.d.ts.map +1 -0
  272. package/dist/entries/input-group.js +2 -0
  273. package/dist/entries/input-group.js.map +1 -0
  274. package/dist/entries/input.d.ts +3 -0
  275. package/dist/entries/input.d.ts.map +1 -0
  276. package/dist/entries/input.js +2 -0
  277. package/dist/entries/input.js.map +1 -0
  278. package/dist/entries/menu.d.ts +3 -0
  279. package/dist/entries/menu.d.ts.map +1 -0
  280. package/dist/entries/menu.js +2 -0
  281. package/dist/entries/menu.js.map +1 -0
  282. package/dist/entries/popover.d.ts +3 -0
  283. package/dist/entries/popover.d.ts.map +1 -0
  284. package/dist/entries/popover.js +2 -0
  285. package/dist/entries/popover.js.map +1 -0
  286. package/dist/entries/progress.d.ts +3 -0
  287. package/dist/entries/progress.d.ts.map +1 -0
  288. package/dist/entries/progress.js +2 -0
  289. package/dist/entries/progress.js.map +1 -0
  290. package/dist/entries/radio-group.d.ts +3 -0
  291. package/dist/entries/radio-group.d.ts.map +1 -0
  292. package/dist/entries/radio-group.js +2 -0
  293. package/dist/entries/radio-group.js.map +1 -0
  294. package/dist/entries/scroll-area.d.ts +3 -0
  295. package/dist/entries/scroll-area.d.ts.map +1 -0
  296. package/dist/entries/scroll-area.js +2 -0
  297. package/dist/entries/scroll-area.js.map +1 -0
  298. package/dist/entries/select.d.ts +3 -0
  299. package/dist/entries/select.d.ts.map +1 -0
  300. package/dist/entries/select.js +2 -0
  301. package/dist/entries/select.js.map +1 -0
  302. package/dist/entries/separator.d.ts +3 -0
  303. package/dist/entries/separator.d.ts.map +1 -0
  304. package/dist/entries/separator.js +2 -0
  305. package/dist/entries/separator.js.map +1 -0
  306. package/dist/entries/sidebar.d.ts +3 -0
  307. package/dist/entries/sidebar.d.ts.map +1 -0
  308. package/dist/entries/sidebar.js +2 -0
  309. package/dist/entries/sidebar.js.map +1 -0
  310. package/dist/entries/slider.d.ts +3 -0
  311. package/dist/entries/slider.d.ts.map +1 -0
  312. package/dist/entries/slider.js +2 -0
  313. package/dist/entries/slider.js.map +1 -0
  314. package/dist/entries/spinner.d.ts +3 -0
  315. package/dist/entries/spinner.d.ts.map +1 -0
  316. package/dist/entries/spinner.js +2 -0
  317. package/dist/entries/spinner.js.map +1 -0
  318. package/dist/entries/switch.d.ts +3 -0
  319. package/dist/entries/switch.d.ts.map +1 -0
  320. package/dist/entries/switch.js +2 -0
  321. package/dist/entries/switch.js.map +1 -0
  322. package/dist/entries/table.d.ts +3 -0
  323. package/dist/entries/table.d.ts.map +1 -0
  324. package/dist/entries/table.js +2 -0
  325. package/dist/entries/table.js.map +1 -0
  326. package/dist/entries/text.d.ts +3 -0
  327. package/dist/entries/text.d.ts.map +1 -0
  328. package/dist/entries/text.js +2 -0
  329. package/dist/entries/text.js.map +1 -0
  330. package/dist/entries/textarea.d.ts +3 -0
  331. package/dist/entries/textarea.d.ts.map +1 -0
  332. package/dist/entries/textarea.js +2 -0
  333. package/dist/entries/textarea.js.map +1 -0
  334. package/dist/entries/theme.d.ts +3 -0
  335. package/dist/entries/theme.d.ts.map +1 -0
  336. package/dist/entries/theme.js +2 -0
  337. package/dist/entries/theme.js.map +1 -0
  338. package/dist/entries/toast.d.ts +3 -0
  339. package/dist/entries/toast.d.ts.map +1 -0
  340. package/dist/entries/toast.js +2 -0
  341. package/dist/entries/toast.js.map +1 -0
  342. package/dist/entries/tooltip.d.ts +3 -0
  343. package/dist/entries/tooltip.d.ts.map +1 -0
  344. package/dist/entries/tooltip.js +2 -0
  345. package/dist/entries/tooltip.js.map +1 -0
  346. package/dist/entries/x-stack.d.ts +3 -0
  347. package/dist/entries/x-stack.d.ts.map +1 -0
  348. package/dist/entries/x-stack.js +2 -0
  349. package/dist/entries/x-stack.js.map +1 -0
  350. package/dist/entries/y-stack.d.ts +3 -0
  351. package/dist/entries/y-stack.d.ts.map +1 -0
  352. package/dist/entries/y-stack.js +2 -0
  353. package/dist/entries/y-stack.js.map +1 -0
  354. package/dist/hooks/use-as-ref.d.ts +4 -0
  355. package/dist/hooks/use-as-ref.d.ts.map +1 -0
  356. package/dist/hooks/use-as-ref.js +11 -0
  357. package/dist/hooks/use-as-ref.js.map +1 -0
  358. package/dist/hooks/use-isomorphic-layout-effect.d.ts +4 -0
  359. package/dist/hooks/use-isomorphic-layout-effect.d.ts.map +1 -0
  360. package/dist/hooks/use-isomorphic-layout-effect.js +4 -0
  361. package/dist/hooks/use-isomorphic-layout-effect.js.map +1 -0
  362. package/dist/hooks/use-lazy-ref.d.ts +4 -0
  363. package/dist/hooks/use-lazy-ref.d.ts.map +1 -0
  364. package/dist/hooks/use-lazy-ref.js +10 -0
  365. package/dist/hooks/use-lazy-ref.js.map +1 -0
  366. package/dist/index.d.ts +37 -0
  367. package/dist/index.d.ts.map +1 -0
  368. package/dist/index.js +39 -0
  369. package/dist/index.js.map +1 -0
  370. package/dist/lib/color.d.ts +42 -0
  371. package/dist/lib/color.d.ts.map +1 -0
  372. package/dist/lib/color.js +140 -0
  373. package/dist/lib/color.js.map +1 -0
  374. package/dist/lib/compose-refs.d.ts +14 -0
  375. package/dist/lib/compose-refs.d.ts.map +1 -0
  376. package/dist/lib/compose-refs.js +57 -0
  377. package/dist/lib/compose-refs.js.map +1 -0
  378. package/dist/lib/theme.d.ts +40 -0
  379. package/dist/lib/theme.d.ts.map +1 -0
  380. package/dist/lib/theme.js +82 -0
  381. package/dist/lib/theme.js.map +1 -0
  382. package/dist/lib/utils.d.ts +8 -0
  383. package/dist/lib/utils.d.ts.map +1 -0
  384. package/dist/lib/utils.js +18 -0
  385. package/dist/lib/utils.js.map +1 -0
  386. package/dist/test-setup.d.ts +2 -0
  387. package/dist/test-setup.d.ts.map +1 -0
  388. package/dist/test-setup.js +2 -0
  389. package/dist/test-setup.js.map +1 -0
  390. package/package.json +107 -4
  391. package/src/base.css +99 -0
  392. package/src/components/Alert.stories.tsx +124 -0
  393. package/src/components/Alert.test.tsx +76 -0
  394. package/src/components/Alert.tsx +133 -0
  395. package/src/components/AlertDialog.stories.tsx +184 -0
  396. package/src/components/AlertDialog.test.tsx +142 -0
  397. package/src/components/AlertDialog.tsx +164 -0
  398. package/src/components/Avatar.stories.tsx +92 -0
  399. package/src/components/Avatar.test.tsx +26 -0
  400. package/src/components/Avatar.tsx +96 -0
  401. package/src/components/Badge.stories.tsx +52 -0
  402. package/src/components/Badge.test.tsx +35 -0
  403. package/src/components/Badge.tsx +65 -0
  404. package/src/components/Button.stories.tsx +171 -0
  405. package/src/components/Button.test.tsx +112 -0
  406. package/src/components/Button.tsx +114 -0
  407. package/src/components/Card.stories.tsx +62 -0
  408. package/src/components/Card.test.tsx +23 -0
  409. package/src/components/Card.tsx +129 -0
  410. package/src/components/Dialog.stories.tsx +133 -0
  411. package/src/components/Dialog.test.tsx +95 -0
  412. package/src/components/Dialog.tsx +147 -0
  413. package/src/components/Flex.stories.tsx +70 -0
  414. package/src/components/Flex.test.tsx +53 -0
  415. package/src/components/Flex.tsx +59 -0
  416. package/src/components/Grid.stories.tsx +70 -0
  417. package/src/components/Grid.test.tsx +46 -0
  418. package/src/components/Grid.tsx +114 -0
  419. package/src/components/Group.stories.tsx +130 -0
  420. package/src/components/Group.test.tsx +57 -0
  421. package/src/components/Group.tsx +92 -0
  422. package/src/components/Menu.stories.tsx +139 -0
  423. package/src/components/Menu.test.tsx +103 -0
  424. package/src/components/Menu.tsx +290 -0
  425. package/src/components/Popover.stories.tsx +72 -0
  426. package/src/components/Popover.tsx +117 -0
  427. package/src/components/Progress.stories.tsx +105 -0
  428. package/src/components/Progress.test.tsx +28 -0
  429. package/src/components/Progress.tsx +111 -0
  430. package/src/components/ScrollArea.stories.tsx +66 -0
  431. package/src/components/ScrollArea.test.tsx +32 -0
  432. package/src/components/ScrollArea.tsx +160 -0
  433. package/src/components/Separator.stories.tsx +39 -0
  434. package/src/components/Separator.test.tsx +24 -0
  435. package/src/components/Separator.tsx +27 -0
  436. package/src/components/Sidebar.stories.tsx +87 -0
  437. package/src/components/Sidebar.tsx +166 -0
  438. package/src/components/Spinner.stories.tsx +36 -0
  439. package/src/components/Spinner.test.tsx +25 -0
  440. package/src/components/Spinner.tsx +31 -0
  441. package/src/components/Table.stories.tsx +76 -0
  442. package/src/components/Table.tsx +151 -0
  443. package/src/components/Text.stories.tsx +148 -0
  444. package/src/components/Text.test.tsx +106 -0
  445. package/src/components/Text.tsx +85 -0
  446. package/src/components/Toast.stories.tsx +166 -0
  447. package/src/components/Toast.test.tsx +97 -0
  448. package/src/components/Toast.tsx +270 -0
  449. package/src/components/Tooltip.stories.tsx +116 -0
  450. package/src/components/Tooltip.test.tsx +51 -0
  451. package/src/components/Tooltip.tsx +80 -0
  452. package/src/components/XStack.stories.tsx +64 -0
  453. package/src/components/XStack.test.tsx +33 -0
  454. package/src/components/XStack.tsx +19 -0
  455. package/src/components/YStack.stories.tsx +65 -0
  456. package/src/components/YStack.test.tsx +31 -0
  457. package/src/components/YStack.tsx +19 -0
  458. package/src/components/_internal/Story.tsx +161 -0
  459. package/src/components/_internal/VisuallyHiddenInput.tsx +164 -0
  460. package/src/components/_internal/dialog-styles.ts +57 -0
  461. package/src/components/_internal/layout.ts +103 -0
  462. package/src/components/form/Checkbox.stories.tsx +78 -0
  463. package/src/components/form/Checkbox.test.tsx +107 -0
  464. package/src/components/form/Checkbox.tsx +95 -0
  465. package/src/components/form/ColorPicker.stories.tsx +160 -0
  466. package/src/components/form/ColorPicker.test.tsx +45 -0
  467. package/src/components/form/ColorPicker.tsx +55 -0
  468. package/src/components/form/ColorPickerPrimitive.test.tsx +14 -0
  469. package/src/components/form/ColorPickerPrimitive.tsx +1283 -0
  470. package/src/components/form/Combobox.stories.tsx +378 -0
  471. package/src/components/form/Combobox.test.tsx +334 -0
  472. package/src/components/form/Combobox.tsx +667 -0
  473. package/src/components/form/Form.context.ts +114 -0
  474. package/src/components/form/Form.stories.tsx +226 -0
  475. package/src/components/form/Form.test.tsx +293 -0
  476. package/src/components/form/Form.tsx +50 -0
  477. package/src/components/form/FormCheckbox.tsx +50 -0
  478. package/src/components/form/FormCombobox.tsx +92 -0
  479. package/src/components/form/FormDescription.tsx +21 -0
  480. package/src/components/form/FormError.tsx +21 -0
  481. package/src/components/form/FormField.tsx +109 -0
  482. package/src/components/form/FormInput.tsx +41 -0
  483. package/src/components/form/FormInputGroup.tsx +47 -0
  484. package/src/components/form/FormLabel.tsx +24 -0
  485. package/src/components/form/FormRadioGroup.tsx +46 -0
  486. package/src/components/form/FormRoot.tsx +41 -0
  487. package/src/components/form/FormSelect.tsx +60 -0
  488. package/src/components/form/FormSwitch.tsx +50 -0
  489. package/src/components/form/FormTextarea.tsx +34 -0
  490. package/src/components/form/Input.stories.tsx +114 -0
  491. package/src/components/form/Input.test.tsx +62 -0
  492. package/src/components/form/Input.tsx +93 -0
  493. package/src/components/form/InputGroup.stories.tsx +162 -0
  494. package/src/components/form/InputGroup.tsx +149 -0
  495. package/src/components/form/RadioGroup.stories.tsx +68 -0
  496. package/src/components/form/RadioGroup.test.tsx +171 -0
  497. package/src/components/form/RadioGroup.tsx +127 -0
  498. package/src/components/form/Select.stories.tsx +115 -0
  499. package/src/components/form/Select.test.tsx +115 -0
  500. package/src/components/form/Select.tsx +293 -0
  501. package/src/components/form/Slider.stories.tsx +121 -0
  502. package/src/components/form/Slider.test.tsx +33 -0
  503. package/src/components/form/Slider.tsx +200 -0
  504. package/src/components/form/Switch.stories.tsx +62 -0
  505. package/src/components/form/Switch.test.tsx +29 -0
  506. package/src/components/form/Switch.tsx +81 -0
  507. package/src/components/form/Textarea.stories.tsx +71 -0
  508. package/src/components/form/Textarea.test.tsx +31 -0
  509. package/src/components/form/Textarea.tsx +46 -0
  510. package/src/entries/alert-dialog.ts +11 -0
  511. package/src/entries/alert.ts +8 -0
  512. package/src/entries/avatar.ts +7 -0
  513. package/src/entries/badge.ts +2 -0
  514. package/src/entries/button.ts +2 -0
  515. package/src/entries/card.ts +10 -0
  516. package/src/entries/checkbox.ts +5 -0
  517. package/src/entries/cn.ts +2 -0
  518. package/src/entries/color-picker.ts +13 -0
  519. package/src/entries/combobox.ts +20 -0
  520. package/src/entries/dialog.ts +11 -0
  521. package/src/entries/entries.test.ts +65 -0
  522. package/src/entries/flex.ts +2 -0
  523. package/src/entries/form.ts +16 -0
  524. package/src/entries/grid.ts +2 -0
  525. package/src/entries/group.ts +6 -0
  526. package/src/entries/input-group.ts +8 -0
  527. package/src/entries/input.ts +2 -0
  528. package/src/entries/menu.ts +17 -0
  529. package/src/entries/popover.ts +10 -0
  530. package/src/entries/progress.ts +8 -0
  531. package/src/entries/radio-group.ts +5 -0
  532. package/src/entries/scroll-area.ts +10 -0
  533. package/src/entries/select.ts +12 -0
  534. package/src/entries/separator.ts +2 -0
  535. package/src/entries/sidebar.ts +13 -0
  536. package/src/entries/slider.ts +7 -0
  537. package/src/entries/spinner.ts +2 -0
  538. package/src/entries/switch.ts +2 -0
  539. package/src/entries/table.ts +11 -0
  540. package/src/entries/text.ts +2 -0
  541. package/src/entries/textarea.ts +2 -0
  542. package/src/entries/theme.ts +8 -0
  543. package/src/entries/toast.ts +2 -0
  544. package/src/entries/tooltip.ts +7 -0
  545. package/src/entries/x-stack.ts +2 -0
  546. package/src/entries/y-stack.ts +2 -0
  547. package/src/hooks/use-as-ref.ts +15 -0
  548. package/src/hooks/use-isomorphic-layout-effect.ts +6 -0
  549. package/src/hooks/use-lazy-ref.ts +13 -0
  550. package/src/index.ts +38 -0
  551. package/src/lib/color.test.ts +148 -0
  552. package/src/lib/color.ts +193 -0
  553. package/src/lib/compose-refs.ts +63 -0
  554. package/src/lib/theme.test.ts +64 -0
  555. package/src/lib/theme.ts +143 -0
  556. package/src/lib/utils.ts +25 -0
  557. package/src/public-api.test.ts +74 -0
  558. package/src/styles.css +2 -0
  559. package/src/test-setup.ts +1 -0
  560. package/src/theme.css +195 -0
@@ -0,0 +1,171 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { IconPlus } from '@tabler/icons-react';
4
+
5
+ import { type ComponentMatrixCellProps, Story } from './_internal/Story';
6
+ import { Button, type ButtonProps } from './Button';
7
+
8
+ const meta = {
9
+ component: Button,
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ },
13
+ title: 'components/Button',
14
+ } satisfies Meta<typeof Button>;
15
+
16
+ export default meta;
17
+ type ButtonVariant = NonNullable<ButtonProps['variant']>;
18
+ type StoryDefinition = StoryObj<typeof meta>;
19
+
20
+ const STATE_COLUMNS: {
21
+ key: string;
22
+ label: string;
23
+ props: Partial<ButtonProps>;
24
+ }[] = [
25
+ { key: 'default', label: 'Default', props: {} },
26
+ { key: 'hover', label: 'Hover', props: { className: 'pseudo-hover' } },
27
+ {
28
+ key: 'focus',
29
+ label: 'Focus',
30
+ props: { className: 'pseudo-focus-visible' },
31
+ },
32
+ { key: 'active', label: 'Active', props: { className: 'pseudo-active' } },
33
+ { key: 'disabled', label: 'Disabled', props: { disabled: true } },
34
+ { key: 'loading', label: 'Loading', props: { loading: true } },
35
+ ];
36
+
37
+ const STATE_PROPS = Object.fromEntries(
38
+ STATE_COLUMNS.map((state) => [state.key, state.props]),
39
+ ) as Record<string, Partial<ButtonProps>>;
40
+
41
+ const VARIANT_ROWS: {
42
+ key: ButtonVariant;
43
+ label: string;
44
+ }[] = [
45
+ { key: 'default', label: 'Default' },
46
+ { key: 'secondary', label: 'Secondary' },
47
+ { key: 'destructive', label: 'Destructive' },
48
+ { key: 'destructive-outline', label: 'Destructive outline' },
49
+ { key: 'outline', label: 'Outline' },
50
+ { key: 'ghost', label: 'Ghost' },
51
+ { key: 'link', label: 'Link' },
52
+ ];
53
+
54
+ const SIZE_COLUMNS = [
55
+ { description: 'h-6', key: 'xs', label: 'Extra small' },
56
+ { description: 'h-7', key: 'sm', label: 'Small' },
57
+ { description: 'h-8', key: 'md', label: 'Medium' },
58
+ { description: 'h-9', key: 'lg', label: 'Large' },
59
+ { description: 'h-10', key: 'xl', label: 'Extra large' },
60
+ ];
61
+
62
+ const CONTENT_ROWS = [
63
+ { key: 'text', label: 'Text' },
64
+ { key: 'with-icon', label: 'Icon and text' },
65
+ {
66
+ description: 'Use size="icon" with aria-label',
67
+ key: 'icon',
68
+ label: 'Icon only',
69
+ },
70
+ ];
71
+
72
+ type ButtonSize = NonNullable<ButtonProps['size']>;
73
+
74
+ function ButtonContentCell({ column, row }: ComponentMatrixCellProps) {
75
+ const variant = column.key as ButtonVariant;
76
+
77
+ if (row.key === 'icon') {
78
+ return (
79
+ <Button aria-label="Add" size="icon" variant={variant}>
80
+ <IconPlus aria-hidden="true" stroke={2} />
81
+ </Button>
82
+ );
83
+ }
84
+
85
+ return (
86
+ <Button variant={variant}>
87
+ {row.key === 'with-icon' ? (
88
+ <IconPlus aria-hidden="true" stroke={2} />
89
+ ) : null}
90
+ Add
91
+ </Button>
92
+ );
93
+ }
94
+
95
+ function ButtonSizeCell({ column, row }: ComponentMatrixCellProps) {
96
+ const size = column.key as ButtonSize;
97
+
98
+ if (row.key === 'icon') {
99
+ return (
100
+ <Button aria-label="Add" size="icon">
101
+ <IconPlus aria-hidden="true" stroke={2} />
102
+ </Button>
103
+ );
104
+ }
105
+
106
+ return (
107
+ <Button size={size}>
108
+ {row.key === 'with-icon' ? (
109
+ <IconPlus aria-hidden="true" stroke={2} />
110
+ ) : null}
111
+ Add
112
+ </Button>
113
+ );
114
+ }
115
+
116
+ function ButtonStateCell({ column, row }: ComponentMatrixCellProps) {
117
+ return (
118
+ <Button variant={row.key as ButtonVariant} {...STATE_PROPS[column.key]}>
119
+ Button
120
+ </Button>
121
+ );
122
+ }
123
+
124
+ export const All: StoryDefinition = {
125
+ render: () => (
126
+ <Story.Layout className="max-w-6xl" title="Button">
127
+ <Story.Section title="Variants and states">
128
+ <Story.Matrix
129
+ Cell={ButtonStateCell}
130
+ columns={STATE_COLUMNS}
131
+ rows={VARIANT_ROWS}
132
+ />
133
+ </Story.Section>
134
+
135
+ <Story.Section title="Content patterns">
136
+ <Story.Matrix
137
+ Cell={ButtonContentCell}
138
+ columns={[
139
+ { key: 'default', label: 'Default' },
140
+ { key: 'outline', label: 'Outline' },
141
+ { key: 'ghost', label: 'Ghost' },
142
+ ]}
143
+ rows={CONTENT_ROWS}
144
+ />
145
+ </Story.Section>
146
+
147
+ <Story.Section title="Sizes">
148
+ <Story.Matrix
149
+ Cell={ButtonSizeCell}
150
+ columns={SIZE_COLUMNS}
151
+ rows={CONTENT_ROWS}
152
+ />
153
+ </Story.Section>
154
+
155
+ <Story.Section title="Render as a link">
156
+ <div className="flex flex-wrap items-center gap-3">
157
+ {/* react-doctor-disable-next-line react-doctor/anchor-has-content -- Button renders its children inside the anchor */}
158
+ <Button render={<a href="#docs" />} variant="outline">
159
+ Documentation
160
+ </Button>
161
+ {/* react-doctor-disable-next-line react-doctor/anchor-has-content -- Button renders its children inside the anchor */}
162
+ <Button render={<a href="#signup" />}>Create account</Button>
163
+ {/* react-doctor-disable-next-line react-doctor/anchor-has-content -- Button renders its children inside the anchor */}
164
+ <Button render={<a href="#settings" />} size="sm" variant="ghost">
165
+ Settings
166
+ </Button>
167
+ </div>
168
+ </Story.Section>
169
+ </Story.Layout>
170
+ ),
171
+ };
@@ -0,0 +1,112 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { Button } from './Button';
5
+
6
+ describe('Button', () => {
7
+ it('renders its children and defaults to type="button"', () => {
8
+ render(<Button>Click me</Button>);
9
+ const button = screen.getByRole('button', { name: 'Click me' });
10
+ expect(button).toBeInTheDocument();
11
+ expect(button).toHaveAttribute('type', 'button');
12
+ });
13
+
14
+ it.each([
15
+ ['xs', 'h-6'],
16
+ ['sm', 'h-7'],
17
+ ['md', 'h-8'],
18
+ ['lg', 'h-9'],
19
+ ['xl', 'h-10'],
20
+ ] as const)('keeps size %s at %s on every screen width', (size, height) => {
21
+ render(<Button size={size}>Save</Button>);
22
+ const classes = [...screen.getByRole('button').classList];
23
+
24
+ expect(classes).toContain(height);
25
+ expect(
26
+ classes.filter((name) => /^sm:(h|size|min-[hw])-/.test(name)),
27
+ ).toEqual([]);
28
+ });
29
+
30
+ it('exposes its variant and size as data attributes, defaults included', () => {
31
+ render(
32
+ <>
33
+ <Button>Default</Button>
34
+ <Button size="lg" variant="outline">
35
+ Outline
36
+ </Button>
37
+ </>,
38
+ );
39
+
40
+ const defaultButton = screen.getByRole('button', { name: 'Default' });
41
+ expect(defaultButton).toHaveAttribute('data-variant', 'default');
42
+ expect(defaultButton).toHaveAttribute('data-size', 'md');
43
+
44
+ const outlineButton = screen.getByRole('button', { name: 'Outline' });
45
+ expect(outlineButton).toHaveAttribute('data-variant', 'outline');
46
+ expect(outlineButton).toHaveAttribute('data-size', 'lg');
47
+ });
48
+
49
+ it('omits the data attributes when variant and size are null', () => {
50
+ render(
51
+ <Button size={null} variant={null}>
52
+ Unstyled
53
+ </Button>,
54
+ );
55
+
56
+ const button = screen.getByRole('button', { name: 'Unstyled' });
57
+ expect(button).not.toHaveAttribute('data-variant');
58
+ expect(button).not.toHaveAttribute('data-size');
59
+ expect(button).not.toHaveClass('h-8');
60
+ });
61
+
62
+ it('shows a loading indicator when loading', () => {
63
+ render(<Button loading>Save</Button>);
64
+
65
+ expect(screen.getByRole('button', { name: /Save/ })).toBeDisabled();
66
+ expect(screen.getByRole('status', { name: 'Loading' })).toBeInTheDocument();
67
+ });
68
+
69
+ it('renders button styles on the element passed to render', () => {
70
+ render(
71
+ <Button
72
+ // react-doctor-disable-next-line react-doctor/anchor-has-content -- Button renders its children inside the anchor
73
+ render={<a href="/docs" />}
74
+ variant="outline"
75
+ >
76
+ Documentation
77
+ </Button>,
78
+ );
79
+
80
+ const link = screen.getByRole('link', { name: 'Documentation' });
81
+ expect(link).toHaveAttribute('data-slot', 'button');
82
+ expect(link).not.toHaveAttribute('type');
83
+ expect(link.className).toContain('border');
84
+ });
85
+
86
+ it('marks a rendered element as disabled without the disabled attribute', () => {
87
+ render(
88
+ <Button
89
+ disabled
90
+ // react-doctor-disable-next-line react-doctor/anchor-has-content -- Button renders its children inside the anchor
91
+ render={<a href="/docs" />}
92
+ >
93
+ Documentation
94
+ </Button>,
95
+ );
96
+
97
+ const link = screen.getByRole('link', { name: 'Documentation' });
98
+ expect(link).toHaveAttribute('aria-disabled', 'true');
99
+ expect(link).not.toHaveAttribute('disabled');
100
+ });
101
+
102
+ it('applies classNames to the loading indicator slot', () => {
103
+ const { container } = render(
104
+ <Button classNames={{ loadingIndicator: 'spinner-x' }} loading>
105
+ Save
106
+ </Button>,
107
+ );
108
+ expect(
109
+ container.querySelector('[data-slot="button-loading-indicator"]'),
110
+ ).toHaveClass('spinner-x');
111
+ });
112
+ });
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { mergeProps } from '@base-ui/react/merge-props';
4
+ import { useRender } from '@base-ui/react/use-render';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+
7
+ import { cn, type SlotClassNames } from '@/lib/utils';
8
+
9
+ import { Spinner } from './Spinner';
10
+
11
+ const buttonVariants = cva(
12
+ "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border font-medium text-base outline-none transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-loading:select-none data-loading:text-transparent sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg]:[shape-rendering:geometricPrecision]",
13
+ {
14
+ defaultVariants: {
15
+ size: 'md',
16
+ variant: 'default',
17
+ },
18
+ variants: {
19
+ size: {
20
+ icon: 'size-8',
21
+ 'icon-lg': 'size-9',
22
+ 'icon-sm': 'size-7',
23
+ 'icon-xl': "size-10 [&_svg:not([class*='size-'])]:size-4.5",
24
+ 'icon-xs':
25
+ "size-6 rounded-md before:rounded-[calc(var(--radius-md)-1px)] not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
26
+ lg: 'h-9 px-[calc(--spacing(3.5)-1px)]',
27
+ md: 'h-8 px-[calc(--spacing(3)-1px)]',
28
+ sm: 'h-7 gap-1.5 px-[calc(--spacing(2.5)-1px)]',
29
+ xl: "h-10 px-[calc(--spacing(4)-1px)] text-lg sm:text-base [&_svg:not([class*='size-'])]:size-4.5",
30
+ xs: "h-6 gap-1 rounded-md px-[calc(--spacing(2)-1px)] text-sm before:rounded-[calc(var(--radius-md)-1px)] sm:text-xs [&_svg:not([class*='size-'])]:size-3.5",
31
+ },
32
+ variant: {
33
+ default:
34
+ 'not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] border-primary bg-primary text-primary-foreground shadow-primary/24 shadow-xs hover:bg-primary/90 data-pressed:bg-primary/90 *:data-[slot=button-loading-indicator]:text-primary-foreground [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none',
35
+ destructive:
36
+ 'not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] border-destructive bg-destructive text-destructive-foreground shadow-destructive/24 shadow-xs hover:bg-destructive/90 data-pressed:bg-destructive/90 *:data-[slot=button-loading-indicator]:text-destructive-foreground [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none',
37
+ 'destructive-outline':
38
+ 'border-input bg-popover not-dark:bg-clip-padding text-destructive-subtle-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] hover:border-destructive/32 hover:bg-destructive/4 data-pressed:border-destructive/32 data-pressed:bg-destructive/4 *:data-[slot=button-loading-indicator]:text-foreground dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none',
39
+ ghost:
40
+ 'border-transparent text-foreground hover:bg-accent data-pressed:bg-accent *:data-[slot=button-loading-indicator]:text-foreground',
41
+ link: 'border-transparent text-foreground underline-offset-4 hover:underline data-pressed:underline *:data-[slot=button-loading-indicator]:text-foreground',
42
+ outline:
43
+ 'border-input bg-popover not-dark:bg-clip-padding text-foreground shadow-xs/5 not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] hover:bg-accent/50 data-pressed:bg-accent/50 *:data-[slot=button-loading-indicator]:text-foreground dark:bg-input/32 dark:data-pressed:bg-input/64 dark:hover:bg-input/64 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none',
44
+ secondary:
45
+ 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/90 data-pressed:bg-secondary/90 *:data-[slot=button-loading-indicator]:text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80',
46
+ },
47
+ },
48
+ },
49
+ );
50
+
51
+ export interface ButtonProps
52
+ extends
53
+ useRender.ComponentProps<'button'>,
54
+ VariantProps<typeof buttonVariants> {
55
+ classNames?: SlotClassNames<'loadingIndicator'>;
56
+ loading?: boolean;
57
+ }
58
+
59
+ /**
60
+ * Icon-only buttons need an explicit `size="icon"` (or `icon-*`) and
61
+ * `aria-label`. Use `render` to render a semantic element such as an anchor
62
+ * while keeping button styling: `<Button render={<a href="/docs" />}>`.
63
+ */
64
+ function Button({
65
+ children,
66
+ className,
67
+ classNames,
68
+ disabled,
69
+ loading = false,
70
+ ref,
71
+ render,
72
+ size = 'md',
73
+ type,
74
+ variant = 'default',
75
+ ...props
76
+ }: ButtonProps) {
77
+ // A rendered element such as a link has no `disabled` or `type`: it only
78
+ // reports the state through `aria-disabled`.
79
+ const isNativeButton = render === undefined;
80
+ const isDisabled = Boolean(loading || disabled);
81
+ const defaultProps = {
82
+ 'aria-disabled': (isNativeButton ? loading : isDisabled) || undefined,
83
+ children: (
84
+ <>
85
+ {children}
86
+ {loading ? (
87
+ <Spinner
88
+ className={cn(
89
+ 'pointer-events-none absolute',
90
+ classNames?.loadingIndicator,
91
+ )}
92
+ data-slot="button-loading-indicator"
93
+ />
94
+ ) : null}
95
+ </>
96
+ ),
97
+ className: cn(buttonVariants({ className, size, variant })),
98
+ 'data-loading': loading ? '' : undefined,
99
+ 'data-size': size,
100
+ 'data-slot': 'button',
101
+ 'data-variant': variant,
102
+ disabled: isNativeButton ? isDisabled : undefined,
103
+ type: isNativeButton ? (type ?? 'button') : type,
104
+ };
105
+
106
+ return useRender({
107
+ defaultTagName: 'button',
108
+ props: mergeProps<'button'>(defaultProps, props),
109
+ ref,
110
+ render,
111
+ });
112
+ }
113
+
114
+ export { Button, buttonVariants };
@@ -0,0 +1,62 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { IconArrowUpRight, IconDots } from '@tabler/icons-react';
4
+
5
+ import { Story } from './_internal/Story';
6
+ import { Button } from './Button';
7
+ import { Card } from './Card';
8
+ import { Text } from './Text';
9
+
10
+ const meta = {
11
+ component: Card,
12
+ parameters: { layout: 'fullscreen' },
13
+ title: 'components/Card',
14
+ } satisfies Meta<typeof Card>;
15
+
16
+ export default meta;
17
+ type StoryDefinition = StoryObj<typeof meta>;
18
+
19
+ export const All: StoryDefinition = {
20
+ render: () => (
21
+ <Story.Layout className="max-w-5xl" title="Card">
22
+ <Story.Section title="Composition">
23
+ <div className="max-w-lg space-y-6">
24
+ <Card>
25
+ <Card.Header>
26
+ <Card.Title>Project activity</Card.Title>
27
+ <Card.Description>
28
+ Changes made in the last week.
29
+ </Card.Description>
30
+ <Card.Action>
31
+ <Button aria-label="More options" variant="ghost">
32
+ <IconDots />
33
+ </Button>
34
+ </Card.Action>
35
+ </Card.Header>
36
+ <Card.Content className="space-y-2">
37
+ <Text variant="h1">1,284</Text>
38
+ <Text tone="muted">Events across 8 active projects</Text>
39
+ </Card.Content>
40
+ <Card.Footer>
41
+ <Button className="ml-auto" size="sm" variant="ghost">
42
+ View report <IconArrowUpRight />
43
+ </Button>
44
+ </Card.Footer>
45
+ </Card>
46
+
47
+ <Card className="justify-between">
48
+ <Card.Header>
49
+ <Card.Title>Simple card</Card.Title>
50
+ <Card.Description>
51
+ Slots can be omitted when the content needs less structure.
52
+ </Card.Description>
53
+ </Card.Header>
54
+ <Card.Footer>
55
+ <Button>Continue</Button>
56
+ </Card.Footer>
57
+ </Card>
58
+ </div>
59
+ </Story.Section>
60
+ </Story.Layout>
61
+ ),
62
+ };
@@ -0,0 +1,23 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { Card } from './Card';
5
+
6
+ describe('Card', () => {
7
+ it('exposes its semantic compound slots', () => {
8
+ render(
9
+ <Card>
10
+ <Card.Header>
11
+ <Card.Title>Account</Card.Title>
12
+ <Card.Description>Manage your profile.</Card.Description>
13
+ </Card.Header>
14
+ <Card.Content>Content</Card.Content>
15
+ </Card>,
16
+ );
17
+
18
+ expect(
19
+ screen.getByRole('heading', { name: 'Account' }),
20
+ ).toBeInTheDocument();
21
+ expect(screen.getByText('Manage your profile.')).toBeInTheDocument();
22
+ });
23
+ });
@@ -0,0 +1,129 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from '@/lib/utils';
4
+
5
+ import { Text, type TextProps } from './Text';
6
+
7
+ type CardActionProps = React.ComponentProps<'div'>;
8
+ type CardContentProps = React.ComponentProps<'div'>;
9
+ type CardDescriptionProps = Omit<TextProps, 'as' | 'tone' | 'variant'>;
10
+ type CardFooterProps = React.ComponentProps<'div'>;
11
+ type CardHeaderProps = React.ComponentProps<'div'>;
12
+ type CardProps = React.ComponentProps<'div'>;
13
+ type CardTitleProps = Omit<TextProps, 'as' | 'tone' | 'variant'>;
14
+
15
+ function CardAction({ className, ...props }: CardActionProps) {
16
+ return (
17
+ <div
18
+ className={cn(
19
+ 'col-start-2 row-span-2 row-start-1 inline-flex self-start justify-self-end',
20
+ className,
21
+ )}
22
+ data-slot="card-action"
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function CardContent({ className, ...props }: CardContentProps) {
29
+ return (
30
+ <div
31
+ className={cn(
32
+ 'flex-1 p-6 in-[[data-slot=card]:has(>[data-slot=card-footer]:not(.border-t))]:pb-0 in-[[data-slot=card]:has(>[data-slot=card-header]:not(.border-b))]:pt-0',
33
+ className,
34
+ )}
35
+ data-slot="card-content"
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: CardDescriptionProps) {
42
+ return (
43
+ <Text
44
+ className={cn('text-muted-foreground text-sm', className)}
45
+ data-slot="card-description"
46
+ tone="muted"
47
+ variant="body"
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ function CardFooter({ className, ...props }: CardFooterProps) {
54
+ return (
55
+ <div
56
+ className={cn(
57
+ 'flex items-center p-6 in-[[data-slot=card]:has(>[data-slot=card-content])]:pt-4',
58
+ className,
59
+ )}
60
+ data-slot="card-footer"
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ function CardHeader({ className, ...props }: CardHeaderProps) {
67
+ return (
68
+ <div
69
+ className={cn(
70
+ 'grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 p-6 in-[[data-slot=card]:has(>[data-slot=card-content])]:pb-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]',
71
+ className,
72
+ )}
73
+ data-slot="card-header"
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ function CardRoot({ className, ...props }: CardProps) {
80
+ return (
81
+ <div
82
+ className={cn(
83
+ 'bg-card text-card-foreground relative flex flex-col rounded-2xl border shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]',
84
+ className,
85
+ )}
86
+ data-slot="card"
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ function CardTitle({ className, ...props }: CardTitleProps) {
93
+ return (
94
+ <Text
95
+ className={cn('font-heading leading-none font-semibold', className)}
96
+ data-slot="card-title"
97
+ variant="h3"
98
+ {...props}
99
+ />
100
+ );
101
+ }
102
+
103
+ const Card = Object.assign(CardRoot, {
104
+ Action: CardAction,
105
+ Content: CardContent,
106
+ Description: CardDescription,
107
+ Footer: CardFooter,
108
+ Header: CardHeader,
109
+ Title: CardTitle,
110
+ });
111
+
112
+ export {
113
+ Card,
114
+ CardAction,
115
+ CardContent,
116
+ CardDescription,
117
+ CardFooter,
118
+ CardHeader,
119
+ CardTitle,
120
+ };
121
+ export type {
122
+ CardActionProps,
123
+ CardContentProps,
124
+ CardDescriptionProps,
125
+ CardFooterProps,
126
+ CardHeaderProps,
127
+ CardProps,
128
+ CardTitleProps,
129
+ };