kiso 0.5.2.pre → 0.6.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/tailwind/kiso/button.css +12 -3
  3. data/app/assets/tailwind/kiso/checkbox.css +13 -2
  4. data/app/assets/tailwind/kiso/color-mode.css +15 -3
  5. data/app/assets/tailwind/kiso/dashboard.css +97 -44
  6. data/app/assets/tailwind/kiso/dialog.css +39 -5
  7. data/app/assets/tailwind/kiso/engine.css +117 -34
  8. data/app/assets/tailwind/kiso/input-otp.css +24 -4
  9. data/app/assets/tailwind/kiso/palettes/blue.css +14 -5
  10. data/app/assets/tailwind/kiso/palettes/green.css +9 -5
  11. data/app/assets/tailwind/kiso/palettes/orange.css +9 -5
  12. data/app/assets/tailwind/kiso/palettes/violet.css +9 -5
  13. data/app/assets/tailwind/kiso/palettes/zinc.css +11 -7
  14. data/app/assets/tailwind/kiso/radio-group.css +11 -4
  15. data/app/assets/tailwind/kiso/slider.css +25 -6
  16. data/app/assets/tailwind/kiso/tooltip.css +37 -11
  17. data/app/helpers/kiso/app_component_helper.rb +83 -34
  18. data/app/helpers/kiso/component_helper.rb +227 -70
  19. data/app/helpers/kiso/icon_helper.rb +101 -39
  20. data/app/helpers/kiso/theme_helper.rb +50 -9
  21. data/app/helpers/kiso/ui_context_helper.rb +87 -35
  22. data/app/javascript/controllers/kiso/combobox_controller.js +10 -2
  23. data/app/javascript/controllers/kiso/command_controller.js +2 -0
  24. data/app/javascript/controllers/kiso/command_dialog_controller.js +4 -0
  25. data/app/javascript/controllers/kiso/dialog_controller.js +6 -1
  26. data/app/javascript/controllers/kiso/dialog_trigger_controller.js +1 -1
  27. data/app/javascript/controllers/kiso/dropdown_menu_controller.js +23 -5
  28. data/app/javascript/controllers/kiso/index.js +25 -0
  29. data/app/javascript/controllers/kiso/input_otp_controller.js +5 -3
  30. data/app/javascript/controllers/kiso/popover_controller.js +18 -4
  31. data/app/javascript/controllers/kiso/select_controller.js +10 -2
  32. data/app/javascript/controllers/kiso/sidebar_controller.js +26 -4
  33. data/app/javascript/controllers/kiso/slider_controller.js +3 -3
  34. data/app/javascript/controllers/kiso/theme_controller.js +2 -1
  35. data/app/javascript/controllers/kiso/toggle_controller.js +2 -0
  36. data/app/javascript/controllers/kiso/toggle_group_controller.js +3 -0
  37. data/app/javascript/controllers/kiso/tooltip_controller.js +3 -0
  38. data/app/javascript/kiso/utils/focusable.js +14 -0
  39. data/app/javascript/kiso/utils/highlight.js +15 -1
  40. data/app/views/kiso/components/_alert.html.erb +2 -0
  41. data/app/views/kiso/components/_alert_dialog.html.erb +5 -2
  42. data/app/views/kiso/components/_app.html.erb +2 -0
  43. data/app/views/kiso/components/_aspect_ratio.html.erb +1 -0
  44. data/app/views/kiso/components/_avatar.html.erb +6 -2
  45. data/app/views/kiso/components/_button.html.erb +3 -0
  46. data/app/views/kiso/components/_checkbox.html.erb +1 -0
  47. data/app/views/kiso/components/_color_mode_button.html.erb +2 -0
  48. data/app/views/kiso/components/_color_mode_select.html.erb +2 -0
  49. data/app/views/kiso/components/_combobox.html.erb +3 -0
  50. data/app/views/kiso/components/_command.html.erb +2 -0
  51. data/app/views/kiso/components/_dashboard_group.html.erb +4 -0
  52. data/app/views/kiso/components/_dashboard_navbar.html.erb +2 -0
  53. data/app/views/kiso/components/_dashboard_panel.html.erb +1 -0
  54. data/app/views/kiso/components/_dashboard_sidebar.html.erb +2 -0
  55. data/app/views/kiso/components/_dashboard_toolbar.html.erb +2 -0
  56. data/app/views/kiso/components/_dialog.html.erb +3 -0
  57. data/app/views/kiso/components/_dropdown_menu.html.erb +2 -0
  58. data/app/views/kiso/components/_empty.html.erb +2 -0
  59. data/app/views/kiso/components/_field.html.erb +2 -0
  60. data/app/views/kiso/components/_field_group.html.erb +1 -0
  61. data/app/views/kiso/components/_field_set.html.erb +1 -0
  62. data/app/views/kiso/components/_input_group.html.erb +1 -0
  63. data/app/views/kiso/components/_input_otp.html.erb +3 -0
  64. data/app/views/kiso/components/_nav.html.erb +2 -0
  65. data/app/views/kiso/components/_page_card.html.erb +3 -0
  66. data/app/views/kiso/components/_page_header.html.erb +3 -0
  67. data/app/views/kiso/components/_page_section.html.erb +2 -0
  68. data/app/views/kiso/components/_pagination.html.erb +2 -0
  69. data/app/views/kiso/components/_popover.html.erb +3 -0
  70. data/app/views/kiso/components/_select.html.erb +3 -0
  71. data/app/views/kiso/components/_select_native.html.erb +2 -0
  72. data/app/views/kiso/components/_separator.html.erb +2 -0
  73. data/app/views/kiso/components/_skeleton.html.erb +1 -0
  74. data/app/views/kiso/components/_slider.html.erb +4 -0
  75. data/app/views/kiso/components/_spinner.html.erb +2 -0
  76. data/app/views/kiso/components/_stats_card.html.erb +2 -0
  77. data/app/views/kiso/components/_stats_grid.html.erb +1 -0
  78. data/app/views/kiso/components/_switch.html.erb +2 -0
  79. data/app/views/kiso/components/_table.html.erb +2 -0
  80. data/app/views/kiso/components/_textarea.html.erb +3 -0
  81. data/app/views/kiso/components/_toggle.html.erb +2 -0
  82. data/app/views/kiso/components/_toggle_group.html.erb +2 -0
  83. data/app/views/kiso/components/_tooltip.html.erb +3 -0
  84. data/app/views/kiso/components/alert_dialog/_action.html.erb +1 -0
  85. data/app/views/kiso/components/alert_dialog/_cancel.html.erb +1 -0
  86. data/app/views/kiso/components/alert_dialog/_description.html.erb +1 -0
  87. data/app/views/kiso/components/alert_dialog/_title.html.erb +1 -0
  88. data/app/views/kiso/components/avatar/_image.html.erb +1 -0
  89. data/app/views/kiso/components/breadcrumb/_separator.html.erb +3 -0
  90. data/app/views/kiso/components/combobox/_chips.html.erb +3 -0
  91. data/app/views/kiso/components/command/_dialog.html.erb +2 -0
  92. data/app/views/kiso/components/dashboard_sidebar/_collapse.html.erb +2 -0
  93. data/app/views/kiso/components/dialog/_close.html.erb +1 -0
  94. data/app/views/kiso/components/field/_error.html.erb +4 -0
  95. data/app/views/kiso/components/field/_label.html.erb +2 -0
  96. data/app/views/kiso/components/field/_separator.html.erb +3 -0
  97. data/app/views/kiso/components/input_otp/_separator.html.erb +2 -0
  98. data/app/views/kiso/components/input_otp/_slot.html.erb +2 -0
  99. data/app/views/kiso/components/nav/_section.html.erb +4 -0
  100. data/app/views/kiso/components/tooltip/_content.html.erb +2 -0
  101. data/lib/generators/kiso/install/USAGE +23 -0
  102. data/lib/generators/kiso/install/install_generator.rb +91 -0
  103. data/lib/generators/kiso/install/templates/design_system.md.tt +190 -0
  104. data/lib/generators/kiso/install/templates/initializer.rb.tt +40 -0
  105. data/lib/kiso/cli/make.rb +6 -3
  106. data/lib/kiso/cli.rb +10 -0
  107. data/lib/kiso/color_utils.rb +31 -8
  108. data/lib/kiso/configuration.rb +11 -0
  109. data/lib/kiso/engine.rb +9 -2
  110. data/lib/kiso/propshaft_tailwind_stub_filter.rb +9 -2
  111. data/lib/kiso/themes/avatar.rb +40 -6
  112. data/lib/kiso/themes/badge.rb +5 -1
  113. data/lib/kiso/themes/color_mode_button.rb +11 -0
  114. data/lib/kiso/themes/color_mode_select.rb +7 -0
  115. data/lib/kiso/themes/dashboard.rb +28 -0
  116. data/lib/kiso/themes/dropdown_menu.rb +2 -2
  117. data/lib/kiso/themes/input_otp.rb +6 -3
  118. data/lib/kiso/themes/nav.rb +17 -0
  119. data/lib/kiso/themes/pagination.rb +9 -4
  120. data/lib/kiso/themes/shared.rb +27 -7
  121. data/lib/kiso/version.rb +5 -2
  122. metadata +5 -1
