@stacksjs/components 0.2.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 (342) hide show
  1. package/README.md +477 -0
  2. package/dist/Accordion-x2gjedap.stx +108 -0
  3. package/dist/Audio-0fk6p9tn.stx +50 -0
  4. package/dist/Avatar-5m0v0s4x.stx +88 -0
  5. package/dist/Badge-ynes82vv.stx +70 -0
  6. package/dist/Breadcrumb-h9n427sk.stx +79 -0
  7. package/dist/Button-7xtrzn14.stx +89 -0
  8. package/dist/Calendar-egvx1pma.stx +232 -0
  9. package/dist/Card-r9w7vy1a.stx +74 -0
  10. package/dist/Checkbox-m8v46pmx.stx +92 -0
  11. package/dist/Checkout-vcnnr176.stx +39 -0
  12. package/dist/CodeBlock-mwex82cm.stx +57 -0
  13. package/dist/Combobox-npb08vkp.stx +13 -0
  14. package/dist/ComboboxButton-vcdyq4zw.stx +13 -0
  15. package/dist/ComboboxInput-c9d2p02m.stx +25 -0
  16. package/dist/ComboboxOption-7d0h9ash.stx +52 -0
  17. package/dist/ComboboxOptions-4dtqc0qa.stx +12 -0
  18. package/dist/CommandPalette-ah00bf8v.stx +39 -0
  19. package/dist/CommandPaletteItem-2yx0gvtj.stx +32 -0
  20. package/dist/DefaultPaymentMethod-emw5fg8s.stx +62 -0
  21. package/dist/Dialog-1w4rz7x0.stx +19 -0
  22. package/dist/DialogBackdrop-qbm3y5ab.stx +9 -0
  23. package/dist/DialogDescription-01w5j74b.stx +11 -0
  24. package/dist/DialogPanel-brsszrrs.stx +16 -0
  25. package/dist/DialogTitle-3rxhwg21.stx +11 -0
  26. package/dist/Drawer-yfaz8q7f.stx +137 -0
  27. package/dist/Dropdown-ng7nx556.stx +24 -0
  28. package/dist/DropdownButton-vc4zpyrb.stx +23 -0
  29. package/dist/DropdownItem-z491y23c.stx +43 -0
  30. package/dist/DropdownItems-7ykf29sj.stx +18 -0
  31. package/dist/EmailInput-bfshpj92.stx +41 -0
  32. package/dist/Footer-xdaf02nr.stx +66 -0
  33. package/dist/Form-z4f7w6r1.stx +209 -0
  34. package/dist/Hero-9vvp7pxf.stx +25 -0
  35. package/dist/Image-ktk0451c.stx +197 -0
  36. package/dist/Installation-33bj4rm7.stx +39 -0
  37. package/dist/Listbox-xcejcmyc.stx +14 -0
  38. package/dist/ListboxButton-nx5kmw19.stx +17 -0
  39. package/dist/ListboxLabel-aky8a73n.stx +11 -0
  40. package/dist/ListboxOption-6h4hsdz5.stx +57 -0
  41. package/dist/ListboxOptions-2sacdtm5.stx +16 -0
  42. package/dist/Login-s2jkvd9y.stx +163 -0
  43. package/dist/Navigator-tqqdgdkw.stx +138 -0
  44. package/dist/Notification-sry8egea.stx +71 -0
  45. package/dist/NumberInput-zm93ddfw.stx +138 -0
  46. package/dist/Pagination-yza9v4qw.stx +183 -0
  47. package/dist/PasswordInput-4xnndp5y.stx +155 -0
  48. package/dist/PaymentMethods-jxba6kek.stx +113 -0
  49. package/dist/Popover-scbqatjq.stx +24 -0
  50. package/dist/PopoverButton-rbxhzj4k.stx +23 -0
  51. package/dist/PopoverPanel-9ng1rm6e.stx +23 -0
  52. package/dist/Portal-j111yvqp.stx +74 -0
  53. package/dist/Progress-gbmw3akr.stx +142 -0
  54. package/dist/Radio-1n9qamv1.stx +81 -0
  55. package/dist/RadioGroup-12dw2vhk.stx +13 -0
  56. package/dist/RadioGroupDescription-bjm3mdht.stx +11 -0
  57. package/dist/RadioGroupLabel-aky8a73n.stx +11 -0
  58. package/dist/RadioGroupOption-9pcxgwmd.stx +51 -0
  59. package/dist/SearchInput-g240e2nq.stx +63 -0
  60. package/dist/Select-p9f94b20.stx +89 -0
  61. package/dist/Sidebar-gwxw8q38.stx +288 -0
  62. package/dist/SidebarFooter-887kxret.stx +95 -0
  63. package/dist/SidebarHeader-qjeyx0hv.stx +100 -0
  64. package/dist/SidebarItem-4dpwtmb4.stx +118 -0
  65. package/dist/SidebarSection-kh92fk4m.stx +108 -0
  66. package/dist/Signup-3nxq5zag.stx +161 -0
  67. package/dist/Skeleton-g0nx1njm.stx +48 -0
  68. package/dist/Spinner-w4zj58xp.stx +109 -0
  69. package/dist/Stepper-rwc5actp.stx +17 -0
  70. package/dist/StepperStep-h0k657nm.stx +53 -0
  71. package/dist/SubscriptionCheckout-4cbxdg7w.stx +353 -0
  72. package/dist/Switch-kjq8qd2p.stx +74 -0
  73. package/dist/Table-59j9qwp0.stx +14 -0
  74. package/dist/TableBody-reka2gwv.stx +10 -0
  75. package/dist/TableCell-j7nh4sgr.stx +10 -0
  76. package/dist/TableHead-ecbrwpqm.stx +10 -0
  77. package/dist/TableHeader-jn2shwsv.stx +13 -0
  78. package/dist/TableRow-xz8rrxhc.stx +11 -0
  79. package/dist/Tabs-kb6ex6m7.stx +145 -0
  80. package/dist/Teleport-819cg9zm.stx +101 -0
  81. package/dist/TextInput-zx9kgr79.stx +146 -0
  82. package/dist/Textarea-5xvmrb0z.stx +119 -0
  83. package/dist/Tooltip-5ehqsy49.stx +76 -0
  84. package/dist/Transition-2j39s044.stx +174 -0
  85. package/dist/TwoFactorChallenge-2q4gg89m.stx +125 -0
  86. package/dist/Video-6aq4shh4.stx +45 -0
  87. package/dist/VirtualList-weh5qtm1.stx +83 -0
  88. package/dist/VirtualTable-59a1eskc.stx +113 -0
  89. package/dist/components/index.d.ts +4 -0
  90. package/dist/composables/index.d.ts +4 -0
  91. package/dist/composables/index.js +3 -0
  92. package/dist/composables/useCopyCode.d.ts +19 -0
  93. package/dist/composables/useDarkMode.d.ts +25 -0
  94. package/dist/composables/useSEO.d.ts +36 -0
  95. package/dist/composables/utilities/index.d.ts +5 -0
  96. package/dist/composables/utilities/useClickOutside.d.ts +28 -0
  97. package/dist/composables/utilities/useFocusTrap.d.ts +34 -0
  98. package/dist/composables/utilities/useKeyboard.d.ts +55 -0
  99. package/dist/composables/utilities/useLocalStorage.d.ts +33 -0
  100. package/dist/composables/utilities/useMediaQuery.d.ts +64 -0
  101. package/dist/index.d.ts +86 -0
  102. package/dist/index.js +19 -0
  103. package/dist/ui/accordion/index.d.ts +12 -0
  104. package/dist/ui/audio/index.d.ts +12 -0
  105. package/dist/ui/auth/index.d.ts +29 -0
  106. package/dist/ui/avatar/index.d.ts +11 -0
  107. package/dist/ui/badge/index.d.ts +9 -0
  108. package/dist/ui/breadcrumb/index.d.ts +13 -0
  109. package/dist/ui/button/index.d.ts +45 -0
  110. package/dist/ui/calendar/index.d.ts +13 -0
  111. package/dist/ui/card/index.d.ts +12 -0
  112. package/dist/ui/checkbox/index.d.ts +15 -0
  113. package/dist/ui/combobox/index.d.ts +35 -0
  114. package/dist/ui/command-palette/index.d.ts +13 -0
  115. package/dist/ui/dialog/index.d.ts +27 -0
  116. package/dist/ui/drawer/index.d.ts +8 -0
  117. package/dist/ui/dropdown/index.d.ts +25 -0
  118. package/dist/ui/form/index.d.ts +48 -0
  119. package/dist/ui/image/index.d.ts +21 -0
  120. package/dist/ui/input/index.d.ts +73 -0
  121. package/dist/ui/listbox/index.d.ts +34 -0
  122. package/dist/ui/navigator/index.d.ts +18 -0
  123. package/dist/ui/notification/index.d.ts +11 -0
  124. package/dist/ui/pagination/index.d.ts +10 -0
  125. package/dist/ui/payment/index.d.ts +53 -0
  126. package/dist/ui/popover/index.d.ts +19 -0
  127. package/dist/ui/portal/index.d.ts +5 -0
  128. package/dist/ui/progress/index.d.ts +11 -0
  129. package/dist/ui/radio/index.d.ts +14 -0
  130. package/dist/ui/radio-group/index.d.ts +26 -0
  131. package/dist/ui/select/index.d.ts +19 -0
  132. package/dist/ui/sidebar/index.d.ts +129 -0
  133. package/dist/ui/skeleton/index.d.ts +10 -0
  134. package/dist/ui/spinner/index.d.ts +8 -0
  135. package/dist/ui/stepper/index.d.ts +17 -0
  136. package/dist/ui/storage/index.d.ts +40 -0
  137. package/dist/ui/switch/index.d.ts +9 -0
  138. package/dist/ui/table/index.d.ts +29 -0
  139. package/dist/ui/tabs/index.d.ts +14 -0
  140. package/dist/ui/teleport/index.d.ts +6 -0
  141. package/dist/ui/textarea/index.d.ts +19 -0
  142. package/dist/ui/tooltip/index.d.ts +9 -0
  143. package/dist/ui/transition/index.d.ts +24 -0
  144. package/dist/ui/video/index.d.ts +15 -0
  145. package/dist/ui/virtual-list/index.d.ts +9 -0
  146. package/dist/ui/virtual-table/index.d.ts +19 -0
  147. package/dist/utils/accessibility.d.ts +285 -0
  148. package/dist/utils/animation/easing.d.ts +112 -0
  149. package/dist/utils/animation/helpers.d.ts +119 -0
  150. package/dist/utils/animation/index.d.ts +3 -0
  151. package/dist/utils/animation/keyframes.d.ts +334 -0
  152. package/dist/utils/error-handling.d.ts +182 -0
  153. package/dist/utils/highlighter.d.ts +27 -0
  154. package/dist/utils/highlighter.js +2 -0
  155. package/dist/utils/i18n.d.ts +245 -0
  156. package/dist/utils/keep-alive.d.ts +171 -0
  157. package/dist/utils/lazy-loading.d.ts +174 -0
  158. package/dist/utils/prop-validation.d.ts +106 -0
  159. package/dist/utils/theme.d.ts +234 -0
  160. package/package.json +69 -0
  161. package/src/components/CodeBlock.stx +57 -0
  162. package/src/components/Footer.stx +66 -0
  163. package/src/components/Hero.stx +25 -0
  164. package/src/components/Installation.stx +39 -0
  165. package/src/components/index.ts +4 -0
  166. package/src/composables/index.ts +4 -0
  167. package/src/composables/useCopyCode.ts +73 -0
  168. package/src/composables/useDarkMode.ts +100 -0
  169. package/src/composables/useSEO.ts +229 -0
  170. package/src/composables/utilities/index.ts +7 -0
  171. package/src/composables/utilities/useClickOutside.ts +72 -0
  172. package/src/composables/utilities/useFocusTrap.ts +159 -0
  173. package/src/composables/utilities/useKeyboard.ts +124 -0
  174. package/src/composables/utilities/useLocalStorage.ts +132 -0
  175. package/src/composables/utilities/useMediaQuery.ts +118 -0
  176. package/src/index.ts +99 -0
  177. package/src/stx.d.ts +4 -0
  178. package/src/ui/accordion/Accordion.stx +108 -0
  179. package/src/ui/accordion/index.ts +14 -0
  180. package/src/ui/audio/Audio.stx +50 -0
  181. package/src/ui/audio/README.md +42 -0
  182. package/src/ui/audio/index.ts +13 -0
  183. package/src/ui/auth/Login.stx +163 -0
  184. package/src/ui/auth/README.md +145 -0
  185. package/src/ui/auth/Signup.stx +161 -0
  186. package/src/ui/auth/TwoFactorChallenge.stx +125 -0
  187. package/src/ui/auth/index.ts +32 -0
  188. package/src/ui/avatar/Avatar.stx +88 -0
  189. package/src/ui/avatar/index.ts +12 -0
  190. package/src/ui/badge/Badge.stx +70 -0
  191. package/src/ui/badge/index.ts +10 -0
  192. package/src/ui/breadcrumb/Breadcrumb.stx +79 -0
  193. package/src/ui/breadcrumb/index.ts +15 -0
  194. package/src/ui/button/Button.stx +89 -0
  195. package/src/ui/button/README.md +100 -0
  196. package/src/ui/button/index.ts +85 -0
  197. package/src/ui/calendar/Calendar.stx +232 -0
  198. package/src/ui/calendar/README.md +254 -0
  199. package/src/ui/calendar/index.ts +14 -0
  200. package/src/ui/card/Card.stx +74 -0
  201. package/src/ui/card/index.ts +13 -0
  202. package/src/ui/checkbox/Checkbox.stx +92 -0
  203. package/src/ui/checkbox/index.ts +16 -0
  204. package/src/ui/combobox/Combobox.stx +13 -0
  205. package/src/ui/combobox/ComboboxButton.stx +13 -0
  206. package/src/ui/combobox/ComboboxInput.stx +25 -0
  207. package/src/ui/combobox/ComboboxOption.stx +52 -0
  208. package/src/ui/combobox/ComboboxOptions.stx +12 -0
  209. package/src/ui/combobox/README.md +154 -0
  210. package/src/ui/combobox/index.ts +40 -0
  211. package/src/ui/command-palette/CommandPalette.stx +39 -0
  212. package/src/ui/command-palette/CommandPaletteItem.stx +32 -0
  213. package/src/ui/command-palette/README.md +42 -0
  214. package/src/ui/command-palette/index.ts +15 -0
  215. package/src/ui/dialog/Dialog.stx +19 -0
  216. package/src/ui/dialog/DialogBackdrop.stx +9 -0
  217. package/src/ui/dialog/DialogDescription.stx +11 -0
  218. package/src/ui/dialog/DialogPanel.stx +16 -0
  219. package/src/ui/dialog/DialogTitle.stx +11 -0
  220. package/src/ui/dialog/README.md +133 -0
  221. package/src/ui/dialog/index.ts +32 -0
  222. package/src/ui/drawer/Drawer.stx +137 -0
  223. package/src/ui/drawer/README.md +110 -0
  224. package/src/ui/drawer/index.ts +9 -0
  225. package/src/ui/dropdown/Dropdown.stx +24 -0
  226. package/src/ui/dropdown/DropdownButton.stx +23 -0
  227. package/src/ui/dropdown/DropdownItem.stx +43 -0
  228. package/src/ui/dropdown/DropdownItems.stx +18 -0
  229. package/src/ui/dropdown/README.md +142 -0
  230. package/src/ui/dropdown/index.ts +29 -0
  231. package/src/ui/form/Form.stx +209 -0
  232. package/src/ui/form/index.ts +67 -0
  233. package/src/ui/heatmap/Heatmap.stx +315 -0
  234. package/src/ui/image/Image.stx +197 -0
  235. package/src/ui/image/README.md +171 -0
  236. package/src/ui/image/index.ts +22 -0
  237. package/src/ui/input/EmailInput.stx +41 -0
  238. package/src/ui/input/NumberInput.stx +138 -0
  239. package/src/ui/input/PasswordInput.stx +155 -0
  240. package/src/ui/input/SearchInput.stx +63 -0
  241. package/src/ui/input/TextInput.stx +146 -0
  242. package/src/ui/input/index.ts +180 -0
  243. package/src/ui/listbox/Listbox.stx +14 -0
  244. package/src/ui/listbox/ListboxButton.stx +17 -0
  245. package/src/ui/listbox/ListboxLabel.stx +11 -0
  246. package/src/ui/listbox/ListboxOption.stx +57 -0
  247. package/src/ui/listbox/ListboxOptions.stx +16 -0
  248. package/src/ui/listbox/README.md +143 -0
  249. package/src/ui/listbox/index.ts +39 -0
  250. package/src/ui/navigator/Navigator.stx +138 -0
  251. package/src/ui/navigator/README.md +352 -0
  252. package/src/ui/navigator/index.ts +20 -0
  253. package/src/ui/notification/Notification.stx +71 -0
  254. package/src/ui/notification/README.md +118 -0
  255. package/src/ui/notification/index.ts +12 -0
  256. package/src/ui/pagination/Pagination.stx +183 -0
  257. package/src/ui/pagination/index.ts +11 -0
  258. package/src/ui/payment/Checkout.stx +39 -0
  259. package/src/ui/payment/DefaultPaymentMethod.stx +62 -0
  260. package/src/ui/payment/PaymentMethods.stx +113 -0
  261. package/src/ui/payment/README.md +427 -0
  262. package/src/ui/payment/SubscriptionCheckout.stx +353 -0
  263. package/src/ui/payment/index.ts +59 -0
  264. package/src/ui/popover/Popover.stx +24 -0
  265. package/src/ui/popover/PopoverButton.stx +23 -0
  266. package/src/ui/popover/PopoverPanel.stx +23 -0
  267. package/src/ui/popover/README.md +95 -0
  268. package/src/ui/popover/index.ts +22 -0
  269. package/src/ui/portal/Portal.stx +74 -0
  270. package/src/ui/portal/index.ts +6 -0
  271. package/src/ui/progress/Progress.stx +142 -0
  272. package/src/ui/progress/index.ts +12 -0
  273. package/src/ui/radio/Radio.stx +81 -0
  274. package/src/ui/radio/index.ts +15 -0
  275. package/src/ui/radio-group/README.md +145 -0
  276. package/src/ui/radio-group/RadioGroup.stx +13 -0
  277. package/src/ui/radio-group/RadioGroupDescription.stx +11 -0
  278. package/src/ui/radio-group/RadioGroupLabel.stx +11 -0
  279. package/src/ui/radio-group/RadioGroupOption.stx +51 -0
  280. package/src/ui/radio-group/index.ts +30 -0
  281. package/src/ui/select/README.md +204 -0
  282. package/src/ui/select/Select.stx +89 -0
  283. package/src/ui/select/index.ts +21 -0
  284. package/src/ui/sidebar/Sidebar.stx +288 -0
  285. package/src/ui/sidebar/SidebarFooter.stx +95 -0
  286. package/src/ui/sidebar/SidebarHeader.stx +100 -0
  287. package/src/ui/sidebar/SidebarItem.stx +118 -0
  288. package/src/ui/sidebar/SidebarSection.stx +108 -0
  289. package/src/ui/sidebar/index.ts +175 -0
  290. package/src/ui/skeleton/Skeleton.stx +48 -0
  291. package/src/ui/skeleton/index.ts +11 -0
  292. package/src/ui/spinner/Spinner.stx +109 -0
  293. package/src/ui/spinner/index.ts +9 -0
  294. package/src/ui/stepper/README.md +36 -0
  295. package/src/ui/stepper/Stepper.stx +17 -0
  296. package/src/ui/stepper/StepperStep.stx +53 -0
  297. package/src/ui/stepper/index.ts +19 -0
  298. package/src/ui/storage/README.md +193 -0
  299. package/src/ui/storage/index.ts +347 -0
  300. package/src/ui/switch/README.md +133 -0
  301. package/src/ui/switch/Switch.stx +74 -0
  302. package/src/ui/switch/index.ts +10 -0
  303. package/src/ui/table/README.md +39 -0
  304. package/src/ui/table/Table.stx +14 -0
  305. package/src/ui/table/TableBody.stx +10 -0
  306. package/src/ui/table/TableCell.stx +10 -0
  307. package/src/ui/table/TableHead.stx +10 -0
  308. package/src/ui/table/TableHeader.stx +13 -0
  309. package/src/ui/table/TableRow.stx +11 -0
  310. package/src/ui/table/index.ts +35 -0
  311. package/src/ui/tabs/Tabs.stx +145 -0
  312. package/src/ui/tabs/index.ts +16 -0
  313. package/src/ui/teleport/Teleport.stx +101 -0
  314. package/src/ui/teleport/index.ts +7 -0
  315. package/src/ui/textarea/Textarea.stx +119 -0
  316. package/src/ui/textarea/index.ts +20 -0
  317. package/src/ui/tooltip/Tooltip.stx +76 -0
  318. package/src/ui/tooltip/index.ts +10 -0
  319. package/src/ui/transition/README.md +30 -0
  320. package/src/ui/transition/Transition.stx +174 -0
  321. package/src/ui/transition/index.ts +25 -0
  322. package/src/ui/video/README.md +48 -0
  323. package/src/ui/video/Video.stx +45 -0
  324. package/src/ui/video/index.ts +16 -0
  325. package/src/ui/virtual-list/VirtualList.stx +83 -0
  326. package/src/ui/virtual-list/index.ts +10 -0
  327. package/src/ui/virtual-table/VirtualTable.stx +113 -0
  328. package/src/ui/virtual-table/index.ts +21 -0
  329. package/src/utils/PROP_VALIDATION.md +458 -0
  330. package/src/utils/THEME_SYSTEM.md +702 -0
  331. package/src/utils/accessibility.ts +841 -0
  332. package/src/utils/animation/easing.ts +368 -0
  333. package/src/utils/animation/helpers.ts +223 -0
  334. package/src/utils/animation/index.ts +5 -0
  335. package/src/utils/animation/keyframes.ts +230 -0
  336. package/src/utils/error-handling.ts +473 -0
  337. package/src/utils/highlighter.ts +93 -0
  338. package/src/utils/i18n.ts +634 -0
  339. package/src/utils/keep-alive.ts +818 -0
  340. package/src/utils/lazy-loading.ts +530 -0
  341. package/src/utils/prop-validation.ts +478 -0
  342. package/src/utils/theme.ts +626 -0
