kiso 0.1.0.pre → 0.2.0.pre

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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -2
  3. data/README.md +67 -27
  4. data/Rakefile +8 -0
  5. data/app/assets/tailwind/kiso/checkbox.css +18 -0
  6. data/app/assets/tailwind/kiso/color-mode.css +9 -0
  7. data/app/assets/tailwind/kiso/dashboard.css +194 -0
  8. data/app/assets/tailwind/kiso/engine.css +117 -0
  9. data/app/assets/tailwind/kiso/input-otp.css +10 -0
  10. data/app/assets/tailwind/kiso/radio-group.css +17 -0
  11. data/app/helpers/kiso/component_helper.rb +46 -27
  12. data/app/helpers/kiso/icon_helper.rb +53 -9
  13. data/app/helpers/kiso/theme_helper.rb +38 -0
  14. data/app/javascript/controllers/kiso/combobox_controller.js +616 -0
  15. data/app/javascript/controllers/kiso/command_controller.js +184 -0
  16. data/app/javascript/controllers/kiso/command_dialog_controller.js +104 -0
  17. data/app/javascript/controllers/kiso/dropdown_menu_controller.js +684 -0
  18. data/app/javascript/controllers/kiso/index.d.ts +12 -0
  19. data/app/javascript/controllers/kiso/index.js +42 -0
  20. data/app/javascript/controllers/kiso/input_otp_controller.js +195 -0
  21. data/app/javascript/controllers/kiso/popover_controller.js +254 -0
  22. data/app/javascript/controllers/kiso/select_controller.js +307 -0
  23. data/app/javascript/controllers/kiso/sidebar_controller.js +84 -0
  24. data/app/javascript/controllers/kiso/theme_controller.js +89 -0
  25. data/app/javascript/controllers/kiso/toggle_controller.js +24 -0
  26. data/app/javascript/controllers/kiso/toggle_group_controller.js +128 -0
  27. data/app/javascript/kiso/utils/focusable.js +8 -0
  28. data/app/javascript/kiso/utils/highlight.js +43 -0
  29. data/app/javascript/kiso/utils/positioning.js +86 -0
  30. data/app/javascript/kiso/vendor/floating-ui-core.js +1 -0
  31. data/app/javascript/kiso/vendor/floating-ui-dom.js +1 -0
  32. data/app/views/kiso/components/_alert.html.erb +1 -1
  33. data/app/views/kiso/components/_avatar.html.erb +23 -0
  34. data/app/views/kiso/components/_badge.html.erb +1 -1
  35. data/app/views/kiso/components/_breadcrumb.html.erb +8 -0
  36. data/app/views/kiso/components/_button.html.erb +1 -1
  37. data/app/views/kiso/components/_card.html.erb +1 -1
  38. data/app/views/kiso/components/_checkbox.html.erb +7 -0
  39. data/app/views/kiso/components/_color_mode_button.html.erb +14 -0
  40. data/app/views/kiso/components/_color_mode_select.html.erb +24 -0
  41. data/app/views/kiso/components/_combobox.html.erb +12 -0
  42. data/app/views/kiso/components/_command.html.erb +7 -0
  43. data/app/views/kiso/components/_dashboard_group.html.erb +14 -0
  44. data/app/views/kiso/components/_dashboard_navbar.html.erb +7 -0
  45. data/app/views/kiso/components/_dashboard_panel.html.erb +7 -0
  46. data/app/views/kiso/components/_dashboard_sidebar.html.erb +11 -0
  47. data/app/views/kiso/components/_dashboard_toolbar.html.erb +7 -0
  48. data/app/views/kiso/components/_dropdown_menu.html.erb +7 -0
  49. data/app/views/kiso/components/{_empty_state.html.erb → _empty.html.erb} +2 -2
  50. data/app/views/kiso/components/_field.html.erb +12 -0
  51. data/app/views/kiso/components/_field_group.html.erb +7 -0
  52. data/app/views/kiso/components/_field_set.html.erb +7 -0
  53. data/app/views/kiso/components/_input.html.erb +8 -0
  54. data/app/views/kiso/components/_input_group.html.erb +8 -0
  55. data/app/views/kiso/components/_input_otp.html.erb +22 -0
  56. data/app/views/kiso/components/_kbd.html.erb +7 -0
  57. data/app/views/kiso/components/_label.html.erb +5 -0
  58. data/app/views/kiso/components/_nav.html.erb +7 -0
  59. data/app/views/kiso/components/_pagination.html.erb +9 -0
  60. data/app/views/kiso/components/_popover.html.erb +8 -0
  61. data/app/views/kiso/components/_radio_group.html.erb +8 -0
  62. data/app/views/kiso/components/_select.html.erb +8 -0
  63. data/app/views/kiso/components/_select_native.html.erb +16 -0
  64. data/app/views/kiso/components/_separator.html.erb +1 -1
  65. data/app/views/kiso/components/_stats_card.html.erb +1 -1
  66. data/app/views/kiso/components/_stats_grid.html.erb +1 -1
  67. data/app/views/kiso/components/_switch.html.erb +10 -0
  68. data/app/views/kiso/components/_table.html.erb +2 -1
  69. data/app/views/kiso/components/_textarea.html.erb +9 -0
  70. data/app/views/kiso/components/_toggle.html.erb +12 -0
  71. data/app/views/kiso/components/_toggle_group.html.erb +12 -0
  72. data/app/views/kiso/components/alert/_description.html.erb +1 -1
  73. data/app/views/kiso/components/alert/_title.html.erb +1 -1
  74. data/app/views/kiso/components/avatar/_badge.html.erb +7 -0
  75. data/app/views/kiso/components/avatar/_fallback.html.erb +7 -0
  76. data/app/views/kiso/components/avatar/_group.html.erb +7 -0
  77. data/app/views/kiso/components/avatar/_group_count.html.erb +7 -0
  78. data/app/views/kiso/components/avatar/_image.html.erb +6 -0
  79. data/app/views/kiso/components/breadcrumb/_ellipsis.html.erb +10 -0
  80. data/app/views/kiso/components/breadcrumb/_item.html.erb +7 -0
  81. data/app/views/kiso/components/breadcrumb/_link.html.erb +7 -0
  82. data/app/views/kiso/components/breadcrumb/_list.html.erb +7 -0
  83. data/app/views/kiso/components/breadcrumb/_page.html.erb +9 -0
  84. data/app/views/kiso/components/breadcrumb/_separator.html.erb +9 -0
  85. data/app/views/kiso/components/card/_action.html.erb +7 -0
  86. data/app/views/kiso/components/card/_content.html.erb +1 -1
  87. data/app/views/kiso/components/card/_description.html.erb +1 -1
  88. data/app/views/kiso/components/card/_footer.html.erb +1 -1
  89. data/app/views/kiso/components/card/_header.html.erb +1 -1
  90. data/app/views/kiso/components/card/_title.html.erb +1 -1
  91. data/app/views/kiso/components/combobox/_chip.html.erb +19 -0
  92. data/app/views/kiso/components/combobox/_chips.html.erb +20 -0
  93. data/app/views/kiso/components/combobox/_chips_input.html.erb +10 -0
  94. data/app/views/kiso/components/combobox/_content.html.erb +9 -0
  95. data/app/views/kiso/components/combobox/_empty.html.erb +9 -0
  96. data/app/views/kiso/components/combobox/_group.html.erb +8 -0
  97. data/app/views/kiso/components/combobox/_input.html.erb +23 -0
  98. data/app/views/kiso/components/combobox/_item.html.erb +19 -0
  99. data/app/views/kiso/components/combobox/_label.html.erb +7 -0
  100. data/app/views/kiso/components/combobox/_list.html.erb +10 -0
  101. data/app/views/kiso/components/combobox/_separator.html.erb +6 -0
  102. data/app/views/kiso/components/command/_dialog.html.erb +11 -0
  103. data/app/views/kiso/components/command/_empty.html.erb +9 -0
  104. data/app/views/kiso/components/command/_group.html.erb +14 -0
  105. data/app/views/kiso/components/command/_input.html.erb +16 -0
  106. data/app/views/kiso/components/command/_item.html.erb +13 -0
  107. data/app/views/kiso/components/command/_list.html.erb +10 -0
  108. data/app/views/kiso/components/command/_separator.html.erb +7 -0
  109. data/app/views/kiso/components/command/_shortcut.html.erb +7 -0
  110. data/app/views/kiso/components/dashboard_navbar/_toggle.html.erb +11 -0
  111. data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +12 -0
  112. data/app/views/kiso/components/dashboard_sidebar/_footer.html.erb +7 -0
  113. data/app/views/kiso/components/dashboard_sidebar/_header.html.erb +7 -0
  114. data/app/views/kiso/components/dashboard_sidebar/_toggle.html.erb +11 -0
  115. data/app/views/kiso/components/dashboard_toolbar/_left.html.erb +7 -0
  116. data/app/views/kiso/components/dashboard_toolbar/_right.html.erb +7 -0
  117. data/app/views/kiso/components/dropdown_menu/_checkbox_item.html.erb +18 -0
  118. data/app/views/kiso/components/dropdown_menu/_content.html.erb +10 -0
  119. data/app/views/kiso/components/dropdown_menu/_group.html.erb +8 -0
  120. data/app/views/kiso/components/dropdown_menu/_item.html.erb +15 -0
  121. data/app/views/kiso/components/dropdown_menu/_label.html.erb +8 -0
  122. data/app/views/kiso/components/dropdown_menu/_radio_group.html.erb +10 -0
  123. data/app/views/kiso/components/dropdown_menu/_radio_item.html.erb +19 -0
  124. data/app/views/kiso/components/dropdown_menu/_separator.html.erb +6 -0
  125. data/app/views/kiso/components/dropdown_menu/_shortcut.html.erb +7 -0
  126. data/app/views/kiso/components/dropdown_menu/_sub.html.erb +8 -0
  127. data/app/views/kiso/components/dropdown_menu/_sub_content.html.erb +10 -0
  128. data/app/views/kiso/components/dropdown_menu/_sub_trigger.html.erb +12 -0
  129. data/app/views/kiso/components/dropdown_menu/_trigger.html.erb +9 -0
  130. data/app/views/kiso/components/empty/_content.html.erb +7 -0
  131. data/app/views/kiso/components/empty/_description.html.erb +7 -0
  132. data/app/views/kiso/components/empty/_header.html.erb +7 -0
  133. data/app/views/kiso/components/empty/_media.html.erb +7 -0
  134. data/app/views/kiso/components/empty/_title.html.erb +7 -0
  135. data/app/views/kiso/components/field/_content.html.erb +7 -0
  136. data/app/views/kiso/components/field/_description.html.erb +7 -0
  137. data/app/views/kiso/components/field/_error.html.erb +22 -0
  138. data/app/views/kiso/components/field/_label.html.erb +5 -0
  139. data/app/views/kiso/components/field/_separator.html.erb +15 -0
  140. data/app/views/kiso/components/field/_title.html.erb +7 -0
  141. data/app/views/kiso/components/field_set/_legend.html.erb +9 -0
  142. data/app/views/kiso/components/input_group/_addon.html.erb +7 -0
  143. data/app/views/kiso/components/input_otp/_group.html.erb +7 -0
  144. data/app/views/kiso/components/input_otp/_separator.html.erb +8 -0
  145. data/app/views/kiso/components/input_otp/_slot.html.erb +11 -0
  146. data/app/views/kiso/components/kbd/_group.html.erb +7 -0
  147. data/app/views/kiso/components/nav/_item.html.erb +15 -0
  148. data/app/views/kiso/components/nav/_section.html.erb +37 -0
  149. data/app/views/kiso/components/nav/_section_title.html.erb +7 -0
  150. data/app/views/kiso/components/pagination/_content.html.erb +7 -0
  151. data/app/views/kiso/components/pagination/_ellipsis.html.erb +9 -0
  152. data/app/views/kiso/components/pagination/_item.html.erb +7 -0
  153. data/app/views/kiso/components/pagination/_link.html.erb +9 -0
  154. data/app/views/kiso/components/pagination/_next.html.erb +12 -0
  155. data/app/views/kiso/components/pagination/_previous.html.erb +12 -0
  156. data/app/views/kiso/components/popover/_anchor.html.erb +8 -0
  157. data/app/views/kiso/components/popover/_content.html.erb +11 -0
  158. data/app/views/kiso/components/popover/_description.html.erb +7 -0
  159. data/app/views/kiso/components/popover/_header.html.erb +7 -0
  160. data/app/views/kiso/components/popover/_title.html.erb +7 -0
  161. data/app/views/kiso/components/popover/_trigger.html.erb +9 -0
  162. data/app/views/kiso/components/radio_group/_item.html.erb +6 -0
  163. data/app/views/kiso/components/select/_content.html.erb +10 -0
  164. data/app/views/kiso/components/select/_group.html.erb +8 -0
  165. data/app/views/kiso/components/select/_item.html.erb +19 -0
  166. data/app/views/kiso/components/select/_label.html.erb +7 -0
  167. data/app/views/kiso/components/select/_separator.html.erb +6 -0
  168. data/app/views/kiso/components/select/_trigger.html.erb +13 -0
  169. data/app/views/kiso/components/select/_value.html.erb +11 -0
  170. data/app/views/kiso/components/stats_card/_description.html.erb +1 -1
  171. data/app/views/kiso/components/stats_card/_header.html.erb +1 -1
  172. data/app/views/kiso/components/stats_card/_label.html.erb +1 -1
  173. data/app/views/kiso/components/stats_card/_value.html.erb +1 -1
  174. data/app/views/kiso/components/table/_body.html.erb +1 -1
  175. data/app/views/kiso/components/table/_caption.html.erb +1 -1
  176. data/app/views/kiso/components/table/_cell.html.erb +1 -1
  177. data/app/views/kiso/components/table/_footer.html.erb +1 -1
  178. data/app/views/kiso/components/table/_head.html.erb +1 -1
  179. data/app/views/kiso/components/table/_header.html.erb +1 -1
  180. data/app/views/kiso/components/table/_row.html.erb +1 -1
  181. data/app/views/kiso/components/toggle_group/_item.html.erb +13 -0
  182. data/config/deploy.docs.yml +31 -0
  183. data/config/deploy.yml +34 -0
  184. data/config/importmap.rb +10 -0
  185. data/lib/kiso/cli/base.rb +15 -0
  186. data/lib/kiso/cli/icons.rb +2 -1
  187. data/lib/kiso/cli/main.rb +6 -0
  188. data/lib/kiso/cli/make.rb +22 -12
  189. data/lib/kiso/configuration.rb +54 -0
  190. data/lib/kiso/engine.rb +36 -1
  191. data/lib/kiso/theme_overrides.rb +130 -0
  192. data/lib/kiso/themes/alert.rb +16 -1
  193. data/lib/kiso/themes/avatar.rb +53 -0
  194. data/lib/kiso/themes/badge.rb +15 -5
  195. data/lib/kiso/themes/breadcrumb.rb +44 -0
  196. data/lib/kiso/themes/button.rb +15 -2
  197. data/lib/kiso/themes/card.rb +18 -2
  198. data/lib/kiso/themes/checkbox.rb +33 -0
  199. data/lib/kiso/themes/color_mode_button.rb +15 -0
  200. data/lib/kiso/themes/color_mode_select.rb +7 -0
  201. data/lib/kiso/themes/combobox.rb +97 -0
  202. data/lib/kiso/themes/command.rb +79 -0
  203. data/lib/kiso/themes/dashboard.rb +51 -0
  204. data/lib/kiso/themes/dropdown_menu.rb +108 -0
  205. data/lib/kiso/themes/empty.rb +54 -0
  206. data/lib/kiso/themes/field.rb +76 -0
  207. data/lib/kiso/themes/field_group.rb +15 -0
  208. data/lib/kiso/themes/field_set.rb +32 -0
  209. data/lib/kiso/themes/input.rb +33 -0
  210. data/lib/kiso/themes/input_group.rb +39 -0
  211. data/lib/kiso/themes/input_otp.rb +46 -0
  212. data/lib/kiso/themes/kbd.rb +31 -0
  213. data/lib/kiso/themes/label.rb +16 -0
  214. data/lib/kiso/themes/nav.rb +27 -0
  215. data/lib/kiso/themes/pagination.rb +73 -0
  216. data/lib/kiso/themes/popover.rb +32 -0
  217. data/lib/kiso/themes/radio_group.rb +43 -0
  218. data/lib/kiso/themes/select.rb +78 -0
  219. data/lib/kiso/themes/select_native.rb +49 -0
  220. data/lib/kiso/themes/separator.rb +8 -2
  221. data/lib/kiso/themes/shared.rb +51 -0
  222. data/lib/kiso/themes/stats_card.rb +26 -14
  223. data/lib/kiso/themes/switch.rb +56 -0
  224. data/lib/kiso/themes/table.rb +18 -15
  225. data/lib/kiso/themes/textarea.rb +33 -0
  226. data/lib/kiso/themes/toggle.rb +71 -0
  227. data/lib/kiso/themes/toggle_group.rb +13 -0
  228. data/lib/kiso/version.rb +4 -1
  229. data/lib/kiso.rb +70 -2
  230. metadata +183 -22
  231. data/app/views/kiso/components/empty_state/_content.html.erb +0 -7
  232. data/app/views/kiso/components/empty_state/_description.html.erb +0 -7
  233. data/app/views/kiso/components/empty_state/_header.html.erb +0 -7
  234. data/app/views/kiso/components/empty_state/_media.html.erb +0 -7
  235. data/app/views/kiso/components/empty_state/_title.html.erb +0 -7
  236. data/lib/kiso/themes/empty_state.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cd911512ec3d322db2ec1fd6ff2a24660fbc28e654424e710007e77a2e1317b
