shadcn-phlex 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 (113) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +195 -0
  3. data/app.css +20 -0
  4. data/css/shadcn-source.css +3 -0
  5. data/css/shadcn-tailwind.css +160 -0
  6. data/css/themes/mauve.css +62 -0
  7. data/css/themes/mist.css +62 -0
  8. data/css/themes/neutral.css +74 -0
  9. data/css/themes/olive.css +62 -0
  10. data/css/themes/stone.css +62 -0
  11. data/css/themes/taupe.css +62 -0
  12. data/css/themes/zinc.css +62 -0
  13. data/js/controllers/accordion_controller.js +135 -0
  14. data/js/controllers/checkbox_controller.js +52 -0
  15. data/js/controllers/collapsible_controller.js +85 -0
  16. data/js/controllers/combobox_controller.js +168 -0
  17. data/js/controllers/command_controller.js +171 -0
  18. data/js/controllers/context_menu_controller.js +132 -0
  19. data/js/controllers/dark_mode_controller.js +106 -0
  20. data/js/controllers/dialog_controller.js +205 -0
  21. data/js/controllers/drawer_controller.js +161 -0
  22. data/js/controllers/dropdown_menu_controller.js +189 -0
  23. data/js/controllers/hover_card_controller.js +85 -0
  24. data/js/controllers/index.js +89 -0
  25. data/js/controllers/menubar_controller.js +171 -0
  26. data/js/controllers/navigation_menu_controller.js +160 -0
  27. data/js/controllers/popover_controller.js +151 -0
  28. data/js/controllers/radio_group_controller.js +78 -0
  29. data/js/controllers/scroll_area_controller.js +117 -0
  30. data/js/controllers/select_controller.js +198 -0
  31. data/js/controllers/sheet_controller.js +130 -0
  32. data/js/controllers/slider_controller.js +142 -0
  33. data/js/controllers/switch_controller.js +40 -0
  34. data/js/controllers/tabs_controller.js +96 -0
  35. data/js/controllers/toast_controller.js +206 -0
  36. data/js/controllers/toggle_controller.js +30 -0
  37. data/js/controllers/toggle_group_controller.js +73 -0
  38. data/js/controllers/tooltip_controller.js +146 -0
  39. data/lib/generators/shadcn_phlex/component_generator.rb +79 -0
  40. data/lib/generators/shadcn_phlex/install_generator.rb +217 -0
  41. data/lib/shadcn/base.rb +27 -0
  42. data/lib/shadcn/engine.rb +24 -0
  43. data/lib/shadcn/kit.rb +1158 -0
  44. data/lib/shadcn/themes/accent_colors.rb +106 -0
  45. data/lib/shadcn/themes/base_colors.rb +313 -0
  46. data/lib/shadcn/ui/accordion.rb +135 -0
  47. data/lib/shadcn/ui/alert.rb +79 -0
  48. data/lib/shadcn/ui/alert_dialog.rb +220 -0
  49. data/lib/shadcn/ui/aspect_ratio.rb +35 -0
  50. data/lib/shadcn/ui/avatar.rb +134 -0
  51. data/lib/shadcn/ui/badge.rb +48 -0
  52. data/lib/shadcn/ui/breadcrumb.rb +180 -0
  53. data/lib/shadcn/ui/button.rb +63 -0
  54. data/lib/shadcn/ui/button_group.rb +58 -0
  55. data/lib/shadcn/ui/card.rb +133 -0
  56. data/lib/shadcn/ui/checkbox.rb +72 -0
  57. data/lib/shadcn/ui/collapsible.rb +76 -0
  58. data/lib/shadcn/ui/combobox.rb +229 -0
  59. data/lib/shadcn/ui/command.rb +256 -0
  60. data/lib/shadcn/ui/context_menu.rb +319 -0
  61. data/lib/shadcn/ui/dialog.rb +226 -0
  62. data/lib/shadcn/ui/direction.rb +23 -0
  63. data/lib/shadcn/ui/drawer.rb +217 -0
  64. data/lib/shadcn/ui/dropdown_menu.rb +384 -0
  65. data/lib/shadcn/ui/empty.rb +97 -0
  66. data/lib/shadcn/ui/field.rb +126 -0
  67. data/lib/shadcn/ui/hover_card.rb +75 -0
  68. data/lib/shadcn/ui/input.rb +36 -0
  69. data/lib/shadcn/ui/input_group.rb +32 -0
  70. data/lib/shadcn/ui/input_otp.rb +112 -0
  71. data/lib/shadcn/ui/item.rb +115 -0
  72. data/lib/shadcn/ui/kbd.rb +45 -0
  73. data/lib/shadcn/ui/label.rb +28 -0
  74. data/lib/shadcn/ui/menubar.rb +345 -0
  75. data/lib/shadcn/ui/native_select.rb +31 -0
  76. data/lib/shadcn/ui/navigation_menu.rb +238 -0
  77. data/lib/shadcn/ui/pagination.rb +224 -0
  78. data/lib/shadcn/ui/popover.rb +147 -0
  79. data/lib/shadcn/ui/progress.rb +40 -0
  80. data/lib/shadcn/ui/radio_group.rb +92 -0
  81. data/lib/shadcn/ui/resizable.rb +108 -0
  82. data/lib/shadcn/ui/scroll_area.rb +75 -0
  83. data/lib/shadcn/ui/select.rb +235 -0
  84. data/lib/shadcn/ui/separator.rb +36 -0
  85. data/lib/shadcn/ui/sheet.rb +231 -0
  86. data/lib/shadcn/ui/sidebar.rb +420 -0
  87. data/lib/shadcn/ui/skeleton.rb +23 -0
  88. data/lib/shadcn/ui/slider.rb +72 -0
  89. data/lib/shadcn/ui/sonner.rb +177 -0
  90. data/lib/shadcn/ui/spinner.rb +58 -0
  91. data/lib/shadcn/ui/switch.rb +75 -0
  92. data/lib/shadcn/ui/table.rb +154 -0
  93. data/lib/shadcn/ui/tabs.rb +154 -0
  94. data/lib/shadcn/ui/text_field.rb +146 -0
  95. data/lib/shadcn/ui/textarea.rb +32 -0
  96. data/lib/shadcn/ui/theme_toggle.rb +74 -0
  97. data/lib/shadcn/ui/toggle.rb +66 -0
  98. data/lib/shadcn/ui/toggle_group.rb +75 -0
  99. data/lib/shadcn/ui/tooltip.rb +78 -0
  100. data/lib/shadcn/ui/typography.rb +217 -0
  101. data/lib/shadcn/version.rb +5 -0
  102. data/lib/shadcn-phlex.rb +6 -0
  103. data/lib/shadcn.rb +80 -0
  104. data/package.json +14 -0
  105. data/skills/shadcn-phlex/SKILL.md +190 -0
  106. data/skills/shadcn-phlex/evals/evals.json +90 -0
  107. data/skills/shadcn-phlex/references/component-catalog.md +355 -0
  108. data/skills/shadcn-phlex/rules/composition.md +235 -0
  109. data/skills/shadcn-phlex/rules/forms.md +151 -0
  110. data/skills/shadcn-phlex/rules/helpers.md +54 -0
  111. data/skills/shadcn-phlex/rules/stimulus.md +61 -0
  112. data/skills/shadcn-phlex/rules/styling.md +177 -0
  113. metadata +209 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Themes