@@ -0,0 +1,133 @@
1
+ # Switch Component
2
+
3
+ A modern toggle switch component built with STX and headwind utility classes, inspired by Headless UI.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ @component('Switch', { checked: true, label: 'Enable notifications' })
15
+ ```
16
+
17
+ ## Props
18
+
19
+ | Prop | Type | Default | Description |
20
+ |------|------|---------|-------------|
21
+ | `checked` | `boolean` | `false` | Whether the switch is checked |
22
+ | `disabled` | `boolean` | `false` | Disable the switch |
23
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Switch size |
24
+ | `className` | `string` | `''` | Additional CSS classes |
25
+ | `onChange` | `(checked: boolean) => void` | - | Change handler |
26
+ | `label` | `string` | `''` | Optional label text |
27
+
28
+ ## Examples
29
+
30
+ ### Basic Switch
31
+
32
+ ```stx
33
+ <script>
34
+ let enabled = false
35
+
36
+ function handleChange(checked) {
37
+ enabled = checked
38
+ console.log('Switch is now:', checked)
39
+ }
40
+
41
+ module.exports = { enabled, handleChange }
42
+ </script>
43
+
44
+ @component('Switch', {
45
+ checked: enabled,
46
+ onChange: handleChange
47
+ })
48
+ ```
49
+
50
+ ### With Label
51
+
52
+ ```stx
53
+ @component('Switch', {
54
+ checked: true,
55
+ label: 'Enable notifications',
56
+ onChange: handleChange
57
+ })
58
+ ```
59
+
60
+ ### Sizes
61
+
62
+ ```stx
63
+ @component('Switch', { size: 'sm', checked: true })
64
+
65
+ @component('Switch', { size: 'md', checked: true })
66
+
67
+ @component('Switch', { size: 'lg', checked: true })
68
+ ```
69
+
70
+ ### Disabled State
71
+
72
+ ```stx
73
+ @component('Switch', {
74
+ checked: true,
75
+ disabled: true,
76
+ label: 'Cannot toggle'
77
+ })
78
+ ```
79
+
80
+ ### Custom Styling
81
+
82
+ ```stx
83
+ @component('Switch', {
84
+ checked: true,
85
+ className: 'my-4'
86
+ })
87
+ ```
88
+
89
+ ## Accessibility
90
+
91
+ The Switch component follows WAI-ARIA best practices:
92
+
93
+ - Proper `role="switch"` attribute
94
+ - `aria-checked` reflects the current state
95
+ - `aria-disabled` when disabled
96
+ - Screen reader text via `sr-only` class
97
+ - Keyboard accessible (can be toggled with Space/Enter)
98
+ - Focus visible ring for keyboard navigation
99
+
100
+ ## Features
101
+
102
+ - Smooth animations and transitions
103
+ - Three size options (sm, md, lg)
104
+ - Dark mode support
105
+ - Disabled state
106
+ - Optional label
107
+ - Accessible by default
108
+ - Headwind utility classes for easy customization
109
+ - Inspired by Headless UI design
110
+
111
+ ## Styling
112
+
113
+ The component uses headwind utility classes for styling. You can customize it by:
114
+
115
+ 1. Passing additional classes via `className` prop
116
+ 2. Modifying the component source
117
+ 3. Using headwind configuration to adjust the design system
118
+
119
+ ### Color Customization
120
+
121
+ To customize colors, you can:
122
+
123
+ - Override the checked state background color in your headwind config
124
+ - Pass custom classes via `className` prop
125
+ - Modify the component's source code
126
+
127
+ Example:
128
+ ```stx
129
+ @component('Switch', {
130
+ checked: true,
131
+ className: '!bg-green-500'
132
+ })
133
+ ```
@@ -0,0 +1,74 @@
1
+ <script>
2
+ export const checked = $props.checked || false
3
+ export const disabled = $props.disabled || false
4
+ export const size = $props.size || 'md'
5
+ export const className = $props.className || ''
6
+ export const onChange = $props.onChange
7
+ export const label = $props.label || ''
8
+
9
+ // Size configurations using headwind
10
+ const sizeConfig = {
11
+ sm: {
12
+ track: 'h-5 w-9',
13
+ thumb: 'h-4 w-4',
14
+ translate: 'translate-x-4',
15
+ },
16
+ md: {
17
+ track: 'h-[38px] w-[74px]',
18
+ thumb: 'h-[34px] w-[34px]',
19
+ translate: 'translate-x-9',
20
+ },
21
+ lg: {
22
+ track: 'h-12 w-24',
23
+ thumb: 'h-11 w-11',
24
+ translate: 'translate-x-12',
25
+ },
26
+ }
27
+
28
+ const config = sizeConfig[size]
29
+
30
+ // Track classes
31
+ const trackBaseClasses = 'relative inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-300 dark:focus-visible:ring-offset-neutral-700'
32
+ const trackStateClasses = checked
33
+ ? 'bg-blue-500 dark:bg-blue-600'
34
+ : 'bg-neutral-400 dark:bg-neutral-600'
35
+ const trackDisabledClasses = disabled
36
+ ? 'opacity-50 cursor-not-allowed pointer-events-none'
37
+ : ''
38
+
39
+ export const trackClasses = `${trackBaseClasses} ${trackStateClasses} ${trackDisabledClasses} ${config.track} ${className}`.trim()
40
+
41
+ // Thumb classes
42
+ const thumbBaseClasses = 'pointer-events-none inline-block transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out'
43
+ const thumbPositionClasses = checked ? config.translate : 'translate-x-0'
44
+ export const thumbClasses = `${thumbBaseClasses} ${thumbPositionClasses} ${config.thumb}`.trim()
45
+
46
+ // Toggle function
47
+ export function toggle() {
48
+ if (disabled) return
49
+ onChange?.(!checked)
50
+ }
51
+ </script>
52
+
53
+ <label class="inline-flex items-center gap-3 cursor-pointer">
54
+ @if(label)
55
+ <span class="text-sm font-medium text-neutral-700 dark:text-neutral-300">
56
+ {{ label }}
57
+ </span>
58
+ @endif
59
+
60
+ <button
61
+ type="button"
62
+ role="switch"
63
+ aria-checked="{{ checked ? 'true' : 'false' }}"
64
+ class="{{ trackClasses }}"
65
+ @if(disabled)disabled aria-disabled="true"@endif
66
+ @click="toggle"
67
+ >
68
+ <span class="sr-only">@if(label){{ label }}@else Toggle switch@endif</span>
69
+ <span
70
+ aria-hidden="true"
71
+ class="{{ thumbClasses }}"
72
+ />
73
+ </button>
74
+ </label>
@@ -0,0 +1,10 @@
1
+ export { default as Switch } from './Switch.stx'
2
+
3
+ export interface SwitchProps {
4
+ checked?: boolean
5
+ disabled?: boolean
6
+ size?: 'sm' | 'md' | 'lg'
7
+ className?: string
8
+ onChange?: (checked: boolean) => void
9
+ label?: string
10
+ }
@@ -0,0 +1,39 @@
1
+ # Table Component
2
+
3
+ Responsive table component with sorting and styling options.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @stacksjs/components
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```stx
14
+ @component('Table')
15
+ @component('TableHead')
16
+ @component('TableRow')
17
+ @component('TableHeader') Name @endcomponent
18
+ @component('TableHeader') Email @endcomponent
19
+ @component('TableHeader') Role @endcomponent
20
+ @endcomponent
21
+ @endcomponent
22
+
23
+ @component('TableBody')
24
+ @component('TableRow')
25
+ @component('TableCell') John Doe @endcomponent
26
+ @component('TableCell') john@example.com @endcomponent
27
+ @component('TableCell') Admin @endcomponent
28
+ @endcomponent
29
+ @endcomponent
30
+ @endcomponent
31
+ ```
32
+
33
+ ## Features
34
+
35
+ - Responsive design
36
+ - Sortable columns
37
+ - Striped rows
38
+ - Hover effects
39
+ - Dark mode support
@@ -0,0 +1,14 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const striped = $props.striped || false
4
+ export const hoverable = $props.hoverable !== false
5
+
6
+ const baseClasses = 'min-w-full divide-y divide-neutral-200 dark:divide-neutral-700'
7
+ const tableClasses = `${baseClasses} ${className}`.trim()
8
+ </script>
9
+
10
+ <div class="overflow-x-auto">
11
+ <table class="{{ tableClasses }}">
12
+ <slot :striped="striped" :hoverable="hoverable" />
13
+ </table>
14
+ </div>
@@ -0,0 +1,10 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+
4
+ const baseClasses = 'bg-white divide-y divide-neutral-200 dark:bg-neutral-900 dark:divide-neutral-700'
5
+ const bodyClasses = `${baseClasses} ${className}`.trim()
6
+ </script>
7
+
8
+ <tbody class="{{ bodyClasses }}">
9
+ <slot />
10
+ </tbody>
@@ -0,0 +1,10 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+
4
+ const baseClasses = 'px-6 py-4 whitespace-nowrap text-sm text-neutral-900 dark:text-neutral-100'
5
+ const cellClasses = `${baseClasses} ${className}`.trim()
6
+ </script>
7
+
8
+ <td class="{{ cellClasses }}">
9
+ <slot />
10
+ </td>
@@ -0,0 +1,10 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+
4
+ const baseClasses = 'bg-neutral-50 dark:bg-neutral-800'
5
+ const headClasses = `${baseClasses} ${className}`.trim()
6
+ </script>
7
+
8
+ <thead class="{{ headClasses }}">
9
+ <slot />
10
+ </thead>
@@ -0,0 +1,13 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const sortable = $props.sortable || false
4
+ export const onClick = $props.onClick
5
+
6
+ const baseClasses = 'px-6 py-3 text-left text-xs font-medium text-neutral-500 uppercase tracking-wider dark:text-neutral-400'
7
+ const sortableClasses = sortable ? 'cursor-pointer hover:text-neutral-700 dark:hover:text-neutral-200' : ''
8
+ const headerClasses = `${baseClasses} ${sortableClasses} ${className}`.trim()
9
+ </script>
10
+
11
+ <th class="{{ headerClasses }}" @if(sortable)@click="onClick"@endif>
12
+ <slot />
13
+ </th>
@@ -0,0 +1,11 @@
1
+ <script>
2
+ export const className = $props.className || ''
3
+ export const hoverable = $props.hoverable !== false
4
+
5
+ const hoverClasses = hoverable ? 'hover:bg-neutral-50 dark:hover:bg-neutral-800' : ''
6
+ const rowClasses = `${hoverClasses} ${className}`.trim()
7
+ </script>
8
+
9
+ <tr class="{{ rowClasses }}">
10
+ <slot />
11
+ </tr>
@@ -0,0 +1,35 @@
1
+ export { default as Table } from './Table.stx'
2
+ export { default as TableBody } from './TableBody.stx'
3
+ export { default as TableCell } from './TableCell.stx'
4
+ export { default as TableHead } from './TableHead.stx'
5
+ export { default as TableHeader } from './TableHeader.stx'
6
+ export { default as TableRow } from './TableRow.stx'
7
+
8
+ export interface TableProps {
9
+ className?: string
10
+ striped?: boolean
11
+ hoverable?: boolean
12
+ }
13
+
14
+ export interface TableHeadProps {
15
+ className?: string
16
+ }
17
+
18
+ export interface TableBodyProps {
19
+ className?: string
20
+ }
21
+
22
+ export interface TableRowProps {
23
+ className?: string
24
+ hoverable?: boolean
25
+ }
26
+
27
+ export interface TableHeaderProps {
28
+ className?: string
29
+ sortable?: boolean
30
+ onClick?: () => void
31
+ }
32
+
33
+ export interface TableCellProps {
34
+ className?: string
35
+ }
@@ -0,0 +1,145 @@
1
+ <script>
2
+ export const tabs = $props.tabs || []
3
+ export const defaultTab = $props.defaultTab || 0
4
+ export const orientation = $props.orientation || 'horizontal'
5
+ export const variant = $props.variant || 'line'
6
+ export const className = $props.className || ''
7
+ export const onChange = $props.onChange
8
+
9
+ let activeTab = defaultTab
10
+
11
+ function selectTab(index) {
12
+ activeTab = index
13
+ if (onChange) {
14
+ onChange(index, tabs[index])
15
+ }
16
+ }
17
+
18
+ // Orientation classes
19
+ const orientationClasses = {
20
+ horizontal: 'flex-col',
21
+ vertical: 'flex-row',
22
+ }
23
+
24
+ // Tab list orientation classes
25
+ const tabListOrientationClasses = {
26
+ horizontal: 'flex-row border-b',
27
+ vertical: 'flex-col border-r',
28
+ }
29
+
30
+ // Variant classes for tab list
31
+ const variantClasses = {
32
+ line: 'border-gray-200 dark:border-gray-700',
33
+ pills: 'gap-2 border-0 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg',
34
+ enclosed: 'border-gray-200 dark:border-gray-700',
35
+ }
36
+
37
+ // Tab button classes based on variant
38
+ function getTabClasses(index, variant, orientation) {
39
+ const isActive = index === activeTab
40
+ const baseClasses = 'px-4 py-2 font-medium text-sm transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900'
41
+
42
+ if (variant === 'line') {
43
+ const borderPosition = orientation === 'horizontal' ? 'border-b-2' : 'border-r-2'
44
+ return `${baseClasses} ${borderPosition} ${isActive ? 'border-blue-500 text-blue-600 dark:text-blue-400' : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600'}`
45
+ } else if (variant === 'pills') {
46
+ return `${baseClasses} rounded-md ${isActive ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'}`
47
+ } else if (variant === 'enclosed') {
48
+ const borderClasses = orientation === 'horizontal' ? 'border-t border-l border-r rounded-t-md' : 'border-t border-l border-b rounded-l-md'
49
+ return `${baseClasses} ${borderClasses} ${isActive ? 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 -mb-px' : 'bg-gray-50 dark:bg-gray-900 border-transparent hover:border-gray-200 dark:hover:border-gray-700'}`
50
+ }
51
+ }
52
+
53
+ export const containerClasses = `flex ${orientationClasses[orientation]} ${className}`.trim()
54
+ export const tabListClasses = `flex ${tabListOrientationClasses[orientation]} ${variantClasses[variant]}`.trim()
55
+ export const tabPanelClasses = `p-4 focus:outline-none`.trim()
56
+ </script>
57
+
58
+ <div class="{{ containerClasses }}">
59
+ <!-- Tab List -->
60
+ <div
61
+ class="{{ tabListClasses }}"
62
+ role="tablist"
63
+ aria-orientation="{{ orientation }}"
64
+ >
65
+ @foreach(tab in tabs)
66
+ <button
67
+ type="button"
68
+ class="{{ getTabClasses($loop.index, variant, orientation) }}"
69
+ role="tab"
70
+ aria-selected="{{ activeTab === $loop.index ? 'true' : 'false' }}"
71
+ aria-controls="tab-panel-{{ $loop.index }}"
72
+ id="tab-{{ $loop.index }}"
73
+ tabindex="{{ activeTab === $loop.index ? '0' : '-1' }}"
74
+ onclick="selectTab({{ $loop.index }})"
75
+ onkeydown="handleKeyDown(event, {{ $loop.index }})"
76
+ >
77
+ @if(tab.icon)
78
+ <span class="inline-flex items-center gap-2">
79
+ <span innerHTML="{!! tab.icon !!}"></span>
80
+ <span>{{ tab.label }}</span>
81
+ </span>
82
+ @else
83
+ {{ tab.label }}
84
+ @endif
85
+ </button>
86
+ @endforeach
87
+ </div>
88
+
89
+ <!-- Tab Panels -->
90
+ @foreach(tab in tabs)
91
+ @if(activeTab === $loop.index)
92
+ <div
93
+ id="tab-panel-{{ $loop.index }}"
94
+ class="{{ tabPanelClasses }}"
95
+ role="tabpanel"
96
+ aria-labelledby="tab-{{ $loop.index }}"
97
+ tabindex="0"
98
+ >
99
+ {!! tab.content !!}
100
+ </div>
101
+ @endif
102
+ @endforeach
103
+ </div>
104
+
105
+ <script>
106
+ function handleKeyDown(event, currentIndex) {
107
+ const tabCount = tabs.length
108
+ let newIndex = currentIndex
109
+
110
+ if (orientation === 'horizontal') {
111
+ if (event.key === 'ArrowRight') {
112
+ event.preventDefault()
113
+ newIndex = (currentIndex + 1) % tabCount
114
+ } else if (event.key === 'ArrowLeft') {
115
+ event.preventDefault()
116
+ newIndex = (currentIndex - 1 + tabCount) % tabCount
117
+ }
118
+ } else {
119
+ if (event.key === 'ArrowDown') {
120
+ event.preventDefault()
121
+ newIndex = (currentIndex + 1) % tabCount
122
+ } else if (event.key === 'ArrowUp') {
123
+ event.preventDefault()
124
+ newIndex = (currentIndex - 1 + tabCount) % tabCount
125
+ }
126
+ }
127
+
128
+ if (event.key === 'Home') {
129
+ event.preventDefault()
130
+ newIndex = 0
131
+ } else if (event.key === 'End') {
132
+ event.preventDefault()
133
+ newIndex = tabCount - 1
134
+ }
135
+
136
+ if (newIndex !== currentIndex) {
137
+ selectTab(newIndex)
138
+ // Focus the new tab
139
+ if (typeof window !== 'undefined') {
140
+ const newTab = document.getElementById(`tab-${newIndex}`)
141
+ if (newTab) newTab.focus()
142
+ }
143
+ }
144
+ }
145
+ </script>
@@ -0,0 +1,16 @@
1
+ export { default as Tabs } from './Tabs.stx'
2
+
3
+ export interface Tab {
4
+ label: string
5
+ content: string
6
+ icon?: string
7
+ }
8
+
9
+ export interface TabsProps {
10
+ tabs: Tab[]
11
+ defaultTab?: number
12
+ orientation?: 'horizontal' | 'vertical'
13
+ variant?: 'line' | 'pills' | 'enclosed'
14
+ onChange?: (index: number, tab: Tab) => void
15
+ className?: string
16
+ }
@@ -0,0 +1,101 @@
1
+ <script>
2
+ export const to = $props.to || 'body'
3
+ export const disabled = $props.disabled || false
4
+ export const defer = $props.defer || false
5
+
6
+ let teleportContainer = null
7
+ let teleportId = `teleport-${Math.random().toString(36).substr(2, 9)}`
8
+
9
+ // Teleport content to target
10
+ function teleport() {
11
+ if (typeof window === 'undefined' || disabled)
12
+ return
13
+
14
+ // Get target element
15
+ const targetElement = typeof to === 'string'
16
+ ? document.querySelector(to)
17
+ : to
18
+
19
+ if (!targetElement) {
20
+ console.warn(`Teleport target "${to}" not found`)
21
+ return
22
+ }
23
+
24
+ // Get source element
25
+ const sourceElement = document.getElementById(teleportId)
26
+ if (!sourceElement)
27
+ return
28
+
29
+ // Create teleport container if needed
30
+ if (!teleportContainer) {
31
+ teleportContainer = document.createElement('div')
32
+ teleportContainer.setAttribute('data-teleport-id', teleportId)
33
+ }
34
+
35
+ // Move all child nodes to container
36
+ while (sourceElement.firstChild) {
37
+ teleportContainer.appendChild(sourceElement.firstChild)
38
+ }
39
+
40
+ // Append to target
41
+ targetElement.appendChild(teleportContainer)
42
+ }
43
+
44
+ // Return content back to original location
45
+ function returnContent() {
46
+ if (!teleportContainer || !teleportContainer.parentNode)
47
+ return
48
+
49
+ const sourceElement = document.getElementById(teleportId)
50
+ if (!sourceElement)
51
+ return
52
+
53
+ // Move content back
54
+ while (teleportContainer.firstChild) {
55
+ sourceElement.appendChild(teleportContainer.firstChild)
56
+ }
57
+
58
+ // Remove container
59
+ teleportContainer.parentNode.removeChild(teleportContainer)
60
+ teleportContainer = null
61
+ }
62
+
63
+ // Initialize teleport
64
+ if (typeof window !== 'undefined') {
65
+ if (defer) {
66
+ // Defer until next tick
67
+ window.addEventListener('DOMContentLoaded', () => {
68
+ setTimeout(teleport, 0)
69
+ })
70
+ }
71
+ else {
72
+ // Immediate
73
+ window.addEventListener('DOMContentLoaded', teleport)
74
+ }
75
+
76
+ // Cleanup on page unload
77
+ window.addEventListener('beforeunload', returnContent)
78
+ }
79
+ </script>
80
+
81
+ @if(disabled)
82
+ <!-- Render in place if disabled -->
83
+ <slot />
84
+ @else
85
+ <!-- Source container -->
86
+ <div id="{{ teleportId }}" data-teleport-source="true">
87
+ <slot />
88
+ </div>
89
+ @endif
90
+
91
+ <script>
92
+ // Trigger teleport after render
93
+ if (typeof window !== 'undefined' && document.readyState === 'complete') {
94
+ if (defer) {
95
+ setTimeout(teleport, 0)
96
+ }
97
+ else {
98
+ teleport()
99
+ }
100
+ }
101
+ </script>
@@ -0,0 +1,7 @@
1
+ export { default as Teleport } from './Teleport.stx'
2
+
3
+ export interface TeleportProps {
4
+ to?: string | HTMLElement
5
+ disabled?: boolean
6
+ defer?: boolean
7
+ }