4
- data.tar.gz: 8506e424c434844d10dd1dd7899ace376a65119b22e4602ea1ded3280ea85e86
3
+ metadata.gz: a28e88755cea2967813e27293413e587cef78cd5466240e16034df5c512b887d
4
+ data.tar.gz: 6f8cee28fae6476f598f384ef4898726499f39e0ed99e4a785f64ef08f517205
5
5
  SHA512:
6
- metadata.gz: ffa23334590c70c3576bba3f3cbab806d4965d1c673e12652f1c976f3a213d02a88fa568a2773d62a484eae7af9a4a62b018c3eed8fd04ecb383f7e122274b0f
7
- data.tar.gz: 371226d21fc79972ff6b7fc222f59b7fa81a5a3fc660cc6f73bb2bfc5986a22ba3200d05a360551c249583b33930f0370e899bb6af3bd8497335da4d80eddd39
6
+ metadata.gz: 3caa5d60dd0339c04dfd2c7cd03695dcac54d9c9c91fe6b082b9fb6adc79bca73a8e7d9c0de9c91d50bba095f1dd738032d2be04cc77b558ba6ba9e45144e7d9
7
+ data.tar.gz: 97b58647c61a6460cb72a9633bef14037026fd7c1be9d22b073e98f3de7c28d830b8f24f1fc628bc145d91b996954f7d7c22f49cc7a54ac9c35e420b3c877f1f
data/CHANGELOG.md CHANGED
@@ -7,11 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0.pre] - 2026-03-03
11
+
12
+ ### Added
13
+
14
+ - InputOTP component — one-time password input with individual character slots, auto-advance, paste support, and mobile SMS autofill via `autocomplete="one-time-code"`. Stimulus controller distributes a single transparent input to visual slot divs. Sub-parts: group, slot, separator. Dispatches `change` and `complete` events for auto-submit workflows.
15
+ - SelectNative component — styled native `<select>` with chevron icon overlay. Variant × size axes matching Input (outline/soft/ghost, sm/md/lg). No JavaScript required.
16
+ - Sidebar state variants — `kui-sidebar-open:` and `kui-sidebar-closed:` custom Tailwind variants for showing/hiding any element based on sidebar open/closed state. Composable with breakpoints (e.g., `kui-sidebar-open:lg:hidden`).
17
+ - Custom toggle icon override — sidebar toggle and collapse buttons accept a block to replace the default icon.
18
+ - Auto body base styles — engine CSS now applies `bg-background text-foreground antialiased` on `<body>` via `@layer base`, so host apps no longer need to add these classes manually.
19
+
20
+ ## [0.1.1.pre] - 2026-03-03
21
+
22
+ ### Added
23
+
24
+ - Dashboard layout system — sidebar, navbar, panel, toolbar, and nav components with cookie-persisted sidebar state
25
+ - Avatar component with image, fallback, badge, and group support
26
+ - Form components — Field, Label, Input, Textarea, InputGroup, Checkbox, RadioGroup, Switch, Select, Combobox
27
+ - Overlay components — Popover, DropdownMenu, Command palette
28
+ - Navigation components — Breadcrumb, Pagination
29
+ - Element components — Kbd, Toggle, ToggleGroup
30
+ - Dark mode system — `kiso_theme_script` helper, ColorModeButton, ColorModeSelect
31
+ - Floating UI positioning for popovers and dropdowns
32
+ - Global theme overrides via `Kiso.configure`
33
+ - Configurable default icons via `kiso_component_icon`
34
+ - Getting Started guide
35
+
36
+ ### Changed
37
+
38
+ - Renamed `kiso()` helper to `kui()` to avoid Rails route proxy collision
39
+ - Renamed `empty_state` to `empty` to match shadcn naming
40
+ - Adopted `data-slot` convention from shadcn v4
41
+
10
42
  ## [0.1.0.pre] - 2026-02-25