5
+ # All 17 accent color themes from shadcn v4
6
+ # Each overrides only primary, secondary, chart, and sidebar-primary
7
+ # Merged on top of a base color palette
8
+ ACCENT_COLORS = {
9
+ blue: {
10
+ light: { primary: "oklch(0.546 0.245 262.881)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.944 0.032 255.585)", secondary_foreground: "oklch(0.379 0.146 265.522)" },
11
+ dark: { primary: "oklch(0.623 0.214 259.815)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.268 0.058 264.695)", secondary_foreground: "oklch(0.809 0.105 251.813)" }
12
+ },
13
+ red: {
14
+ light: { primary: "oklch(0.577 0.245 27.325)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.956 0.042 17.717)", secondary_foreground: "oklch(0.444 0.177 26.899)" },
15
+ dark: { primary: "oklch(0.637 0.237 25.331)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.279 0.065 26.935)", secondary_foreground: "oklch(0.795 0.124 19.25)" }
16
+ },
17
+ green: {
18
+ light: { primary: "oklch(0.595 0.18 163.319)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.952 0.043 166.913)", secondary_foreground: "oklch(0.448 0.119 163.331)" },
19
+ dark: { primary: "oklch(0.648 0.2 163.122)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.268 0.058 163.564)", secondary_foreground: "oklch(0.809 0.108 163.599)" }
20
+ },
21
+ orange: {
22
+ light: { primary: "oklch(0.705 0.213 47.604)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.954 0.043 48.561)", secondary_foreground: "oklch(0.533 0.164 48.998)" },
23
+ dark: { primary: "oklch(0.705 0.213 47.604)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.282 0.06 47.917)", secondary_foreground: "oklch(0.822 0.107 47.857)" }
24
+ },
25
+ violet: {
26
+ light: { primary: "oklch(0.541 0.281 293.009)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.943 0.05 294.588)", secondary_foreground: "oklch(0.408 0.208 292.361)" },
27
+ dark: { primary: "oklch(0.624 0.249 292.717)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.265 0.075 293.141)", secondary_foreground: "oklch(0.802 0.128 293.051)" }
28
+ },
29
+ amber: {
30
+ light: { primary: "oklch(0.769 0.188 70.08)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.963 0.043 76.318)", secondary_foreground: "oklch(0.555 0.163 48.998)" },
31
+ dark: { primary: "oklch(0.769 0.188 70.08)", primary_foreground: "oklch(0.296 0.07 45.622)", secondary: "oklch(0.282 0.059 50.058)", secondary_foreground: "oklch(0.822 0.107 56.366)" }
32
+ },
33
+ cyan: {
34
+ light: { primary: "oklch(0.6 0.118 184.714)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.952 0.032 190.9)", secondary_foreground: "oklch(0.448 0.098 184.364)" },
35
+ dark: { primary: "oklch(0.648 0.145 184.525)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.263 0.048 184.525)", secondary_foreground: "oklch(0.797 0.08 184.525)" }
36
+ },
37
+ emerald: {
38
+ light: { primary: "oklch(0.596 0.145 163.225)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.952 0.037 166.913)", secondary_foreground: "oklch(0.448 0.107 163.331)" },
39
+ dark: { primary: "oklch(0.648 0.178 163.122)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.268 0.048 163.564)", secondary_foreground: "oklch(0.809 0.093 163.599)" }
40
+ },
41
+ fuchsia: {
42
+ light: { primary: "oklch(0.591 0.293 322.896)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.952 0.05 325.661)", secondary_foreground: "oklch(0.444 0.222 323.29)" },
43
+ dark: { primary: "oklch(0.667 0.295 322.15)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.274 0.076 322.827)", secondary_foreground: "oklch(0.814 0.151 322.827)" }
44
+ },
45
+ indigo: {
46
+ light: { primary: "oklch(0.508 0.242 274.966)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.943 0.04 272.788)", secondary_foreground: "oklch(0.377 0.178 275.513)" },
47
+ dark: { primary: "oklch(0.585 0.233 277.117)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.264 0.066 275.513)", secondary_foreground: "oklch(0.793 0.118 274.373)" }
48
+ },
49
+ lime: {
50
+ light: { primary: "oklch(0.768 0.233 130.85)", primary_foreground: "oklch(0.296 0.1 130.85)", secondary: "oklch(0.959 0.058 131.684)", secondary_foreground: "oklch(0.487 0.174 131.684)" },
51
+ dark: { primary: "oklch(0.768 0.233 130.85)", primary_foreground: "oklch(0.296 0.1 130.85)", secondary: "oklch(0.276 0.06 131.175)", secondary_foreground: "oklch(0.813 0.116 131.175)" }
52
+ },
53
+ pink: {
54
+ light: { primary: "oklch(0.592 0.249 0.584)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.953 0.04 0.584)", secondary_foreground: "oklch(0.445 0.187 0.584)" },
55
+ dark: { primary: "oklch(0.656 0.241 354.308)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.275 0.065 354.308)", secondary_foreground: "oklch(0.81 0.12 354.308)" }
56
+ },
57
+ purple: {
58
+ light: { primary: "oklch(0.546 0.245 292.881)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.943 0.05 292.588)", secondary_foreground: "oklch(0.408 0.182 292.881)" },
59
+ dark: { primary: "oklch(0.624 0.249 292.717)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.265 0.065 292.717)", secondary_foreground: "oklch(0.802 0.128 292.717)" }
60
+ },
61
+ rose: {
62
+ light: { primary: "oklch(0.585 0.247 17.585)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.955 0.04 17.585)", secondary_foreground: "oklch(0.444 0.183 17.585)" },
63
+ dark: { primary: "oklch(0.645 0.246 16.439)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.278 0.063 16.439)", secondary_foreground: "oklch(0.807 0.122 16.439)" }
64
+ },
65
+ sky: {
66
+ light: { primary: "oklch(0.585 0.187 227.392)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.946 0.036 230.9)", secondary_foreground: "oklch(0.432 0.14 227.392)" },
67
+ dark: { primary: "oklch(0.63 0.178 230.902)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.265 0.054 230.902)", secondary_foreground: "oklch(0.801 0.094 230.902)" }
68
+ },
69
+ teal: {
70
+ light: { primary: "oklch(0.6 0.118 184.714)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.952 0.032 190.9)", secondary_foreground: "oklch(0.448 0.098 184.364)" },
71
+ dark: { primary: "oklch(0.648 0.145 184.525)", primary_foreground: "oklch(0.985 0 0)", secondary: "oklch(0.263 0.048 184.525)", secondary_foreground: "oklch(0.797 0.08 184.525)" }
72
+ },
73
+ yellow: {
74
+ light: { primary: "oklch(0.795 0.184 86.047)", primary_foreground: "oklch(0.296 0.07 45.622)", secondary: "oklch(0.963 0.043 86.318)", secondary_foreground: "oklch(0.555 0.163 68.998)" },
75
+ dark: { primary: "oklch(0.795 0.184 86.047)", primary_foreground: "oklch(0.296 0.07 45.622)", secondary: "oklch(0.282 0.059 86.058)", secondary_foreground: "oklch(0.822 0.107 86.366)" }
76
+ }
77
+ }.freeze
78
+
79
+ # Generate CSS for a theme by merging base + accent
80
+ def self.generate_css(base_color: :neutral, accent_color: nil, radius: "0.625rem")
81
+ base = BASE_COLORS[base_color]
82
+ raise ArgumentError, "Unknown base color: #{base_color}" unless base
83
+
84
+ light_vars = base[:light].dup
85
+ dark_vars = base[:dark].dup
86
+
87
+ if accent_color
88
+ accent = ACCENT_COLORS[accent_color]
89
+ raise ArgumentError, "Unknown accent color: #{accent_color}" unless accent
90
+ light_vars.merge!(accent[:light])
91
+ dark_vars.merge!(accent[:dark])
92
+ end
93
+
94
+ css = ":root {\n --radius: #{radius};\n"
95
+ light_vars.each do |key, value|
96
+ css << " --#{key.to_s.tr('_', '-')}: #{value};\n"
97
+ end
98
+ css << "}\n\n.dark {\n"
99
+ dark_vars.each do |key, value|
100
+ css << " --#{key.to_s.tr('_', '-')}: #{value};\n"
101
+ end
102
+ css << "}\n"
103
+ css
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,313 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module Themes
5
+ # All 7 base color palettes from shadcn v4
6
+ # Each defines the full set of CSS variables for light and dark modes
7
+ BASE_COLORS = {
8
+ neutral: {
9
+ light: {
10
+ background: "oklch(1 0 0)",
11
+ foreground: "oklch(0.145 0 0)",
12
+ card: "oklch(1 0 0)",
13
+ card_foreground: "oklch(0.145 0 0)",
14
+ popover: "oklch(1 0 0)",
15
+ popover_foreground: "oklch(0.145 0 0)",
16
+ primary: "oklch(0.205 0 0)",
17
+ primary_foreground: "oklch(0.985 0 0)",
18
+ secondary: "oklch(0.97 0 0)",
19
+ secondary_foreground: "oklch(0.205 0 0)",
20
+ muted: "oklch(0.97 0 0)",
21
+ muted_foreground: "oklch(0.556 0 0)",
22
+ accent: "oklch(0.97 0 0)",
23
+ accent_foreground: "oklch(0.205 0 0)",
24
+ destructive: "oklch(0.577 0.245 27.325)",
25
+ border: "oklch(0.922 0 0)",
26
+ input: "oklch(0.922 0 0)",
27
+ ring: "oklch(0.708 0 0)",
28
+ sidebar: "oklch(0.985 0 0)",
29
+ sidebar_foreground: "oklch(0.145 0 0)",
30
+ sidebar_primary: "oklch(0.205 0 0)",
31
+ sidebar_primary_foreground: "oklch(0.985 0 0)",
32
+ sidebar_accent: "oklch(0.97 0 0)",
33
+ sidebar_accent_foreground: "oklch(0.205 0 0)",
34
+ sidebar_border: "oklch(0.922 0 0)",
35
+ sidebar_ring: "oklch(0.708 0 0)"
36
+ },
37
+ dark: {
38
+ background: "oklch(0.145 0 0)",
39
+ foreground: "oklch(0.985 0 0)",
40
+ card: "oklch(0.205 0 0)",
41
+ card_foreground: "oklch(0.985 0 0)",
42
+ popover: "oklch(0.205 0 0)",
43
+ popover_foreground: "oklch(0.985 0 0)",
44
+ primary: "oklch(0.922 0 0)",
45
+ primary_foreground: "oklch(0.205 0 0)",
46
+ secondary: "oklch(0.269 0 0)",
47
+ secondary_foreground: "oklch(0.985 0 0)",
48
+ muted: "oklch(0.269 0 0)",
49
+ muted_foreground: "oklch(0.708 0 0)",
50
+ accent: "oklch(0.269 0 0)",
51
+ accent_foreground: "oklch(0.985 0 0)",
52
+ destructive: "oklch(0.704 0.191 22.216)",
53
+ border: "oklch(1 0 0 / 10%)",
54
+ input: "oklch(1 0 0 / 15%)",
55
+ ring: "oklch(0.556 0 0)",
56
+ sidebar: "oklch(0.205 0 0)",
57
+ sidebar_foreground: "oklch(0.985 0 0)",
58
+ sidebar_primary: "oklch(0.488 0.243 264.376)",
59
+ sidebar_primary_foreground: "oklch(0.985 0 0)",
60
+ sidebar_accent: "oklch(0.269 0 0)",
61
+ sidebar_accent_foreground: "oklch(0.985 0 0)",
62
+ sidebar_border: "oklch(1 0 0 / 10%)",
63
+ sidebar_ring: "oklch(0.556 0 0)"
64
+ }
65
+ },
66
+ stone: {
67
+ light: {
68
+ background: "oklch(1 0 75)",
69
+ foreground: "oklch(0.147 0.004 49.25)",
70
+ card: "oklch(1 0 75)",
71
+ card_foreground: "oklch(0.147 0.004 49.25)",
72
+ popover: "oklch(1 0 75)",
73
+ popover_foreground: "oklch(0.147 0.004 49.25)",
74
+ primary: "oklch(0.216 0.006 56.043)",
75
+ primary_foreground: "oklch(0.985 0.001 106.423)",
76
+ secondary: "oklch(0.97 0.001 106.424)",
77
+ secondary_foreground: "oklch(0.216 0.006 56.043)",
78
+ muted: "oklch(0.97 0.001 106.424)",
79
+ muted_foreground: "oklch(0.553 0.013 58.071)",
80
+ accent: "oklch(0.97 0.001 106.424)",
81
+ accent_foreground: "oklch(0.216 0.006 56.043)",
82
+ destructive: "oklch(0.577 0.245 27.325)",
83
+ border: "oklch(0.923 0.003 48.717)",
84
+ input: "oklch(0.923 0.003 48.717)",
85
+ ring: "oklch(0.709 0.01 56.259)",
86
+ sidebar: "oklch(0.985 0.001 106.423)",
87
+ sidebar_foreground: "oklch(0.147 0.004 49.25)",
88
+ sidebar_primary: "oklch(0.216 0.006 56.043)",
89
+ sidebar_primary_foreground: "oklch(0.985 0.001 106.423)",
90
+ sidebar_accent: "oklch(0.97 0.001 106.424)",
91
+ sidebar_accent_foreground: "oklch(0.216 0.006 56.043)",
92
+ sidebar_border: "oklch(0.923 0.003 48.717)",
93
+ sidebar_ring: "oklch(0.709 0.01 56.259)"
94
+ },
95
+ dark: {
96
+ background: "oklch(0.147 0.004 49.25)",
97
+ foreground: "oklch(0.985 0.001 106.423)",
98
+ card: "oklch(0.216 0.006 56.043)",
99
+ card_foreground: "oklch(0.985 0.001 106.423)",
100
+ popover: "oklch(0.216 0.006 56.043)",
101
+ popover_foreground: "oklch(0.985 0.001 106.423)",
102
+ primary: "oklch(0.923 0.003 48.717)",
103
+ primary_foreground: "oklch(0.216 0.006 56.043)",
104
+ secondary: "oklch(0.268 0.007 34.298)",
105
+ secondary_foreground: "oklch(0.985 0.001 106.423)",
106
+ muted: "oklch(0.268 0.007 34.298)",
107
+ muted_foreground: "oklch(0.709 0.01 56.259)",
108
+ accent: "oklch(0.268 0.007 34.298)",
109
+ accent_foreground: "oklch(0.985 0.001 106.423)",
110
+ destructive: "oklch(0.704 0.191 22.216)",
111
+ border: "oklch(1 0 0 / 10%)",
112
+ input: "oklch(1 0 0 / 15%)",
113
+ ring: "oklch(0.553 0.013 58.071)",
114
+ sidebar: "oklch(0.216 0.006 56.043)",
115
+ sidebar_foreground: "oklch(0.985 0.001 106.423)",
116
+ sidebar_primary: "oklch(0.488 0.243 264.376)",
117
+ sidebar_primary_foreground: "oklch(0.985 0 0)",
118
+ sidebar_accent: "oklch(0.268 0.007 34.298)",
119
+ sidebar_accent_foreground: "oklch(0.985 0.001 106.423)",
120
+ sidebar_border: "oklch(1 0 0 / 10%)",
121
+ sidebar_ring: "oklch(0.553 0.013 58.071)"
122
+ }
123
+ },
124
+ zinc: {
125
+ light: {
126
+ background: "oklch(1 0 0)",
127
+ foreground: "oklch(0.141 0.005 285.823)",
128
+ card: "oklch(1 0 0)",
129
+ card_foreground: "oklch(0.141 0.005 285.823)",
130
+ popover: "oklch(1 0 0)",
131
+ popover_foreground: "oklch(0.141 0.005 285.823)",
132
+ primary: "oklch(0.21 0.006 285.885)",
133
+ primary_foreground: "oklch(0.985 0.002 247.839)",
134
+ secondary: "oklch(0.967 0.001 264.542)",
135
+ secondary_foreground: "oklch(0.21 0.006 285.885)",
136
+ muted: "oklch(0.967 0.001 264.542)",
137
+ muted_foreground: "oklch(0.552 0.016 285.938)",
138
+ accent: "oklch(0.967 0.001 264.542)",
139
+ accent_foreground: "oklch(0.21 0.006 285.885)",
140
+ destructive: "oklch(0.577 0.245 27.325)",
141
+ border: "oklch(0.92 0.004 264.532)",
142
+ input: "oklch(0.92 0.004 264.532)",
143
+ ring: "oklch(0.705 0.015 286.067)",
144
+ sidebar: "oklch(0.985 0 0)",
145
+ sidebar_foreground: "oklch(0.141 0.005 285.823)",
146
+ sidebar_primary: "oklch(0.21 0.006 285.885)",
147
+ sidebar_primary_foreground: "oklch(0.985 0.002 247.839)",
148
+ sidebar_accent: "oklch(0.967 0.001 264.542)",
149
+ sidebar_accent_foreground: "oklch(0.21 0.006 285.885)",
150
+ sidebar_border: "oklch(0.92 0.004 264.532)",
151
+ sidebar_ring: "oklch(0.705 0.015 286.067)"
152
+ },
153
+ dark: {
154
+ background: "oklch(0.141 0.005 285.823)",
155
+ foreground: "oklch(0.985 0.002 247.839)",
156
+ card: "oklch(0.21 0.006 285.885)",
157
+ card_foreground: "oklch(0.985 0.002 247.839)",
158
+ popover: "oklch(0.21 0.006 285.885)",
159
+ popover_foreground: "oklch(0.985 0.002 247.839)",
160
+ primary: "oklch(0.92 0.004 264.532)",
161
+ primary_foreground: "oklch(0.21 0.006 285.885)",
162
+ secondary: "oklch(0.274 0.006 286.033)",
163
+ secondary_foreground: "oklch(0.985 0.002 247.839)",
164
+ muted: "oklch(0.274 0.006 286.033)",
165
+ muted_foreground: "oklch(0.705 0.015 286.067)",
166
+ accent: "oklch(0.274 0.006 286.033)",
167
+ accent_foreground: "oklch(0.985 0.002 247.839)",
168
+ destructive: "oklch(0.704 0.191 22.216)",
169
+ border: "oklch(1 0 0 / 10%)",
170
+ input: "oklch(1 0 0 / 15%)",
171
+ ring: "oklch(0.552 0.016 285.938)",
172
+ sidebar: "oklch(0.21 0.006 285.885)",
173
+ sidebar_foreground: "oklch(0.985 0.002 247.839)",
174
+ sidebar_primary: "oklch(0.488 0.243 264.376)",
175
+ sidebar_primary_foreground: "oklch(0.985 0 0)",
176
+ sidebar_accent: "oklch(0.274 0.006 286.033)",
177
+ sidebar_accent_foreground: "oklch(0.985 0.002 247.839)",
178
+ sidebar_border: "oklch(1 0 0 / 10%)",
179
+ sidebar_ring: "oklch(0.552 0.016 285.938)"
180
+ }
181
+ },
182
+ mauve: {
183
+ light: {
184
+ background: "oklch(1 0 0)", foreground: "oklch(0.153 0.014 302)",
185
+ card: "oklch(1 0 0)", card_foreground: "oklch(0.153 0.014 302)",
186
+ popover: "oklch(1 0 0)", popover_foreground: "oklch(0.153 0.014 302)",
187
+ primary: "oklch(0.218 0.019 302)", primary_foreground: "oklch(0.985 0.003 302)",
188
+ secondary: "oklch(0.968 0.005 302)", secondary_foreground: "oklch(0.218 0.019 302)",
189
+ muted: "oklch(0.968 0.005 302)", muted_foreground: "oklch(0.554 0.022 302)",
190
+ accent: "oklch(0.968 0.005 302)", accent_foreground: "oklch(0.218 0.019 302)",
191
+ destructive: "oklch(0.577 0.245 27.325)",
192
+ border: "oklch(0.921 0.008 302)", input: "oklch(0.921 0.008 302)",
193
+ ring: "oklch(0.706 0.02 302)",
194
+ sidebar: "oklch(0.985 0.003 302)", sidebar_foreground: "oklch(0.153 0.014 302)",
195
+ sidebar_primary: "oklch(0.218 0.019 302)", sidebar_primary_foreground: "oklch(0.985 0.003 302)",
196
+ sidebar_accent: "oklch(0.968 0.005 302)", sidebar_accent_foreground: "oklch(0.218 0.019 302)",
197
+ sidebar_border: "oklch(0.921 0.008 302)", sidebar_ring: "oklch(0.706 0.02 302)"
198
+ },
199
+ dark: {
200
+ background: "oklch(0.153 0.014 302)", foreground: "oklch(0.985 0.003 302)",
201
+ card: "oklch(0.218 0.019 302)", card_foreground: "oklch(0.985 0.003 302)",
202
+ popover: "oklch(0.218 0.019 302)", popover_foreground: "oklch(0.985 0.003 302)",
203
+ primary: "oklch(0.921 0.008 302)", primary_foreground: "oklch(0.218 0.019 302)",
204
+ secondary: "oklch(0.274 0.019 302)", secondary_foreground: "oklch(0.985 0.003 302)",
205
+ muted: "oklch(0.274 0.019 302)", muted_foreground: "oklch(0.706 0.02 302)",
206
+ accent: "oklch(0.274 0.019 302)", accent_foreground: "oklch(0.985 0.003 302)",
207
+ destructive: "oklch(0.704 0.191 22.216)",
208
+ border: "oklch(1 0 0 / 10%)", input: "oklch(1 0 0 / 15%)", ring: "oklch(0.554 0.022 302)",
209
+ sidebar: "oklch(0.218 0.019 302)", sidebar_foreground: "oklch(0.985 0.003 302)",
210
+ sidebar_primary: "oklch(0.488 0.243 264.376)", sidebar_primary_foreground: "oklch(0.985 0 0)",
211
+ sidebar_accent: "oklch(0.274 0.019 302)", sidebar_accent_foreground: "oklch(0.985 0.003 302)",
212
+ sidebar_border: "oklch(1 0 0 / 10%)", sidebar_ring: "oklch(0.554 0.022 302)"
213
+ }
214
+ },
215
+ olive: {
216
+ light: {
217
+ background: "oklch(1 0 0)", foreground: "oklch(0.155 0.014 155)",
218
+ card: "oklch(1 0 0)", card_foreground: "oklch(0.155 0.014 155)",
219
+ popover: "oklch(1 0 0)", popover_foreground: "oklch(0.155 0.014 155)",
220
+ primary: "oklch(0.22 0.018 155)", primary_foreground: "oklch(0.985 0.003 155)",
221
+ secondary: "oklch(0.968 0.005 155)", secondary_foreground: "oklch(0.22 0.018 155)",
222
+ muted: "oklch(0.968 0.005 155)", muted_foreground: "oklch(0.556 0.02 155)",
223
+ accent: "oklch(0.968 0.005 155)", accent_foreground: "oklch(0.22 0.018 155)",
224
+ destructive: "oklch(0.577 0.245 27.325)",
225
+ border: "oklch(0.923 0.008 155)", input: "oklch(0.923 0.008 155)", ring: "oklch(0.708 0.018 155)",
226
+ sidebar: "oklch(0.985 0.003 155)", sidebar_foreground: "oklch(0.155 0.014 155)",
227
+ sidebar_primary: "oklch(0.22 0.018 155)", sidebar_primary_foreground: "oklch(0.985 0.003 155)",
228
+ sidebar_accent: "oklch(0.968 0.005 155)", sidebar_accent_foreground: "oklch(0.22 0.018 155)",
229
+ sidebar_border: "oklch(0.923 0.008 155)", sidebar_ring: "oklch(0.708 0.018 155)"
230
+ },
231
+ dark: {
232
+ background: "oklch(0.155 0.014 155)", foreground: "oklch(0.985 0.003 155)",
233
+ card: "oklch(0.22 0.018 155)", card_foreground: "oklch(0.985 0.003 155)",
234
+ popover: "oklch(0.22 0.018 155)", popover_foreground: "oklch(0.985 0.003 155)",
235
+ primary: "oklch(0.923 0.008 155)", primary_foreground: "oklch(0.22 0.018 155)",
236
+ secondary: "oklch(0.276 0.018 155)", secondary_foreground: "oklch(0.985 0.003 155)",
237
+ muted: "oklch(0.276 0.018 155)", muted_foreground: "oklch(0.708 0.018 155)",
238
+ accent: "oklch(0.276 0.018 155)", accent_foreground: "oklch(0.985 0.003 155)",
239
+ destructive: "oklch(0.704 0.191 22.216)",
240
+ border: "oklch(1 0 0 / 10%)", input: "oklch(1 0 0 / 15%)", ring: "oklch(0.556 0.02 155)",
241
+ sidebar: "oklch(0.22 0.018 155)", sidebar_foreground: "oklch(0.985 0.003 155)",
242
+ sidebar_primary: "oklch(0.488 0.243 264.376)", sidebar_primary_foreground: "oklch(0.985 0 0)",
243
+ sidebar_accent: "oklch(0.276 0.018 155)", sidebar_accent_foreground: "oklch(0.985 0.003 155)",
244
+ sidebar_border: "oklch(1 0 0 / 10%)", sidebar_ring: "oklch(0.556 0.02 155)"
245
+ }
246
+ },
247
+ mist: {
248
+ light: {
249
+ background: "oklch(1 0 0)", foreground: "oklch(0.15 0.01 230)",
250
+ card: "oklch(1 0 0)", card_foreground: "oklch(0.15 0.01 230)",
251
+ popover: "oklch(1 0 0)", popover_foreground: "oklch(0.15 0.01 230)",
252
+ primary: "oklch(0.215 0.015 230)", primary_foreground: "oklch(0.985 0.002 230)",
253
+ secondary: "oklch(0.968 0.004 230)", secondary_foreground: "oklch(0.215 0.015 230)",
254
+ muted: "oklch(0.968 0.004 230)", muted_foreground: "oklch(0.555 0.018 230)",
255
+ accent: "oklch(0.968 0.004 230)", accent_foreground: "oklch(0.215 0.015 230)",
256
+ destructive: "oklch(0.577 0.245 27.325)",
257
+ border: "oklch(0.922 0.006 230)", input: "oklch(0.922 0.006 230)", ring: "oklch(0.707 0.016 230)",
258
+ sidebar: "oklch(0.985 0.002 230)", sidebar_foreground: "oklch(0.15 0.01 230)",
259
+ sidebar_primary: "oklch(0.215 0.015 230)", sidebar_primary_foreground: "oklch(0.985 0.002 230)",
260
+ sidebar_accent: "oklch(0.968 0.004 230)", sidebar_accent_foreground: "oklch(0.215 0.015 230)",
261
+ sidebar_border: "oklch(0.922 0.006 230)", sidebar_ring: "oklch(0.707 0.016 230)"
262
+ },
263
+ dark: {
264
+ background: "oklch(0.15 0.01 230)", foreground: "oklch(0.985 0.002 230)",
265
+ card: "oklch(0.215 0.015 230)", card_foreground: "oklch(0.985 0.002 230)",
266
+ popover: "oklch(0.215 0.015 230)", popover_foreground: "oklch(0.985 0.002 230)",
267
+ primary: "oklch(0.922 0.006 230)", primary_foreground: "oklch(0.215 0.015 230)",
268
+ secondary: "oklch(0.272 0.015 230)", secondary_foreground: "oklch(0.985 0.002 230)",
269
+ muted: "oklch(0.272 0.015 230)", muted_foreground: "oklch(0.707 0.016 230)",
270
+ accent: "oklch(0.272 0.015 230)", accent_foreground: "oklch(0.985 0.002 230)",
271
+ destructive: "oklch(0.704 0.191 22.216)",
272
+ border: "oklch(1 0 0 / 10%)", input: "oklch(1 0 0 / 15%)", ring: "oklch(0.555 0.018 230)",
273
+ sidebar: "oklch(0.215 0.015 230)", sidebar_foreground: "oklch(0.985 0.002 230)",
274
+ sidebar_primary: "oklch(0.488 0.243 264.376)", sidebar_primary_foreground: "oklch(0.985 0 0)",
275
+ sidebar_accent: "oklch(0.272 0.015 230)", sidebar_accent_foreground: "oklch(0.985 0.002 230)",
276
+ sidebar_border: "oklch(1 0 0 / 10%)", sidebar_ring: "oklch(0.555 0.018 230)"
277
+ }
278
+ },
279
+ taupe: {
280
+ light: {
281
+ background: "oklch(1 0 0)", foreground: "oklch(0.15 0.008 75)",
282
+ card: "oklch(1 0 0)", card_foreground: "oklch(0.15 0.008 75)",
283
+ popover: "oklch(1 0 0)", popover_foreground: "oklch(0.15 0.008 75)",
284
+ primary: "oklch(0.218 0.012 75)", primary_foreground: "oklch(0.985 0.002 75)",
285
+ secondary: "oklch(0.968 0.003 75)", secondary_foreground: "oklch(0.218 0.012 75)",
286
+ muted: "oklch(0.968 0.003 75)", muted_foreground: "oklch(0.556 0.016 75)",
287
+ accent: "oklch(0.968 0.003 75)", accent_foreground: "oklch(0.218 0.012 75)",
288
+ destructive: "oklch(0.577 0.245 27.325)",
289
+ border: "oklch(0.923 0.005 75)", input: "oklch(0.923 0.005 75)", ring: "oklch(0.708 0.014 75)",
290
+ sidebar: "oklch(0.985 0.002 75)", sidebar_foreground: "oklch(0.15 0.008 75)",
291
+ sidebar_primary: "oklch(0.218 0.012 75)", sidebar_primary_foreground: "oklch(0.985 0.002 75)",
292
+ sidebar_accent: "oklch(0.968 0.003 75)", sidebar_accent_foreground: "oklch(0.218 0.012 75)",
293
+ sidebar_border: "oklch(0.923 0.005 75)", sidebar_ring: "oklch(0.708 0.014 75)"
294
+ },
295
+ dark: {
296
+ background: "oklch(0.15 0.008 75)", foreground: "oklch(0.985 0.002 75)",
297
+ card: "oklch(0.218 0.012 75)", card_foreground: "oklch(0.985 0.002 75)",
298
+ popover: "oklch(0.218 0.012 75)", popover_foreground: "oklch(0.985 0.002 75)",
299
+ primary: "oklch(0.923 0.005 75)", primary_foreground: "oklch(0.218 0.012 75)",
300
+ secondary: "oklch(0.274 0.012 75)", secondary_foreground: "oklch(0.985 0.002 75)",
301
+ muted: "oklch(0.274 0.012 75)", muted_foreground: "oklch(0.708 0.014 75)",
302
+ accent: "oklch(0.274 0.012 75)", accent_foreground: "oklch(0.985 0.002 75)",
303
+ destructive: "oklch(0.704 0.191 22.216)",
304
+ border: "oklch(1 0 0 / 10%)", input: "oklch(1 0 0 / 15%)", ring: "oklch(0.556 0.016 75)",
305
+ sidebar: "oklch(0.218 0.012 75)", sidebar_foreground: "oklch(0.985 0.002 75)",
306
+ sidebar_primary: "oklch(0.488 0.243 264.376)", sidebar_primary_foreground: "oklch(0.985 0 0)",
307
+ sidebar_accent: "oklch(0.274 0.012 75)", sidebar_accent_foreground: "oklch(0.985 0.002 75)",
308
+ sidebar_border: "oklch(1 0 0 / 10%)", sidebar_ring: "oklch(0.556 0.016 75)"
309
+ }
310
+ }
311
+ }.freeze
312
+ end
313
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Accordion
6
+ # Wired to shadcn--accordion Stimulus controller
7
+ # type: "single" (default) or "multiple"
8
+ # collapsible: true allows closing all items in single mode
9
+ class Accordion < Base
10
+ def initialize(type: "single", collapsible: false, **attrs)
11
+ @type = type
12
+ @collapsible = collapsible
13
+ @attrs = attrs
14
+ end
15
+
16
+ def view_template(&block)
17
+ div(**build_attrs, &block)
18
+ end
19
+
20
+ private
21
+
22
+ def build_attrs
23
+ classes = cn("w-full", @attrs.delete(:class))
24
+ @attrs.merge(
25
+ data_slot: "accordion",
26
+ data_controller: "shadcn--accordion",
27
+ data_shadcn__accordion_type_value: @type,
28
+ data_shadcn__accordion_collapsible_value: @collapsible,
29
+ class: classes
30
+ )
31
+ end
32
+ end
33
+
34
+ class AccordionItem < Base
35
+ def initialize(open: false, **attrs)
36
+ @open = open
37
+ @attrs = attrs
38
+ end
39
+
40
+ def view_template(&block)
41
+ div(**build_attrs, &block)
42
+ end
43
+
44
+ private
45
+
46
+ def build_attrs
47
+ classes = cn("border-b last:border-b-0", @attrs.delete(:class))
48
+ @attrs.merge(
49
+ data_slot: "accordion-item",
50
+ data_state: @open ? "open" : "closed",
51
+ data_shadcn__accordion_target: "item",
52
+ class: classes
53
+ )
54
+ end
55
+ end
56
+
57
+ class AccordionTrigger < Base
58
+ def initialize(**attrs)
59
+ @attrs = attrs
60
+ end
61
+
62
+ def view_template(&block)
63
+ h3(data_slot: "accordion-header", class: "flex") do
64
+ button(**build_attrs) do
65
+ yield if block_given?
66
+ svg(
67
+ xmlns: "http://www.w3.org/2000/svg",
68
+ width: "16", height: "16",
69
+ viewbox: "0 0 24 24",
70
+ fill: "none",
71
+ stroke: "currentColor",
72
+ stroke_width: "2",
73
+ stroke_linecap: "round",
74
+ stroke_linejoin: "round",
75
+ class: "size-4 shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180"
76
+ ) do |s|
77
+ s.path(d: "m6 9 6 6 6-6")
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def build_attrs
86
+ classes = cn(
87
+ "group flex flex-1 items-center justify-between gap-4 rounded-md py-4 text-left text-sm font-medium",
88
+ "transition-all outline-none",
89
+ "hover:underline",
90
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
91
+ "disabled:pointer-events-none disabled:opacity-50",
92
+ "[&[data-state=open]>svg]:rotate-180",
93
+ @attrs.delete(:class)
94
+ )
95
+ @attrs.merge(
96
+ data_slot: "accordion-trigger",
97
+ data_shadcn__accordion_target: "trigger",
98
+ data_action: "click->shadcn--accordion#toggle keydown->shadcn--accordion#keydown",
99
+ type: "button",
100
+ aria_expanded: "false",
101
+ class: classes
102
+ )
103
+ end
104
+ end
105
+
106
+ class AccordionContent < Base
107
+ def initialize(**attrs)
108
+ @attrs = attrs
109
+ end
110
+
111
+ def view_template(&block)
112
+ div(**build_attrs) do
113
+ div(class: "pb-4 pt-0", &block)
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def build_attrs
120
+ classes = cn(
121
+ "overflow-hidden text-sm",
122
+ "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
123
+ @attrs.delete(:class)
124
+ )
125
+ @attrs.merge(
126
+ data_slot: "accordion-content",
127
+ data_shadcn__accordion_target: "content",
128
+ role: "region",
129
+ hidden: true,
130
+ class: classes
131
+ )
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shadcn
4
+ module UI
5
+ # Port of shadcn/ui Alert
6
+ # Variants: default, destructive
7
+ class Alert < Base
8
+ VARIANTS = ClassVariants.build(
9
+ base: [
10
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm",
11
+ "has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3",
12
+ "[&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current"
13
+ ].join(" "),
14
+ variants: {
15
+ variant: {
16
+ default: "bg-card text-card-foreground",
17
+ destructive: "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90"
18
+ }
19
+ },
20
+ defaults: {
21
+ variant: :default
22
+ }
23
+ )
24
+
25
+ def initialize(variant: :default, **attrs)
26
+ @variant = variant
27
+ @attrs = attrs
28
+ end
29
+
30
+ def view_template(&block)
31
+ div(**build_attrs, &block)
32
+ end
33
+
34
+ private
35
+
36
+ def build_attrs
37
+ classes = cn(VARIANTS.render(variant: @variant), @attrs.delete(:class))
38
+ @attrs.merge(data_slot: "alert", role: "alert", class: classes)
39
+ end
40
+ end
41
+
42
+ class AlertTitle < Base
43
+ def initialize(**attrs)
44
+ @attrs = attrs
45
+ end
46
+
47
+ def view_template(&block)
48
+ div(**build_attrs, &block)
49
+ end
50
+
51
+ private
52
+
53
+ def build_attrs
54
+ classes = cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", @attrs.delete(:class))
55
+ @attrs.merge(data_slot: "alert-title", class: classes)
56
+ end
57
+ end
58
+
59
+ class AlertDescription < Base
60
+ def initialize(**attrs)
61
+ @attrs = attrs
62
+ end
63
+
64
+ def view_template(&block)
65
+ div(**build_attrs, &block)
66
+ end
67
+
68
+ private
69
+
70
+ def build_attrs
71
+ classes = cn(
72
+ "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
73
+ @attrs.delete(:class)
74
+ )
75
+ @attrs.merge(data_slot: "alert-description", class: classes)
76
+ end
77
+ end
78
+ end
79
+ end