@@ -1,9 +1,12 @@
1
1
  <%# locals: (variant: :outline, size: :md, disabled: false,
2
2
  css_classes: "", **component_options) %>
3
+ <%# Multi-line text input. Yield a block to set default content (initial value). %>
3
4
  <% component_options[:disabled] = true if disabled %>
4
5
  <%= content_tag :textarea,
5
6
  class: Kiso::Themes::Textarea.render(variant: variant, size: size, class: css_classes),
6
7
  data: kiso_prepare_options(component_options, slot: "textarea"),
7
8
  **component_options do %>
9
+ <%# capture { yield }.presence renders block content as the textarea's initial value,
10
+ or nil if no block was provided (avoids rendering empty whitespace). %>
8
11
  <%= capture { yield }.presence %>
9
12
  <% end %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (variant: :default, size: :default, pressed: false, css_classes: "", **component_options) %>
2
+ <%# Two-state toggle button with aria-pressed. The Stimulus controller toggles
3
+ the data-state attribute between "on" and "off" on click. %>
2
4
  <% component_options[:type] ||= :button
3
5
  component_options[:"aria-pressed"] = pressed %>
4
6
  <%= content_tag :button,
@@ -1,4 +1,6 @@
1
1
  <%# locals: (type: :single, variant: :default, size: :default, css_classes: "", **component_options) %>