11
43
 
12
44
  ### Added
13
45
 
14
- - Core engine with `kiso()` component helper and `component_tag` builder
46
+ - Core engine with `kui()` component helper and `kiso_prepare_options` builder
15
47
  - `class_variants` + `tailwind_merge` integration for variant definitions
16
48
  - Theme CSS with 7 palettes, surface tokens, and dark mode
17
49
  - Badge component (color × variant × size, pill shape, SVG handling)
@@ -23,5 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
55
  - Lookbook component previews
24
56
  - Bridgetown documentation site
25
57
 
26
- [Unreleased]: https://github.com/steveclarke/kiso/compare/v0.1.0.pre...HEAD
58
+ [Unreleased]: https://github.com/steveclarke/kiso/compare/v0.2.0.pre...HEAD
59
+ [0.2.0.pre]: https://github.com/steveclarke/kiso/releases/tag/v0.2.0.pre
60
+ [0.1.1.pre]: https://github.com/steveclarke/kiso/releases/tag/v0.1.1.pre
27
61
  [0.1.0.pre]: https://github.com/steveclarke/kiso/releases/tag/v0.1.0.pre
data/README.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  UI components for Rails. Built on ERB, Tailwind CSS, and Hotwire.
4
4
 
5
- Add one gem and get badges, buttons, cards, alerts, and more. They all work with screen readers and dark mode. The look follows [shadcn/ui](https://ui.shadcn.com).
5
+ Add one gem and get badges, buttons, cards, alerts, and more. They all work with screen readers and dark mode.
6
6
 
7
7
  No React. No extra build step. Just ERB with [class_variants](https://github.com/avo-hq/class_variants) for styling.
8
8
 
9
- Icons come from the [kiso-icons](https://github.com/steveclarke/kiso-icons) gem, which is bundled in.
9
+ > [!WARNING]
10
+ > Kiso is in early development and **not ready for production use**. The gem is published to reserve the name on RubyGems. APIs, component names, and theme tokens will change without notice. Watch the repo or check back in a few weeks.
10
11
 
11
- ## Installation
12
+ ## Getting started
13
+
14
+ Add the gem to your Gemfile and bundle:
12
15
 
13
16
  ```ruby
14
17
  # Gemfile
@@ -17,36 +20,68 @@ gem "kiso"
17
20
 
18
21
  ```bash
19
22
  bundle install
20
- bin/rails generate kiso:install
21
23
  ```
22
24
 
25
+ Then add one import to your Tailwind CSS entrypoint:
26
+
27
+ ```css
28
+ /* app/assets/tailwind/application.css */
29
+ @import "../builds/tailwind/kiso.css";
30
+ ```
31
+
32
+ And add the dark mode helper to your layout's `<head>`:
33
+
34
+ ```erb
35
+ <head>
36
+ <%= kiso_theme_script %>
37
+ <%= stylesheet_link_tag "tailwind" %>
38
+ <%= javascript_importmap_tags %>
39
+ </head>
40
+ ```
41
+
42
+ That's it. Helpers, importmap pins, asset paths, dark mode tokens, and base
43
+ body styles are all wired up automatically by the engine. See the
44
+ [Getting Started guide](https://kisoui.com/getting-started) for the full
45
+ walkthrough.
46
+
47
+ ### Bundler apps (esbuild, Vite, Bun)
48
+
49
+ If your app uses a JS bundler instead of importmaps, also install the npm
50
+ package for Stimulus controllers:
51
+
52
+ ```bash
53
+ npm install kiso-ui
54
+ ```
55
+
56
+ ```js
57
+ // app/javascript/controllers/index.js
58
+ import KisoUi from "kiso-ui"
59
+ KisoUi.start(application)
60
+ ```
61
+
62
+ Importmap apps get Stimulus controllers automatically — no npm install needed.
63
+
23
64
  ## Usage
24
65
 
25
- Use the `kiso()` helper to render components:
66
+ Use the `kui()` helper to render components:
26
67
 
27
68
  ```erb
28
- <%= kiso(:badge, variant: :primary) { "Active" } %>
69
+ <%= kui(:badge, color: :primary) { "Active" } %>
29
70
  ```
30
71
 
31
72
  Components are made of small parts. A card has a header, title, content, and footer:
32
73
 
33
74
  ```erb
34
- <%= kiso(:card) do %>
35
- <%= kiso(:card, :header) do %>
36
- <%= kiso(:card, :title, text: "Members") %>
75
+ <%= kui(:card) do %>
76
+ <%= kui(:card, :header) do %>
77
+ <%= kui(:card, :title, text: "Members") %>
37
78
  <% end %>
38
- <%= kiso(:card, :content) do %>
79
+ <%= kui(:card, :content) do %>
39
80
  ...
40
81
  <% end %>
41
82
  <% end %>
42
83
  ```
43
84
 
44
- Data attributes work on any HTML element too:
45
-
46
- ```erb
47
- <%= f.submit "Save", data: { component: "button", variant: "primary" } %>
48
- ```
49
-
50
85
  ## How it works
51
86
 
52
87
  Each component has two parts:
@@ -56,6 +91,10 @@ Each component has two parts:
56
91
 
57
92
  Colors use tokens like `bg-primary` and `text-muted`. They switch on their own in dark mode. No `dark:` classes needed.
58
93
 
94
+ ## Design system
95
+
96
+ Kiso follows a strict spatial system — consistent heights, padding, gaps, typography, border radius, and icon sizing across every component. See the [Design System](https://kisoui.com/design-system) page for the visual reference.
97
+
59
98
  ## Design principles
60
99
 
61
100
  1. **Native HTML first.** Use `<dialog>`, `[popover]`, `<details>` before adding JavaScript.
@@ -79,11 +118,14 @@ This starts [Lookbook](https://lookbook.build) on port 4001 with a Tailwind watc
79
118
 
80
119
  Cloned without `--recurse-submodules`? Run `bin/vendor init` to fetch the vendor repos.
81
120
 
82
- Run tests:
121
+ Run tests and lint:
83
122
 
84
123
  ```bash
85
- bundle exec rake test # all tests
86
- bundle exec standardrb # lint
124
+ bundle exec rake test # Ruby tests
125
+ npm run test:unit # JS unit tests (Vitest)
126
+ npm run test:e2e # E2E tests (Playwright, needs bin/dev)
127
+ bundle exec standardrb # Ruby lint
128
+ npm run lint && npm run fmt:check # JS lint + format check
87
129
  ```
88
130
 
89
131
  See [CONTRIBUTING.md](CONTRIBUTING.md) to help out.
@@ -93,12 +135,10 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) to help out.
93
135
  ```
94
136
  app/views/kiso/components/ ERB partials
95
137
  lib/kiso/themes/ Theme files (class_variants)
96
- app/helpers/kiso/ component_tag, kiso() helpers
97
- app/assets/stylesheets/kiso/ CSS (only transitions and pseudo-states)
98
- test/components/previews/ Lookbook previews
99
- test/dummy/ Dev Rails app
100
- vendor/shadcn-ui/ Layout reference (git submodule)
101
- vendor/nuxt-ui/ Theme reference (git submodule)
138
+ app/javascript/controllers/ Stimulus controllers (also shipped via npm as kiso-ui)
139
+ app/helpers/kiso/ kui(), kiso_prepare_options() helpers
140
+ app/assets/tailwind/kiso/ CSS (only transitions and pseudo-states)
141
+ lookbook/ Lookbook dev app (previews on port 4001)
102
142
  docs/ Docs site (Bridgetown)
103
143
  ```
104
144
 
@@ -106,11 +146,11 @@ docs/ Docs site (Bridgetown)
106
146
 
107
147
  - Ruby >= 3.3
108
148
  - Rails >= 8.0
109
- - [tailwindcss-rails](https://github.com/rails/tailwindcss-rails)
149
+ - A Tailwind CSS build pipeline ([tailwindcss-rails](https://github.com/rails/tailwindcss-rails) or [cssbundling-rails](https://github.com/rails/cssbundling-rails))
110
150
 
111
151
  ## Status
112
152
 
113
- Early development. See [VISION.md](VISION.md) for the roadmap and full component list.
153
+ Early development. APIs, component names, and theme tokens may change.
114
154
 
115
155
  ## License
116
156
 
data/Rakefile CHANGED
@@ -1,5 +1,13 @@
1
1
  require "bundler/setup"
2
2
  require "bundler/gem_tasks"
3
+ require "rake/testtask"
3
4
 
4
5
  APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
5
6
  load "rails/tasks/engine.rake"
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << "test"
10
+ t.pattern = "test/**/*_test.rb"
11
+ end
12
+
13
+ task default: :test
@@ -0,0 +1,18 @@
1
+ /* Checkbox indicator — CSS ::after for the checkmark since native <input>
2
+ can't contain child elements. Uses mask-image with the Lucide Check icon. */
3
+
4
+ [data-component="checkbox"] {
5
+ display: grid;
6
+ place-content: center;
7
+ }
8
+
9
+ [data-component="checkbox"]:checked::after {
10
+ content: '';
11
+ width: 0.875em;
12
+ height: 0.875em;
13
+ background-color: currentColor;
14
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
15
+ mask-size: contain;
16
+ mask-repeat: no-repeat;
17
+ mask-position: center;
18
+ }
@@ -0,0 +1,9 @@
1
+ /* ── Color Mode Components ────────────────────────────────────────────────────
2
+ Icon visibility for the color mode button. The .dark class on <html> drives
3
+ which icon is shown — sun in light mode, moon in dark mode. */
4
+
5
+ @layer components {
6
+ [data-slot="color-mode-icon-dark"] { display: none; }
7
+ .dark [data-slot="color-mode-icon-light"] { display: none; }
8
+ .dark [data-slot="color-mode-icon-dark"] { display: inline-block; }
9
+ }
@@ -0,0 +1,194 @@
1
+ /* ── Kiso Dashboard Layout ────────────────────────────────────────────────────
2
+ Tokens and mechanics for the full-screen sidebar + topbar dashboard shell.
3
+ Bundled with the engine — all apps get these tokens and layout rules.
4
+
5
+ Host apps can override any token in their own @theme block:
6
+ @theme { --sidebar-width: 18rem; }
7
+ */
8
+
9
+ /* ── Tokens ──────────────────────────────────────────────────────────────── */
10
+
11
+ @theme {
12
+ /* Sidebar geometry */
13
+ --sidebar-width: 16rem;
14
+
15
+ /* Layout heights */
16
+ --topbar-height: 3.5rem;
17
+
18
+ /* Sidebar surface tokens (light mode defaults) */
19
+ --sidebar-background: var(--color-white);
20
+ --sidebar-foreground: var(--color-zinc-900);
21
+ --sidebar-border: var(--color-zinc-200);
22
+ --sidebar-accent: var(--color-zinc-100);
23
+ --sidebar-accent-foreground: var(--color-zinc-700);
24
+
25
+ /* Animation */
26
+ --sidebar-duration: 220ms;
27
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
28
+
29
+ /* Z-index stack */
30
+ --z-topbar: 30;
31
+ --z-sidebar: 40;
32
+ }
33
+
34
+ /* Dark mode sidebar token overrides */
35
+ .dark {
36
+ --sidebar-background: var(--color-zinc-950);
37
+ --sidebar-foreground: var(--color-zinc-100);
38
+ --sidebar-border: var(--color-zinc-800);
39
+ --sidebar-accent: var(--color-zinc-800);
40
+ --sidebar-accent-foreground: var(--color-zinc-300);
41
+ }
42
+
43
+ /* ── Sidebar state variants ──────────────────────────────────────────────────
44
+ Custom Tailwind variants for showing/hiding content based on sidebar state.
45
+ Works on any descendant of dashboard_group. Compose with breakpoints:
46
+ kui-sidebar-open:lg:hidden — hide on desktop when sidebar expanded
47
+ kui-sidebar-closed:lg:hidden — hide on desktop when sidebar collapsed */
48
+
49
+ @custom-variant kui-sidebar-open {
50
+ [data-sidebar-open="true"] & {
51
+ @slot;
52
+ }
53
+ }
54
+
55
+ @custom-variant kui-sidebar-closed {
56
+ [data-sidebar-open="false"] & {
57
+ @slot;
58
+ }
59
+ }
60
+
61
+ /* ── Layout mechanics ─────────────────────────────────────────────────────── */
62
+
63
+ @layer components {
64
+ /* Drive --sidebar-current-width from the data attribute set by sidebar controller */
65
+ [data-sidebar-open="true"] { --sidebar-current-width: var(--sidebar-width); }
66
+ [data-sidebar-open="false"] { --sidebar-current-width: 0rem; }
67
+
68
+ /* Hide sidebar border when collapsed to prevent ghost border at 0 width */
69
+ [data-sidebar-open="false"] [data-slot="dashboard-sidebar"] {
70
+ border-right-width: 0;
71
+ }
72
+
73
+ /* Flat 2×2 grid: topbar row + sidebar/panel row */
74
+ [data-slot="dashboard-group"] {
75
+ grid-template-rows: var(--topbar-height) 1fr;
76
+ grid-template-columns: var(--sidebar-current-width) 1fr;
77
+ transition: grid-template-columns var(--sidebar-duration) var(--ease-out-expo);
78
+ }
79
+
80
+ /* Topbar spans panel column only (sidebar owns full height) */
81
+ [data-slot="dashboard-navbar"] {
82
+ grid-column: 2;
83
+ }
84
+
85
+ /*
86
+ Sidebar: spans full height (both grid rows). background-color and
87
+ border-right-color are set here (not via Tailwind arbitrary classes)
88
+ because bg-[--css-var] classes don't generate reliably.
89
+ */
90
+ [data-slot="dashboard-sidebar"] {
91
+ grid-row: 1 / -1;
92
+ background-color: var(--sidebar-background);
93
+ border-right: 1px solid var(--sidebar-border);
94
+ }
95
+
96
+ /* Sidebar header: matches topbar height, bottom border */
97
+ [data-slot="dashboard-sidebar-header"] {
98
+ height: var(--topbar-height);
99
+ border-bottom: 1px solid var(--sidebar-border);
100
+ }
101
+
102
+ /* Sidebar footer: top border */
103
+ [data-slot="dashboard-sidebar-footer"] {
104
+ border-top: 1px solid var(--sidebar-border);
105
+ }
106
+
107
+ /* Inner nav: always sidebar-width, flex column for header/nav/footer layout */
108
+ [data-slot="dashboard-sidebar-inner"] {
109
+ width: var(--sidebar-width);
110
+ height: 100%;
111
+ overflow: hidden;
112
+ display: flex;
113
+ flex-direction: column;
114
+ color: var(--sidebar-foreground);
115
+ }
116
+
117
+ /* Panel: row 2, col 2 */
118
+ [data-slot="dashboard-panel"] {
119
+ grid-column: 2;
120
+ grid-row: 2;
121
+ }
122
+
123
+ /* ── Navbar layout variant: navbar spans full width, sidebar below ────── */
124
+ [data-layout="navbar"] [data-slot="dashboard-navbar"] {
125
+ grid-column: 1 / -1;
126
+ }
127
+ [data-layout="navbar"] [data-slot="dashboard-sidebar"] {
128
+ grid-row: auto;
129
+ }
130
+
131
+ /* Scrim: hidden on desktop */
132
+ [data-slot="dashboard-scrim"] { display: none; }
133
+
134
+ /* ── Collapse button icon switching ────────────────────────────────────── */
135
+ [data-sidebar-open="true"] [data-slot="collapse-icon-open"] { display: inline; }
136
+ [data-sidebar-open="true"] [data-slot="collapse-icon-closed"] { display: none; }
137
+ [data-sidebar-open="false"] [data-slot="collapse-icon-open"] { display: none; }
138
+ [data-sidebar-open="false"] [data-slot="collapse-icon-closed"] { display: inline; }
139
+
140
+ /* ── Nav section (details/summary) ───────────────────────────────────── */
141
+ [data-slot="nav-section-chevron"] {
142
+ transition: transform 200ms ease;
143
+ }
144
+ [data-slot="nav-section"][open] [data-slot="nav-section-chevron"] {
145
+ transform: rotate(180deg);
146
+ }
147
+
148
+ /* ── Nav item sidebar context ────────────────────────────────────────── */
149
+ [data-slot="dashboard-sidebar-inner"] [data-slot="nav-item"]:hover {
150
+ background-color: var(--sidebar-accent);
151
+ color: var(--sidebar-accent-foreground);
152
+ }
153
+ [data-slot="dashboard-sidebar-inner"] [data-slot="nav-item"][data-active="true"] {
154
+ background-color: var(--sidebar-accent);
155
+ color: var(--sidebar-accent-foreground);
156
+ }
157
+
158
+ /* ── Mobile: sidebar becomes a fixed full-width overlay ───────────────── */
159
+ @media (max-width: 767px) {
160
+ /* Collapse sidebar column to 0 on mobile */
161
+ [data-slot="dashboard-group"] {
162
+ grid-template-columns: 0 1fr;
163
+ }
164
+
165
+ /* Sidebar switches from grid column to fixed overlay */
166
+ [data-slot="dashboard-sidebar"] {
167
+ position: fixed;
168
+ inset-block: 0;
169
+ inset-inline-start: 0;
170
+ z-index: var(--z-sidebar);
171
+ width: 100dvw;
172
+ transform: translateX(-100dvw);
173
+ transition: transform var(--sidebar-duration) var(--ease-out-expo);
174
+ }
175
+
176
+ [data-sidebar-open="true"] [data-slot="dashboard-sidebar"] {
177
+ transform: translateX(0);
178
+ }
179
+
180
+ /* Inner nav fills the full-width sidebar on mobile */
181
+ [data-slot="dashboard-sidebar-inner"] {
182
+ width: 100dvw;
183
+ }
184
+
185
+ /* Scrim: appears behind the sidebar overlay on mobile */
186
+ [data-sidebar-open="true"] [data-slot="dashboard-scrim"] {
187
+ display: block;
188
+ position: fixed;
189
+ inset: 0;
190
+ z-index: calc(var(--z-sidebar) - 1);
191
+ background: oklch(0% 0 0 / 40%);
192
+ }
193
+ }
194
+ }
@@ -3,6 +3,19 @@
3
3
  that are awkward to express in ERB. Most styling lives in Ruby theme modules
4
4
  (lib/kiso/themes/) as computed Tailwind classes. */
5
5
 
6
+ @import "./checkbox.css";
7
+ @import "./radio-group.css";
8
+ @import "./color-mode.css";
9
+ @import "./dashboard.css";
10
+ @import "./input-otp.css";
11
+
12
+ /* Scan Kiso's own files so host apps don't need to know the gem's internals.
13
+ Paths are relative to THIS file (app/assets/tailwind/kiso/engine.css).
14
+ Count carefully — wrong paths fail SILENTLY (classes just won't generate). */
15
+ @source "../../../views"; /* → app/views */
16
+ @source "../../../helpers"; /* → app/helpers */
17
+ @source "../../../../lib/kiso/themes"; /* → lib/kiso/themes */
18
+
6
19
  /* === Geist Font (by Vercel) ===
7
20
  Self-hosted variable fonts — no CDN dependency.
8
21
  Licensed under the SIL Open Font License (see OFL.txt).
@@ -30,3 +43,107 @@
30
43
  --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
31
44
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
32
45
  }
46
+
47
+ /* === Semantic Color Tokens ===
48
+ Purpose-named tokens that flip in dark mode. Components use bg-primary,
49
+ text-foreground, etc. — never raw palette shades or dark: prefixes.
50
+
51
+ Override any token in your app's CSS to retheme:
52
+ @theme { --color-primary: var(--color-violet-600); }
53
+ */
54
+
55
+ @theme {
56
+ /* Brand / action colors */
57
+ --color-primary: var(--color-sky-600);
58
+ --color-primary-foreground: white;
59
+
60
+ --color-secondary: var(--color-teal-600);
61
+ --color-secondary-foreground: white;
62
+
63
+ --color-success: var(--color-green-600);
64
+ --color-success-foreground: white;
65
+
66
+ --color-info: var(--color-sky-600);
67
+ --color-info-foreground: white;
68
+
69
+ --color-warning: var(--color-amber-500);
70
+ --color-warning-foreground: var(--color-amber-950);
71
+
72
+ --color-error: var(--color-red-600);
73
+ --color-error-foreground: white;
74
+
75
+ /* Surface tokens */
76
+ --color-background: white;
77
+ --color-foreground: var(--color-zinc-950);
78
+
79
+ --color-muted: var(--color-zinc-100);
80
+ --color-muted-foreground: var(--color-zinc-500);
81
+
82
+ --color-accent: var(--color-zinc-100);
83
+ --color-accent-foreground: var(--color-zinc-900);
84
+
85
+ --color-inverted: var(--color-zinc-900);
86
+ --color-inverted-foreground: white;
87
+ --color-elevated: var(--color-zinc-100);
88
+ --color-accented: var(--color-zinc-300);
89
+
90
+ --color-border: var(--color-zinc-200);
91
+ --color-border-accented: var(--color-zinc-300);
92
+
93
+ --color-ring: var(--color-zinc-400);
94
+ }
95
+
96
+ /* === Dark Mode ===
97
+ Redefine tokens under .dark — components never use dark: prefixes. */
98
+
99
+ .dark {
100
+ --color-primary: var(--color-blue-400);
101
+ --color-primary-foreground: var(--color-zinc-950);
102
+
103
+ --color-secondary: var(--color-teal-400);
104
+ --color-secondary-foreground: var(--color-zinc-950);
105
+
106
+ --color-success: var(--color-green-400);
107
+ --color-success-foreground: var(--color-zinc-950);
108
+
109
+ --color-info: var(--color-sky-400);
110
+ --color-info-foreground: var(--color-zinc-950);
111
+
112
+ --color-warning: var(--color-amber-400);
113
+ --color-warning-foreground: var(--color-zinc-950);
114
+
115
+ --color-error: var(--color-red-400);
116
+ --color-error-foreground: var(--color-zinc-950);
117
+
118
+ --color-background: var(--color-zinc-950);
119
+ --color-foreground: var(--color-zinc-50);
120
+
121
+ --color-muted: var(--color-zinc-800);
122
+ --color-muted-foreground: var(--color-zinc-400);
123
+
124
+ --color-accent: var(--color-zinc-800);
125
+ --color-accent-foreground: var(--color-zinc-50);
126
+
127
+ --color-inverted: white;
128
+ --color-inverted-foreground: var(--color-zinc-950);
129
+ --color-elevated: var(--color-zinc-800);
130
+ --color-accented: var(--color-zinc-700);
131
+
132
+ --color-border: var(--color-zinc-800);
133
+ --color-border-accented: var(--color-zinc-700);
134
+
135
+ --color-ring: var(--color-zinc-600);
136
+ }
137
+
138
+ /* === Page Defaults ===
139
+ Kiso automatically applies sensible base styles to <body> so host apps
140
+ start with the correct colors and rendering. Uses @layer base (lowest
141
+ Tailwind priority) — utility classes on <body> override automatically. */
142
+
143
+ @layer base {
144
+ body {
145
+ @apply bg-background text-foreground antialiased;
146
+ font-synthesis-weight: none;
147
+ text-rendering: optimizeLegibility;
148
+ }
149
+ }
@@ -0,0 +1,10 @@
1
+ /* InputOTP caret blink animation — the blinking cursor in the active empty slot. */
2
+
3
+ @theme inline {
4
+ --animate-caret-blink: caret-blink 1s ease-out infinite;
5
+ }
6
+
7
+ @keyframes caret-blink {
8
+ 0%, 70%, 100% { opacity: 1; }
9
+ 20%, 50% { opacity: 0; }
10
+ }
@@ -0,0 +1,17 @@
1
+ /* RadioGroupItem indicator — CSS ::after for the filled circle dot since
2
+ native <input type="radio"> can't contain child elements. Uses currentColor
3
+ which inherits from checked:text-{color}-foreground set by compound variants.
4
+ Follows the same pattern as Checkbox (mask-image with currentColor). */
5
+
6
+ [data-radio-group-part="item"] {
7
+ display: grid;
8
+ place-content: center;
9
+ }
10
+
11
+ [data-radio-group-part="item"]:checked::after {
12
+ content: '';
13
+ width: 0.5em;
14
+ height: 0.5em;
15
+ border-radius: 9999px;
16
+ background-color: currentColor;
17
+ }