kozenet_ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +76 -0
  6. data/app/assets/images/kozenet_ui/icons/cart.svg +1 -0
  7. data/app/assets/images/kozenet_ui/icons/heart.svg +1 -0
  8. data/app/assets/javascripts/kozenet_ui/controllers/dropdown_controller.js +55 -0
  9. data/app/assets/javascripts/kozenet_ui/controllers/header_controller.js +32 -0
  10. data/app/assets/javascripts/kozenet_ui/controllers/mobile_nav_controller.js +43 -0
  11. data/app/assets/javascripts/kozenet_ui/controllers/user_menu_controller.js +60 -0
  12. data/app/assets/javascripts/kozenet_ui/index.js +23 -0
  13. data/app/assets/stylesheets/kozenet_ui/base.css +69 -0
  14. data/app/assets/stylesheets/kozenet_ui/components/avatar.css +88 -0
  15. data/app/assets/stylesheets/kozenet_ui/components/badge.css +101 -0
  16. data/app/assets/stylesheets/kozenet_ui/components/button.css +230 -0
  17. data/app/assets/stylesheets/kozenet_ui/components/header.css +389 -0
  18. data/app/assets/stylesheets/kozenet_ui/components/utilities.css +270 -0
  19. data/app/assets/stylesheets/kozenet_ui/components.css +8 -0
  20. data/app/assets/stylesheets/kozenet_ui/tokens.css +168 -0
  21. data/app/components/kozenet_ui/avatar_component.rb +72 -0
  22. data/app/components/kozenet_ui/badge_component.rb +62 -0
  23. data/app/components/kozenet_ui/base_component.rb +84 -0
  24. data/app/components/kozenet_ui/button_component.rb +156 -0
  25. data/app/components/kozenet_ui/header_component/action_button_component.html.erb +11 -0
  26. data/app/components/kozenet_ui/header_component/action_button_component.rb +29 -0
  27. data/app/components/kozenet_ui/header_component/brand_component.rb +32 -0
  28. data/app/components/kozenet_ui/header_component/cta_component.html.erb +5 -0
  29. data/app/components/kozenet_ui/header_component/cta_component.rb +23 -0
  30. data/app/components/kozenet_ui/header_component/nav_item_component.html.erb +8 -0
  31. data/app/components/kozenet_ui/header_component/nav_item_component.rb +28 -0
  32. data/app/components/kozenet_ui/header_component/search_component.html.erb +17 -0
  33. data/app/components/kozenet_ui/header_component/search_component.rb +29 -0
  34. data/app/components/kozenet_ui/header_component/user_menu_component.html.erb +18 -0
  35. data/app/components/kozenet_ui/header_component/user_menu_component.rb +21 -0
  36. data/app/components/kozenet_ui/header_component.html.erb +81 -0
  37. data/app/components/kozenet_ui/header_component.rb +40 -0
  38. data/app/helpers/kozenet_ui/component_helper.rb +59 -0
  39. data/app/helpers/kozenet_ui/icon_helper.rb +16 -0
  40. data/lib/generators/kozenet_ui/install/install_generator.rb +67 -0
  41. data/lib/generators/kozenet_ui/install/templates/kozenet_ui.rb +39 -0
  42. data/lib/generators/kozenet_ui/install/templates/tailwind.config.js +19 -0
  43. data/lib/kozenet_ui/configuration.rb +21 -0
  44. data/lib/kozenet_ui/engine.rb +94 -0
  45. data/lib/kozenet_ui/theme/palette.rb +132 -0
  46. data/lib/kozenet_ui/theme/tokens.rb +100 -0
  47. data/lib/kozenet_ui/theme/variants.rb +51 -0
  48. data/lib/kozenet_ui/version.rb +5 -0
  49. data/lib/kozenet_ui.rb +30 -0
  50. metadata +308 -0
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ KozenetUi.configure do |config|
4
+ # Customize your brand colors and gradients
5
+ # config.customize_colors(
6
+ # primary: "#6366f1", # Indigo
7
+ # secondary: "#8b5cf6", # Purple
8
+ # accent: "#06b6d4", # Cyan
9
+ # success: "#10b981", # Green
10
+ # warning: "#f59e0b", # Amber
11
+ # error: "#ef4444", # Red
12
+ # gradient_from: "#f0f9ff", # Light gradient start
13
+ # gradient_to: "#e0f2fe", # Light gradient end
14
+ # gradient_accent_from: "#6366f1", # Accent gradient start
15
+ # gradient_accent_via: "#0ea5e9", # Accent gradient mid
16
+ # gradient_accent_to: "#06b6d4", # Accent gradient end
17
+ # gradient_spot_1: "rgba(99,102,241,0.35)",
18
+ # gradient_spot_2: "rgba(14,165,233,0.30)",
19
+ # gradient_from_dark: "#181c2a", # Dark gradient start
20
+ # gradient_to_dark: "#23283a", # Dark gradient end
21
+ # gradient_accent_from_dark: "#1e40af", # Dark accent gradient start
22
+ # gradient_accent_via_dark: "#0369a1", # Dark accent gradient mid
23
+ # gradient_accent_to_dark: "#0891b2", # Dark accent gradient end
24
+ # gradient_spot_1_dark: "rgba(56,189,248,0.20)",
25
+ # gradient_spot_2_dark: "rgba(99,102,241,0.18)"
26
+ # )
27
+
28
+ # Default variant for components
29
+ # config.default_variant = :primary
30
+
31
+ # Default size for components
32
+ # config.default_size = :md
33
+
34
+ # Theme mode: :light, :dark, or :system (follows OS/browser)
35
+ # config.theme = :system
36
+
37
+ # Stimulus controller prefix
38
+ # config.stimulus_prefix = "kz"
39
+ end
@@ -0,0 +1,19 @@
1
+ module.exports = {
2
+ content: [
3
+ './app/views/**/*.html.erb',
4
+ './app/helpers/**/*.rb',
5
+ './app/javascript/**/*.js',
6
+ './app/components/**/*.{rb,erb}',
7
+ // Include Kozenet UI components
8
+ './vendor/bundle/ruby/**/kozenet_ui-*/app/{components,helpers,views}/**/*.{rb,erb}'
9
+ ],
10
+ darkMode: ['class', '[data-theme="dark"]'],
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ // Your custom colors will be available via CSS variables
15
+ }
16
+ }
17
+ },
18
+ plugins: []
19
+ }
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KozenetUi
4
+ # Configuration for Kozenet UI gem
5
+ class Configuration
6
+ attr_accessor :palette, :default_variant, :default_size, :theme, :stimulus_prefix
7
+
8
+ def initialize
9
+ @palette = Theme::Palette.new
10
+ @default_variant = :primary
11
+ @default_size = :md
12
+ @theme = :system
13
+ @stimulus_prefix = "kz"
14
+ end
15
+
16
+ # Allow custom color overrides including gradients
17
+ def customize_colors(colors = {})
18
+ @palette = Theme::Palette.new(colors)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+ require "view_component"
5
+
6
+ module KozenetUi
7
+ # Rails engine for Kozenet UI gem
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace KozenetUi
10
+
11
+ # Add generators path
12
+ config.generators do |g|
13
+ g.test_framework :rspec
14
+ end
15
+
16
+ # This is CRITICAL - tells Rails where to find generators
17
+ config.app_generators.scaffold_controller = :scaffold_controller
18
+
19
+ initializer "kozenet_ui.generators" do
20
+ # Explicitly add generators path
21
+ config.generators do |g|
22
+ g.templates.unshift File.expand_path("../generators", __dir__)
23
+ end
24
+ end
25
+
26
+ # Configure where to look for components
27
+ config.view_component.preview_paths << "#{root}/spec/components/previews" if Rails.env.development?
28
+
29
+ # Add assets paths (for Rails 7/Sprockets only)
30
+ if config.respond_to?(:assets) && config.assets.respond_to?(:paths)
31
+ config.assets.paths << root.join("app/assets/stylesheets")
32
+ config.assets.paths << root.join("app/assets/javascripts")
33
+
34
+ # Precompile assets
35
+ config.assets.precompile += %w[
36
+ kozenet_ui/tokens.css
37
+ kozenet_ui/base.css
38
+ kozenet_ui/components.css
39
+ kozenet_ui/index.js
40
+ ]
41
+ end
42
+
43
+ # Auto-load components
44
+ config.autoload_paths << root.join("app/components")
45
+ config.eager_load_paths << root.join("app/components")
46
+
47
+ # Make helpers available
48
+ initializer "kozenet_ui.helpers" do
49
+ ActiveSupport.on_load(:action_controller_base) do
50
+ helper KozenetUi::ComponentHelper
51
+ end
52
+ end
53
+
54
+ # Inject CSS variables into layout
55
+ initializer "kozenet_ui.theme_injection", after: :load_config_initializers do
56
+ ActiveSupport.on_load(:action_view) do
57
+ ActiveSupport.on_load(:action_view) { prepend ThemeHelper }
58
+ end
59
+ end
60
+
61
+ initializer "kozenet_ui.assets" do |app|
62
+ if app.config.respond_to?(:assets) && app.config.assets.respond_to?(:paths)
63
+ app.config.assets.paths << root.join("app/assets/stylesheets/kozenet_ui")
64
+ app.config.assets.paths << root.join("app/assets/stylesheets/kozenet_ui/components")
65
+ app.config.assets.paths << root.join("app/assets/images/kozenet_ui/icons")
66
+ end
67
+ end
68
+ end
69
+
70
+ # Helper methods for injecting Kozenet UI theme variables into views
71
+ module ThemeHelper
72
+ def kozenet_ui_theme_tag
73
+ content_tag(:style, kozenet_ui_theme_variables, nonce: content_security_policy_nonce)
74
+ end
75
+
76
+ # rubocop:disable Rails/OutputSafety
77
+ def kozenet_ui_theme_variables
78
+ <<~CSS.html_safe
79
+ :root {
80
+ /* Design Tokens */
81
+ #{KozenetUi::Theme::Tokens.to_css_variables}
82
+ /* Color Palette (Light Mode) */
83
+ #{KozenetUi.configuration.palette.to_css_variables(mode: :light)}
84
+ }
85
+
86
+ [data-theme="dark"], .dark {
87
+ /* Color Palette (Dark Mode) */
88
+ #{KozenetUi.configuration.palette.to_css_variables(mode: :dark)}
89
+ }
90
+ CSS
91
+ end
92
+ # rubocop:enable Rails/OutputSafety
93
+ end
94
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "color"
4
+
5
+ module KozenetUi
6
+ module Theme
7
+ # Dynamic color palette system with auto-generated shades
8
+ # Supports light/dark modes and custom brand colors
9
+ #
10
+ # @example
11
+ # KozenetUi::Theme::Palette.new(primary: "#123456")
12
+ class Palette
13
+ DEFAULT_COLORS = {
14
+ primary: "#6366f1", # Indigo
15
+ secondary: "#8b5cf6", # Purple
16
+ accent: "#06b6d4", # Cyan
17
+ success: "#10b981", # Green
18
+ warning: "#f59e0b", # Amber
19
+ error: "#ef4444", # Red
20
+ info: "#0ea5e9" # Sky
21
+ }.freeze
22
+
23
+ # Neutral colors (semantic)
24
+ NEUTRALS_LIGHT = {
25
+ bg_base: "#ffffff",
26
+ bg_elevated: "#f8fafc",
27
+ bg_muted: "#f1f5f9",
28
+ text_default: "#0f172a",
29
+ text_muted: "#64748b",
30
+ border_default: "#e2e8f0",
31
+ border_muted: "#f1f5f9"
32
+ }.freeze
33
+
34
+ NEUTRALS_DARK = {
35
+ bg_base: "#0f172a",
36
+ bg_elevated: "#1e293b",
37
+ bg_muted: "#334155",
38
+ text_default: "#f1f5f9",
39
+ text_muted: "#94a3b8",
40
+ border_default: "#334155",
41
+ border_muted: "#1e293b"
42
+ }.freeze
43
+
44
+ attr_reader :colors
45
+
46
+ def initialize(custom_colors = {})
47
+ @colors = DEFAULT_COLORS.merge(custom_colors)
48
+ end
49
+
50
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
51
+ def to_css_variables(mode: :light)
52
+ variables = []
53
+
54
+ # Brand colors with shades (only for valid hex colors)
55
+ @colors.each do |name, hex|
56
+ next if name.to_s.start_with?("gradient_spot_")
57
+ next if name.to_s.end_with?("_dark")
58
+
59
+ if hex.is_a?(String) && hex.match?(/^#(?:[0-9a-fA-F]{3}){1,2}$/)
60
+ shades = generate_shades(hex)
61
+ shades.each do |shade, color|
62
+ variables << "--kz-#{name}-#{shade}: #{color};"
63
+ end
64
+ else
65
+ # For non-hex, just output as a base variable
66
+ variables << "--kz-#{name}: #{hex};"
67
+ end
68
+ end
69
+
70
+ # Semantic neutrals
71
+ neutrals = mode == :dark ? NEUTRALS_DARK : NEUTRALS_LIGHT
72
+ neutrals.each do |name, value|
73
+ variables << "--kz-#{name.to_s.tr("_", "-")}: #{value};"
74
+ end
75
+
76
+ # Gradient tokens (allow user to override or fallback to palette)
77
+ if mode == :dark
78
+ variables << "--gradient-base-from: #{@colors[:gradient_from_dark] || "#181c2a"};"
79
+ variables << "--gradient-base-to: #{@colors[:gradient_to_dark] || "#23283a"};"
80
+ variables << "--gradient-accent-from: #{@colors[:gradient_accent_from_dark] || "#1e40af"};"
81
+ variables << "--gradient-accent-via: #{@colors[:gradient_accent_via_dark] || "#0369a1"};"
82
+ variables << "--gradient-accent-to: #{@colors[:gradient_accent_to_dark] || "#0891b2"};"
83
+ variables << "--gradient-spot-1: #{@colors[:gradient_spot_1_dark] || "rgba(56,189,248,0.20)"};"
84
+ variables << "--gradient-spot-2: #{@colors[:gradient_spot_2_dark] || "rgba(99,102,241,0.18)"};"
85
+ else
86
+ variables << "--gradient-base-from: #{@colors[:gradient_from] || "#f0f9ff"};"
87
+ variables << "--gradient-base-to: #{@colors[:gradient_to] || "#e0f2fe"};"
88
+ variables << "--gradient-accent-from: #{@colors[:gradient_accent_from] || "#6366f1"};"
89
+ variables << "--gradient-accent-via: #{@colors[:gradient_accent_via] || "#0ea5e9"};"
90
+ variables << "--gradient-accent-to: #{@colors[:gradient_accent_to] || "#06b6d4"};"
91
+ # rubocop:disable Naming/VariableNumber
92
+ variables << "--gradient-spot-1: #{@colors[:gradient_spot_1] || "rgba(99,102,241,0.35)"};"
93
+ variables << "--gradient-spot-2: #{@colors[:gradient_spot_2] || "rgba(14,165,233,0.30)"};"
94
+ # rubocop:enable Naming/VariableNumber
95
+ end
96
+
97
+ variables.join("\n ")
98
+ end
99
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
100
+
101
+ private
102
+
103
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
104
+ # Generate 50-900 shades from a base color
105
+ def generate_shades(hex)
106
+ base = Color::RGB.by_hex(hex)
107
+
108
+ {
109
+ 50 => lighten(base, 0.45).html,
110
+ 100 => lighten(base, 0.35).html,
111
+ 200 => lighten(base, 0.25).html,
112
+ 300 => lighten(base, 0.15).html,
113
+ 400 => lighten(base, 0.08).html,
114
+ 500 => hex, # Base color
115
+ 600 => darken(base, 0.08).html,
116
+ 700 => darken(base, 0.15).html,
117
+ 800 => darken(base, 0.25).html,
118
+ 900 => darken(base, 0.35).html
119
+ }
120
+ end
121
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
122
+
123
+ def lighten(color, amount)
124
+ color.lighten_by(amount * 100)
125
+ end
126
+
127
+ def darken(color, amount)
128
+ color.darken_by(amount * 100)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KozenetUi
4
+ module Theme
5
+ # Design tokens for the Kozenet UI system
6
+ class Tokens
7
+ # Spacing scale (in rem)
8
+ SPACING = {
9
+ xs: "0.25rem", # 4px
10
+ sm: "0.5rem", # 8px
11
+ md: "1rem", # 16px
12
+ lg: "1.5rem", # 24px
13
+ xl: "2rem", # 32px
14
+ "2xl": "3rem", # 48px
15
+ "3xl": "4rem" # 64px
16
+ }.freeze
17
+
18
+ # Border radius (Apple-style rounded)
19
+ RADIUS = {
20
+ sm: "12px",
21
+ md: "16px",
22
+ lg: "20px",
23
+ xl: "24px",
24
+ "2xl": "28px",
25
+ full: "9999px"
26
+ }.freeze
27
+
28
+ # Typography scale
29
+ FONT_SIZE = {
30
+ xs: "0.65rem", # 10.4px
31
+ sm: "0.75rem", # 12px
32
+ base: "0.875rem", # 14px
33
+ lg: "1rem", # 16px
34
+ xl: "1.25rem", # 20px
35
+ "2xl": "1.5rem", # 24px
36
+ "3xl": "2rem" # 32px
37
+ }.freeze
38
+
39
+ FONT_WEIGHT = {
40
+ normal: "400",
41
+ medium: "500",
42
+ semibold: "600",
43
+ bold: "700"
44
+ }.freeze
45
+
46
+ # Shadows for depth effects
47
+ SHADOW = {
48
+ sm: "0 1px 2px rgba(0,0,0,.05), 0 2px 6px -2px rgba(0,0,0,.08)",
49
+ md: "0 4px 14px -6px rgba(0,0,0,.25)",
50
+ lg: "0 10px 30px -12px rgba(0,0,0,.45)",
51
+ xl: "0 20px 60px -26px rgba(0,0,0,.28)"
52
+ }.freeze
53
+
54
+ # Transitions
55
+ TRANSITION = {
56
+ fast: "0.15s ease",
57
+ base: "0.25s ease",
58
+ slow: "0.35s ease"
59
+ }.freeze
60
+
61
+ # Z-index layers
62
+ Z_INDEX = {
63
+ dropdown: "50",
64
+ sticky: "60",
65
+ modal: "70",
66
+ popover: "80",
67
+ toast: "90"
68
+ }.freeze
69
+
70
+ class << self
71
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
72
+ def to_css_variables
73
+ variables = []
74
+
75
+ # Spacing
76
+ SPACING.each { |key, value| variables << "--kz-spacing-#{key}: #{value};" }
77
+
78
+ # Radius
79
+ RADIUS.each { |key, value| variables << "--kz-radius-#{key}: #{value};" }
80
+
81
+ # Typography
82
+ FONT_SIZE.each { |key, value| variables << "--kz-font-size-#{key}: #{value};" }
83
+ FONT_WEIGHT.each { |key, value| variables << "--kz-font-weight-#{key}: #{value};" }
84
+
85
+ # Shadows
86
+ SHADOW.each { |key, value| variables << "--kz-shadow-#{key}: #{value};" }
87
+
88
+ # Transitions
89
+ TRANSITION.each { |key, value| variables << "--kz-transition-#{key}: #{value};" }
90
+
91
+ # Z-index
92
+ Z_INDEX.each { |key, value| variables << "--kz-z-#{key}: #{value};" }
93
+
94
+ variables.join("\n ")
95
+ end
96
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KozenetUi
4
+ module Theme
5
+ # Variant mappings for components
6
+ # Maps semantic variants to Tailwind classes
7
+ class Variants
8
+ BUTTON = {
9
+ primary: "kz-variant-primary",
10
+ secondary: "kz-variant-secondary",
11
+ accent: "kz-variant-accent",
12
+ success: "kz-variant-success",
13
+ warning: "kz-variant-warning",
14
+ error: "kz-variant-error",
15
+ ghost: "kz-variant-ghost",
16
+ outline: "kz-variant-outline"
17
+ }.freeze
18
+
19
+ BADGE = {
20
+ primary: "kz-badge-primary",
21
+ secondary: "kz-badge-secondary",
22
+ success: "kz-badge-success",
23
+ warning: "kz-badge-warning",
24
+ error: "kz-badge-error",
25
+ info: "kz-badge-info"
26
+ }.freeze
27
+
28
+ SIZES = {
29
+ xs: "kz-size-xs",
30
+ sm: "kz-size-sm",
31
+ md: "kz-size-md",
32
+ lg: "kz-size-lg",
33
+ xl: "kz-size-xl"
34
+ }.freeze
35
+
36
+ class << self
37
+ def button(variant)
38
+ BUTTON[variant] || BUTTON[:primary]
39
+ end
40
+
41
+ def badge(variant)
42
+ BADGE[variant] || BADGE[:primary]
43
+ end
44
+
45
+ def size(size)
46
+ SIZES[size] || SIZES[:md]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KozenetUi
4
+ VERSION = "0.1.0"
5
+ end
data/lib/kozenet_ui.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "kozenet_ui/version"
4
+ require_relative "kozenet_ui/engine"
5
+ require_relative "kozenet_ui/configuration"
6
+ require_relative "kozenet_ui/theme/tokens"
7
+ require_relative "kozenet_ui/theme/palette"
8
+ require_relative "kozenet_ui/theme/variants"
9
+
10
+ # Main entry point for Kozenet UI gem
11
+ module KozenetUi
12
+ class Error < StandardError; end
13
+
14
+ class << self
15
+ attr_writer :configuration
16
+
17
+ def configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+
21
+ def configure
22
+ yield(configuration)
23
+ end
24
+
25
+ # Reset configuration (useful for testing)
26
+ def reset_configuration!
27
+ @configuration = Configuration.new
28
+ end
29
+ end
30
+ end