2
+ <%# Group of toggle buttons with single or multiple selection. The Stimulus controller
3
+ manages selection state and passes variant/size down to child toggle_group/items. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::ToggleGroup.render(class: css_classes),
4
6
  role: "group",
@@ -1,4 +1,7 @@
1
1
  <%# locals: (text: nil, kbds: nil, side: :top, align: :center, delay: 0, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Tooltip positioned via Floating UI using the popover API. Pass text: for a simple
3
+ tooltip with optional keyboard shortcut hints (kbds:). Without text:, compose
4
+ manually with tooltip/trigger and tooltip/content sub-parts. %>
2
5
  <%= tag.div(
3
6
  class: css_classes.presence,
4
7
  data: kiso_prepare_options(component_options, slot: "tooltip",
@@ -1,4 +1,5 @@
1
1
  <%# locals: (color: :primary, variant: :solid, size: :md, css_classes: "", **component_options) %>
2
+ <%# Confirm action button that closes the dialog. Uses Button theme for styling. %>
2
3
  <%= tag.button type: "button",
3
4
  class: Kiso::Themes::Button.render(color: color, variant: variant, size: size, class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "alert-dialog-action",
@@ -1,4 +1,5 @@
1
1
  <%# locals: (color: :primary, variant: :outline, size: :md, css_classes: "", **component_options) %>
2
+ <%# Cancel button that closes the dialog without confirming. Defaults to outline variant. %>
2
3
  <%= tag.button type: "button",
3
4
  class: Kiso::Themes::Button.render(color: color, variant: variant, size: size, class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "alert-dialog-cancel",
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Auto-generates id from the parent dialog's @_kiso_alert_dialog_id for aria-describedby linking. %>
2
3
  <% component_options[:id] = "#{@_kiso_alert_dialog_id}-description" if @_kiso_alert_dialog_id && !component_options.key?(:id) %>
3
4
  <%= content_tag :p,
4
5
  class: Kiso::Themes::AlertDialogDescription.render(class: css_classes),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Auto-generates id from the parent dialog's @_kiso_alert_dialog_id for aria-labelledby linking. %>
2
3
  <% component_options[:id] = "#{@_kiso_alert_dialog_id}-title" if @_kiso_alert_dialog_id && !component_options.key?(:id) %>
3
4
  <%= content_tag :h2,
4
5
  class: Kiso::Themes::AlertDialogTitle.render(class: css_classes),
@@ -1,4 +1,5 @@
1
1
  <%# locals: (src:, alt: "", css_classes: "", **component_options) %>
2
+ <%# Avatar image that hides itself on load error, revealing the fallback text beneath. %>
2
3
  <%= tag.img src: src, alt: alt,
3
4
  class: Kiso::Themes::AvatarImage.render(class: css_classes),
4
5
  data: kiso_prepare_options(component_options, slot: "avatar-image"),
@@ -1,9 +1,12 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Breadcrumb separator between items. Defaults to a chevron-right icon;
3
+ yield a block to replace with a custom separator. %>
2
4
  <%= content_tag :li,
3
5
  class: Kiso::Themes::BreadcrumbSeparator.render(class: css_classes),
4
6
  role: "presentation",
5
7
  aria: { hidden: "true" },
6
8
  data: kiso_prepare_options(component_options, slot: "breadcrumb-separator"),
7
9
  **component_options do %>
10
+ <%# capture { yield }.presence returns nil when no block content, falling back to default icon. %>
8
11
  <%= capture { yield }.presence || kiso_component_icon(:chevron_right, class: "size-3.5") %>
9
12
  <% end %>
@@ -1,10 +1,13 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Container for multi-select chips. Includes a <template> element that the
3
+ Stimulus controller clones to create new chips dynamically when items are selected. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::ComboboxChips.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "combobox-chips",
5
7
  kiso__combobox_target: "chips"),
6
8
  **component_options do %>
7
9
  <%= yield %>
10
+ <%# Chip template cloned by Stimulus for dynamically-added selections. %>
8
11
  <%= tag.template data: { kiso__combobox_target: "chipTemplate" } do %>
9
12
  <%= tag.span class: Kiso::Themes::ComboboxChip.render,
10
13
  data: { slot: "combobox-chip", kiso__combobox_target: "chip" } do %>
@@ -1,4 +1,6 @@
1
1
  <%# locals: (shortcut: "k", css_classes: "", **component_options) %>
2
+ <%# Command palette wrapped in a native <dialog>. Opens via Cmd/Ctrl+K (configurable
3
+ via shortcut: prop). Typically contains a command/input and command/list. %>
2
4
  <%= content_tag :dialog,
3
5
  class: Kiso::Themes::CommandDialog.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "command-dialog",
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Sidebar collapse toggle for desktop. Shows open/closed panel icons that swap
3
+ visibility based on sidebar state via CSS. Placed inside the sidebar itself. %>
2
4
  <%= content_tag :button,
3
5
  class: Kiso::Themes::DashboardSidebarCollapse.render(class: css_classes),
4
6
  data: kiso_prepare_options(component_options, slot: "dashboard-sidebar-collapse",
@@ -1,4 +1,5 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Dialog close button. Yields a block to override the default X icon. %>
2
3
  <% content = capture { yield }.presence %>
3
4
  <%= tag.button type: "button",
4
5
  class: Kiso::Themes::DialogClose.render(class: css_classes),
@@ -1,5 +1,9 @@
1
1
  <%# locals: (errors: [], css_classes: "", **component_options) %>
2
+ <%# Form field error display. Pass errors: as an array of strings (e.g., from
3
+ ActiveModel) or yield a block for fully custom error markup. Renders nothing
4
+ when both errors and block are empty. Multiple errors render as a bulleted list. %>
2
5
  <% messages = Array(errors).compact.map(&:to_s).reject(&:blank?).uniq %>
6
+ <%# Block override: custom error content replaces the default message rendering. %>
3
7
  <% content = capture { yield }.presence %>
4
8
  <% if content || messages.any? %>
5
9
  <%= content_tag :div,
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Wraps the base Label component with field-specific styling and a data-slot override.
3
+ Manually merges the data-slot because this delegates to kui(:label) which sets its own slot. %>
2
4
  <% component_options[:data] = (component_options[:data] || {}).merge(slot: "field-label") %>
3
5
  <%= kui(:label,
4
6
  css_classes: Kiso::Themes::FieldLabel.render(class: css_classes),
@@ -1,4 +1,7 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Horizontal divider between form fields. Optionally yield text (e.g., "or") to
3
+ display centered over the separator line. Sets data-content when text is present
4
+ so CSS can adjust spacing. %>
2
5
  <% captured = capture { yield }.presence %>
3
6
  <%= content_tag :div,
4
7
  class: Kiso::Themes::FieldSeparator.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", **component_options) %>
2
+ <%# Visual separator between OTP slot groups. Defaults to a minus icon;
3
+ yield a block to replace with a custom separator. %>
2
4
  <%= content_tag :div,
3
5
  role: "separator",
4
6
  class: Kiso::Themes::InputOtpSeparator.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (size: :md, css_classes: "", **component_options) %>
2
+ <%# Individual character display slot for the OTP input. The Stimulus controller
3
+ populates the slot-char span and shows/hides the blinking caret. %>
2
4
  <%= content_tag :div,
3
5
  class: Kiso::Themes::InputOtpSlot.render(size: size, class: css_classes),
4
6
  aria: { hidden: "true" },
@@ -1,4 +1,8 @@
1
1
  <%# locals: (title: nil, open: true, collapsible: true, ui: {}, css_classes: "", **component_options) %>
2
+ <%# Navigation section with three rendering modes:
3
+ 1. title + collapsible (default): native <details> element with toggle
4
+ 2. title + collapsible: false: static section with non-interactive heading
5
+ 3. No title: plain wrapper for ungrouped nav items %>
2
6
  <% if title && collapsible %>
3
7
  <%= content_tag :details,
4
8
  class: Kiso::Themes::NavSection.render(class: css_classes),
@@ -1,4 +1,6 @@
1
1
  <%# locals: (css_classes: "", ui: {}, **component_options) %>
2
+ <%# Tooltip content panel using popover="manual" for visibility control.
3
+ Positioned by Floating UI via the parent tooltip's Stimulus controller. %>
2
4
  <%= tag.div(
3
5
  class: Kiso::Themes::TooltipContent.render(class: css_classes),
4
6
  role: "tooltip",
@@ -0,0 +1,23 @@
1
+ Description:
2
+ Sets up Kiso in your application. Creates a well-commented initializer
3
+ with preset, theme, and icon configuration examples.
4
+
5
+ Optionally generates a DESIGN_SYSTEM.md with your app's spacing,
6
+ typography, color, and component conventions.
7
+
8
+ Examples:
9
+ bin/rails generate kiso:install
10
+
11
+ Creates:
12
+ config/initializers/kiso.rb
13
+
14
+ Optionally creates (interactive prompt):
15
+ DESIGN_SYSTEM.md
16
+
17
+ bin/rails generate kiso:install --no-skip-design-system --app-name="My App"
18
+
19
+ Creates both files without interactive prompts.
20
+
21
+ bin/rails generate kiso:install --skip-design-system
22
+
23
+ Creates only the initializer, skips the design system doc.
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kiso
4
+ module Generators
5
+ # Sets up Kiso in a host application.
6
+ #
7
+ # Creates a well-commented initializer and optionally generates a
8
+ # design system document. No required arguments — interactive prompts
9
+ # handle optional inputs.
10
+ #
11
+ # @example Interactive (default)
12
+ # bin/rails generate kiso:install
13
+ #
14
+ # @example Non-interactive
15
+ # bin/rails generate kiso:install --no-skip-design-system --app-name="Outport"
16
+ class InstallGenerator < Rails::Generators::Base
17
+ source_root File.expand_path("templates", __dir__)
18
+
19
+ class_option :skip_design_system,
20
+ type: :boolean,
21
+ default: nil,
22
+ desc: "Skip generating DESIGN_SYSTEM.md (default: ask interactively)"
23
+
24
+ class_option :app_name,
25
+ type: :string,
26
+ default: nil,
27
+ desc: "App name for the design system doc header (default: ask interactively)"
28
+
29
+ def create_initializer
30
+ initializer_path = "config/initializers/kiso.rb"
31
+ if File.exist?(File.join(destination_root, initializer_path))
32
+ say_status :skip, initializer_path, :yellow
33
+ else
34
+ template "initializer.rb.tt", initializer_path
35
+ end
36
+ end
37
+
38
+ def create_design_system
39
+ should_generate = case options[:skip_design_system]
40
+ when true then false
41
+ when false then true
42
+ else
43
+ yes?(<<~PROMPT)
44
+
45
+ Would you like to generate a Design System document?
46
+ This creates DESIGN_SYSTEM.md with your app's spacing, typography, color,
47
+ and component conventions — useful for team alignment and AI coding agents.
48
+
49
+ Generate DESIGN_SYSTEM.md? (y/n)
50
+ PROMPT
51
+ end
52
+
53
+ return unless should_generate
54
+
55
+ @app_name = resolve_app_name
56
+ template "design_system.md.tt", "DESIGN_SYSTEM.md"
57
+ @design_system_created = true
58
+ end
59
+
60
+ def print_next_steps
61
+ say ""
62
+ say "Kiso installed!", :green
63
+ say ""
64
+ say " Initializer: config/initializers/kiso.rb"
65
+ say " Design System: DESIGN_SYSTEM.md" if @design_system_created
66
+ say ""
67
+ say "Next steps:"
68
+ say " 1. Add Kiso's CSS to your Tailwind stylesheet:"
69
+ say ' @import "../builds/tailwind/kiso";'
70
+ say " 2. Add the theme script to your layout <head>:"
71
+ say " <%%= kiso_theme_script %>"
72
+ say " 3. Customize your brand colors in your Tailwind @theme block."
73
+ say " See: https://kisoui.com/guide/css-variables"
74
+ say ""
75
+ end
76
+
77
+ private
78
+
79
+ def resolve_app_name
80
+ return options[:app_name] if options[:app_name].present?
81
+ return "My App" if options[:skip_design_system] == false
82
+
83
+ response = ask(<<~PROMPT)
84
+ What's your app called? This is just a friendly name for the document
85
+ header (e.g. "Outport", "My App"). [default: My App]
86
+ PROMPT
87
+ response.presence || "My App"
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,190 @@
1
+ # <%= @app_name %> Design System
2
+
3
+ Built on [Kiso UI](https://kisoui.com). Reference this document before
4
+ building any UI — it defines the visual conventions that keep
5
+ <%= @app_name %> consistent.
6
+
7
+ ---
8
+
9
+ ## Color Palette
10
+
11
+ <%= @app_name %> uses Kiso's semantic color tokens. Components reference
12
+ tokens like `bg-primary` and `text-foreground` — never raw palette shades.
13
+
14
+ | Token | Purpose |
15
+ |-------|---------|
16
+ | `primary` | Brand color, primary actions |
17
+ | `secondary` | Secondary actions |
18
+ | `success` | Positive feedback, confirmations |
19
+ | `info` | Informational messages |
20
+ | `warning` | Caution, non-destructive alerts |
21
+ | `error` | Errors, destructive actions |
22
+ | `neutral` | Default, non-colored UI |
23
+
24
+ Every color has a `-foreground` companion for accessible text:
25
+ `bg-primary text-primary-foreground`.
26
+
27
+ **Customize your brand colors** in your Tailwind stylesheet:
28
+
29
+ ```css
30
+ @theme {
31
+ --color-primary: var(--color-violet-600);
32
+ --color-primary-foreground: var(--color-white);
33
+ }
34
+ ```
35
+
36
+ See the full list of overridable tokens:
37
+ [CSS Variables Reference](https://kisoui.com/guide/css-variables)
38
+
39
+ ---
40
+
41
+ ## Typography Hierarchy
42
+
43
+ | Role | Classes | When to use |
44
+ |------|---------|-------------|
45
+ | Page title | `text-lg font-semibold` | Top-level page headings |
46
+ | Section title | `text-base font-semibold` | Section headings within a page |
47
+ | Card title | `text-sm font-semibold` | Card and dialog titles |
48
+ | Body | `text-sm` | Body text, descriptions, menu items |
49
+ | Caption | `text-xs` | Timestamps, counts, helper text, badges |
50
+ | Label | `text-sm font-medium` | Form labels, button text |
51
+
52
+ **Rules:**
53
+ - Never go below `text-xs` (12px)
54
+ - `font-semibold` for primary headings, `font-medium` for interactive
55
+ elements, default weight for body text
56
+ - Inputs use `text-base md:text-sm` (larger on mobile for zoom prevention)
57
+
58
+ ---
59
+
60
+ ## Spacing Scale
61
+
62
+ ### Interactive Element Heights
63
+
64
+ | Size | Class | Examples |
65
+ |------|-------|----------|
66
+ | xs | `h-7` | Icon buttons, compact actions |
67
+ | sm | `h-8` | Small buttons, small inputs |
68
+ | md (default) | `h-9` | Buttons, inputs, selects |
69
+ | lg | `h-10` | Large buttons |
70
+ | xl | `h-11` | Extra-large buttons |
71
+
72
+ ### Padding
73
+
74
+ | Context | Classes | Examples |
75
+ |---------|---------|----------|
76
+ | Interactive (default) | `px-3 py-2` | Buttons, inputs |
77
+ | Interactive (small) | `px-3 py-1.5` | Small buttons |
78
+ | Interactive (compact) | `px-2 py-1` | Extra-small buttons |
79
+ | Menu items | `px-2 py-1.5` | Dropdown, select, command items |
80
+ | Large containers | `p-6` | Card, Dialog |
81
+ | Medium containers | `p-4` | Popover, Sheet header |
82
+ | Compact containers | `p-2` | Sidebar sections |
83
+
84
+ ### Gaps
85
+
86
+ | Class | When to use |
87
+ |-------|-------------|
88
+ | `gap-1` | Tight lists — sidebar menus, accordion items |
89
+ | `gap-1.5` | Between small elements — breadcrumb items |
90
+ | `gap-2` | **Default.** Icon + text, label + control, most siblings |
91
+ | `gap-3` | Radio/checkbox groups, form field spacing |
92
+ | `gap-4` | Between major sections inside a container |
93
+ | `gap-6` | Top-level container divisions (card header/content/footer) |
94
+
95
+ ### Border Radius
96
+
97
+ Controlled by the `--kiso-radius` CSS variable. Override it to shift the
98
+ entire scale:
99
+
100
+ ```css
101
+ @theme {
102
+ --kiso-radius: 0.375rem; /* rounder */
103
+ --kiso-radius: 0; /* sharp, no rounding */
104
+ }
105
+ ```
106
+
107
+ Or use a preset in `config/initializers/kiso.rb`:
108
+
109
+ ```ruby
110
+ config.apply_preset(:rounded) # buttons → rounded-full, cards → rounded-2xl
111
+ config.apply_preset(:sharp) # minimal rounding everywhere
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Component Conventions
117
+
118
+ Use `kui()` components instead of raw HTML. Components handle styling,
119
+ dark mode, accessibility, and consistent structure.
120
+
121
+ ### Page structure
122
+
123
+ ```erb
124
+ <%%= kui(:app) do %>
125
+ <%%= kui(:header) { "..." } %>
126
+ <%%= kui(:main) do %>
127
+ <%%= kui(:container) do %>
128
+ <%%= yield %>
129
+ <%% end %>
130
+ <%% end %>
131
+ <%%= kui(:footer) { "..." } %>
132
+ <%% end %>
133
+ ```
134
+
135
+ ### Content cards
136
+
137
+ ```erb
138
+ <%%= kui(:card) do %>
139
+ <%%= kui(:card, :header) do %>
140
+ <%%= kui(:card, :title) { "Title" } %>
141
+ <%%= kui(:card, :description) { "Description" } %>
142
+ <%% end %>
143
+ <%%= kui(:card, :content) { "..." } %>
144
+ <%%= kui(:card, :footer) { "..." } %>
145
+ <%% end %>
146
+ ```
147
+
148
+ ### Forms
149
+
150
+ ```erb
151
+ <%%= kui(:field_group) do %>
152
+ <%%= kui(:field) do %>
153
+ <%%= kui(:field, :label) { "Email" } %>
154
+ <%%= kui(:input, name: "email", type: "email") %>
155
+ <%%= kui(:field, :description) { "We'll never share your email." } %>
156
+ <%% end %>
157
+ <%%= kui(:field) do %>
158
+ <%%= kui(:field, :label) { "Password" } %>
159
+ <%%= kui(:input, name: "password", type: "password") %>
160
+ <%% end %>
161
+ <%% end %>
162
+ ```
163
+
164
+ ### Empty states
165
+
166
+ ```erb
167
+ <%%= kui(:empty, icon: "inbox", title: "No messages", description: "...") %>
168
+ ```
169
+
170
+ ### Feedback
171
+
172
+ ```erb
173
+ <%%= kui(:alert, color: :success) do %>
174
+ <%%= kui(:alert, :title) { "Saved" } %>
175
+ <%%= kui(:alert, :description) { "Your changes have been saved." } %>
176
+ <%% end %>
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Anti-patterns
182
+
183
+ | Don't | Do instead |
184
+ |-------|------------|
185
+ | Raw `<div>`, `<button>`, `<input>` HTML | Use `kui(:card)`, `kui(:button)`, `kui(:input)` |
186
+ | Arbitrary spacing (`p-[13px]`, `gap-5`) | Use the documented spacing scale |
187
+ | Raw color classes (`bg-blue-500`, `text-zinc-600`) | Use semantic tokens (`bg-primary`, `text-muted-foreground`) |
188
+ | Tailwind `dark:` prefixes | Kiso handles dark mode via CSS variable swapping |
189
+ | Inline styles for layout | Use Tailwind utilities from the spacing scale |
190
+ | Custom font sizes (`text-[11px]`) | Use `text-xs`, `text-sm`, `text-base`, `text-lg` |
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ Kiso.configure do |config|
4
+ # --- Style Preset ---
5
+ # Apply a pre-built style preset. Presets adjust border-radius, padding,
6
+ # and other visual properties across all components.
7
+ #
8
+ # Available presets:
9
+ # :rounded — softer corners (buttons → rounded-full, cards → rounded-2xl)
10
+ # :sharp — minimal rounding (buttons → rounded-sm, cards → rounded-lg)
11
+ #
12
+ # config.apply_preset(:rounded)
13
+
14
+ # --- Global Theme Overrides ---
15
+ # Override component styles globally. These apply to every instance of the
16
+ # component — use css_classes: on individual calls for one-off overrides.
17
+ #
18
+ # Accepts: base:, variants:, compound_variants:, defaults:, ui:
19
+ #
20
+ # config.theme[:button] = { base: "rounded-full" }
21
+ # config.theme[:card] = { base: "rounded-xl shadow-lg" }
22
+ # config.theme[:badge] = { defaults: { variant: :outline } }
23
+ #
24
+ # Override inner sub-part elements with ui:
25
+ # config.theme[:card] = { ui: { header: "p-8", footer: "px-8" } }
26
+
27
+ # --- Icon Customization ---
28
+ # Swap default component icons. Keys are semantic names, values are icon
29
+ # identifiers passed to kiso_icon (e.g. "heroicons:chevron-right").
30
+ #
31
+ # config.icons[:chevron_right] = "heroicons:chevron-right"
32
+ # config.icons[:x] = "heroicons:x-mark"
33
+ # config.icons[:search] = "heroicons:magnifying-glass"
34
+
35
+ # --- App Theme ---
36
+ # Theme directory for appui() components. Themes live in app/themes/<name>/.
37
+ # Default: :default (app/themes/default/)
38
+ #
39
+ # config.app_theme = :default
40
+ end
data/lib/kiso/cli/make.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Generates scaffolding for new Kiso components.
3
+ # Generates scaffolding for new Kiso engine components.
4
4
  #
5
- # Creates theme module, ERB partial, Lookbook preview, and updates
6
- # the require list and skill reference docs.
5
+ # Creates theme module, ERB partial, Lookbook preview templates, and
6
+ # updates +lib/kiso.rb+ requires and skill reference docs.
7
+ #
8
+ # This is the CLI equivalent of +kiso:framework_component+ Rails generator,
9
+ # but intended for quick use without loading the Rails environment.
7
10
  #
8
11
  # @example
9
12
  # $ bin/kiso make component alert
data/lib/kiso/cli.rb CHANGED
@@ -4,6 +4,16 @@ require "thor"
4
4
  require "active_support/core_ext/string/inflections"
5
5
 
6
6
  module Kiso
7
+ # Command-line interface for the Kiso gem, invoked via +bin/kiso+.
8
+ #
9
+ # Routes to subcommands:
10
+ # - +kiso icons+ -- icon set management (delegated to kiso-icons gem)
11
+ # - +kiso make+ -- component scaffolding
12
+ # - +kiso version+ -- print gem version
13
+ #
14
+ # @see Cli::Main the top-level entry point
15
+ # @see Cli::Make component generator subcommand
16
+ # @see Cli::Icons icon management subcommand
7
17
  module Cli
8
18
  end
9
19
  end
@@ -1,16 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kiso
4
+ # Utility methods for color calculations, used by the Avatar component
5
+ # for automatic contrast text color on arbitrary background colors.
6
+ #
7
+ # @see Configuration#contrast_threshold
4
8
  module ColorUtils
5
9
  module_function
6
10
 
7
- # Returns "white" or "black" based on WCAG relative luminance.
8
- # Uses a perceptual threshold of 0.36 rather than the mathematical
9
- # midpoint of 0.179, per Lea Verou's research on contrast color
10
- # generation. The higher threshold produces better results on
11
- # saturated chromatic colors (e.g. Tailwind 500-shade palette).
12
- # Accepts 3-digit (#abc) or 6-digit (#aabbcc) hex strings.
13
- def contrast_text_color(hex)
11
+ # Default luminance threshold for the contrast calculation.
12
+ # 0.42 is a perceptual midpoint per Lea Verou's research that produces
13
+ # better results on saturated chromatic colors than the WCAG mathematical
14
+ # midpoint of 0.179.
15
+ DEFAULT_CONTRAST_THRESHOLD = 0.42
16
+
17
+ # Returns the optimal text color ("white" or "black") for a given
18
+ # background color, based on WCAG relative luminance.
19
+ #
20
+ # Uses a perceptual threshold (default 0.42) rather than the
21
+ # mathematical midpoint of 0.179, per Lea Verou's research on
22
+ # contrast color generation. The higher threshold produces better
23
+ # results on saturated chromatic colors (e.g. Tailwind 500-shade
24
+ # palette).
25
+ #
26
+ # @param hex [String] a hex color string, 3-digit (#abc) or 6-digit (#aabbcc)
27
+ # @param threshold [Float, nil] luminance threshold override; defaults to
28
+ # {Configuration#contrast_threshold}
29
+ # @return [String] "white" or "black"
30
+ #
31
+ # @example
32
+ # ColorUtils.contrast_text_color("#3b82f6") # => "white"
33
+ # ColorUtils.contrast_text_color("#fbbf24") # => "black"
34
+ def contrast_text_color(hex, threshold: nil)
35
+ threshold ||= Kiso.config.contrast_threshold
36
+
14
37
  hex = hex.delete("#")
15
38
  hex = hex.chars.map { |c| c * 2 }.join if hex.length == 3
16
39
 
@@ -19,7 +42,7 @@ module Kiso
19
42
  (c <= 0.04045) ? c / 12.92 : ((c + 0.055) / 1.055)**2.4
20
43
  }.then { |lr, lg, lb| 0.2126 * lr + 0.7152 * lg + 0.0722 * lb }
21
44
 
22
- (luminance > 0.36) ? "black" : "white"
45
+ (luminance > threshold) ? "black" : "white"
23
46
  end
24
47
  end
25
48
  end
@@ -33,10 +33,21 @@ module Kiso
33
33
  # end
34
34
  attr_accessor :app_theme
35
35
 
36
+ # @return [Float] luminance threshold for contrast text color calculation.
37
+ # Colors with luminance above this value get black text, below get white.
38
+ # Default is 0.42 (perceptual midpoint per Lea Verou's research).
39
+ #
40
+ # @example Tune for a darker palette
41
+ # Kiso.configure do |config|
42
+ # config.contrast_threshold = 0.36
43
+ # end
44
+ attr_accessor :contrast_threshold
45
+
36
46
  def initialize
37
47
  @icons = default_icons
38
48
  @theme = {}
39
49
  @app_theme = :default
50
+ @contrast_threshold = Kiso::ColorUtils::DEFAULT_CONTRAST_THRESHOLD
40
51
  end
41
52
 
42
53
  # Resolves the active app theme directory path relative to the given root.