neon_sakura 0.1.4

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 (251) hide show
  1. checksums.yaml +7 -0
  2. data/.ai-reviewer/README.md +182 -0
  3. data/.ai-reviewer/ai-reviewer.sh +56 -0
  4. data/.ai-reviewer/build-system-prompt.sh +136 -0
  5. data/.ai-reviewer/extract-claude-sections.sh +32 -0
  6. data/.ai-reviewer/test-ai-reviewer.sh +40 -0
  7. data/.ai-reviewer-config.yml +190 -0
  8. data/.github/dependabot.yml +12 -0
  9. data/.github/settings.yml +70 -0
  10. data/.github/workflows/ai-pr-review-on-comment.yml +384 -0
  11. data/.github/workflows/ai-pr-review.yml +328 -0
  12. data/.github/workflows/license-check.yml +78 -0
  13. data/.github/workflows/lint.yml +79 -0
  14. data/.github/workflows/security.yml +131 -0
  15. data/.github/workflows/semgrep.yml +26 -0
  16. data/.github/workflows/test.yml +44 -0
  17. data/.gitignore +75 -0
  18. data/.rubocop.yml +33 -0
  19. data/.ruby-version +1 -0
  20. data/.simplecov +14 -0
  21. data/.stylelintignore +10 -0
  22. data/.stylelintrc.json +37 -0
  23. data/AGENTS.md +51 -0
  24. data/CHANGELOG.md +568 -0
  25. data/CLAUDE.md +632 -0
  26. data/Gemfile +8 -0
  27. data/Gemfile.lock +327 -0
  28. data/LICENSE +21 -0
  29. data/README.md +1209 -0
  30. data/Rakefile +25 -0
  31. data/app/assets/images/cherry_blossom.svg +1525 -0
  32. data/app/assets/images/cherry_blossom_tree.png +0 -0
  33. data/app/assets/images/prysm-icon.png +0 -0
  34. data/app/assets/stylesheets/base.css +29 -0
  35. data/app/assets/stylesheets/components.css +1652 -0
  36. data/app/assets/stylesheets/forms.css +152 -0
  37. data/app/assets/stylesheets/loading.css +145 -0
  38. data/app/assets/stylesheets/neon_sakura.css +40 -0
  39. data/app/assets/stylesheets/pagy-tailwind.css +120 -0
  40. data/app/assets/stylesheets/theme-default.css +40 -0
  41. data/app/assets/stylesheets/theme-green.css +84 -0
  42. data/app/assets/stylesheets/theme-purple.css +94 -0
  43. data/app/assets/stylesheets/theme-red.css +84 -0
  44. data/app/assets/stylesheets/utility-borders.css +29 -0
  45. data/app/assets/stylesheets/utility-colors.css +185 -0
  46. data/app/assets/stylesheets/utility-effects.css +123 -0
  47. data/app/assets/stylesheets/utility-gradients.css +158 -0
  48. data/app/assets/stylesheets/utility-layout.css +132 -0
  49. data/app/assets/stylesheets/utility-reset.css +13 -0
  50. data/app/assets/stylesheets/utility-responsive.css +145 -0
  51. data/app/assets/stylesheets/utility-sizing.css +99 -0
  52. data/app/assets/stylesheets/utility-spacing.css +174 -0
  53. data/app/assets/stylesheets/utility-typography.css +97 -0
  54. data/app/controllers/errors_controller.rb +120 -0
  55. data/app/controllers/style_guide_controller.rb +117 -0
  56. data/app/helpers/errors_helper.rb +12 -0
  57. data/app/helpers/neon_sakura/navbar_helper.rb +43 -0
  58. data/app/helpers/style_guide_helper.rb +36 -0
  59. data/app/javascript/neon_sakura/dropdown.js +22 -0
  60. data/app/javascript/neon_sakura/navbar.js +71 -0
  61. data/app/javascript/neon_sakura/theme_switcher.js +187 -0
  62. data/app/views/errors/show.html.erb +105 -0
  63. data/app/views/layouts/error.html.erb +19 -0
  64. data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
  65. data/app/views/layouts/mission_control/jobs/_navigation.html.erb +21 -0
  66. data/app/views/layouts/mission_control/jobs/application.html.erb +453 -0
  67. data/app/views/layouts/style_guide.html.erb +416 -0
  68. data/app/views/shared/_file_upload.html.erb +184 -0
  69. data/app/views/shared/_footer.html.erb +23 -0
  70. data/app/views/shared/_header.html.erb +42 -0
  71. data/app/views/shared/_navbar.html.erb +306 -0
  72. data/app/views/shared/_profile_image_selector.html.erb +165 -0
  73. data/app/views/shared/_theme_switcher.html.erb +64 -0
  74. data/app/views/shared/icons/_adjustments.html.erb +10 -0
  75. data/app/views/shared/icons/_alert_circle.html.erb +3 -0
  76. data/app/views/shared/icons/_alert_triangle.html.erb +3 -0
  77. data/app/views/shared/icons/_archive.html.erb +3 -0
  78. data/app/views/shared/icons/_arrow_down.html.erb +3 -0
  79. data/app/views/shared/icons/_arrow_left.html.erb +3 -0
  80. data/app/views/shared/icons/_arrow_up.html.erb +3 -0
  81. data/app/views/shared/icons/_arrows_pointing_in.html.erb +10 -0
  82. data/app/views/shared/icons/_arrows_pointing_out.html.erb +10 -0
  83. data/app/views/shared/icons/_artemis_logo.html.erb +26 -0
  84. data/app/views/shared/icons/_auth_banner.html.erb +1 -0
  85. data/app/views/shared/icons/_bars.html.erb +10 -0
  86. data/app/views/shared/icons/_bell.html.erb +3 -0
  87. data/app/views/shared/icons/_book.html.erb +3 -0
  88. data/app/views/shared/icons/_bookmark.html.erb +3 -0
  89. data/app/views/shared/icons/_box.html.erb +3 -0
  90. data/app/views/shared/icons/_brain.html.erb +3 -0
  91. data/app/views/shared/icons/_briefcase.html.erb +3 -0
  92. data/app/views/shared/icons/_calendar.html.erb +3 -0
  93. data/app/views/shared/icons/_camera.html.erb +4 -0
  94. data/app/views/shared/icons/_chart_bar.html.erb +3 -0
  95. data/app/views/shared/icons/_chart_line.html.erb +10 -0
  96. data/app/views/shared/icons/_chart_pie.html.erb +11 -0
  97. data/app/views/shared/icons/_chat.html.erb +3 -0
  98. data/app/views/shared/icons/_check.html.erb +3 -0
  99. data/app/views/shared/icons/_check_circle.html.erb +3 -0
  100. data/app/views/shared/icons/_cherry_blossom.html.erb +1516 -0
  101. data/app/views/shared/icons/_cherry_blossom_silhouette.html.erb +1016 -0
  102. data/app/views/shared/icons/_cherry_blossom_single_flower.html.erb +1125 -0
  103. data/app/views/shared/icons/_cherry_blossom_tree.html.erb +159 -0
  104. data/app/views/shared/icons/_chevron_down.html.erb +3 -0
  105. data/app/views/shared/icons/_chevron_right.html.erb +9 -0
  106. data/app/views/shared/icons/_clipboard.html.erb +3 -0
  107. data/app/views/shared/icons/_clock.html.erb +3 -0
  108. data/app/views/shared/icons/_close.html.erb +3 -0
  109. data/app/views/shared/icons/_cog.html.erb +4 -0
  110. data/app/views/shared/icons/_crop.html.erb +10 -0
  111. data/app/views/shared/icons/_crown.html.erb +3 -0
  112. data/app/views/shared/icons/_disc.html.erb +3 -0
  113. data/app/views/shared/icons/_download.html.erb +3 -0
  114. data/app/views/shared/icons/_dragonfly.html.erb +58 -0
  115. data/app/views/shared/icons/_duplicate.html.erb +4 -0
  116. data/app/views/shared/icons/_edit.html.erb +3 -0
  117. data/app/views/shared/icons/_envelope.html.erb +3 -0
  118. data/app/views/shared/icons/_eraser.html.erb +10 -0
  119. data/app/views/shared/icons/_external_link.html.erb +3 -0
  120. data/app/views/shared/icons/_eye.html.erb +4 -0
  121. data/app/views/shared/icons/_file_csv.html.erb +10 -0
  122. data/app/views/shared/icons/_file_export.html.erb +10 -0
  123. data/app/views/shared/icons/_file_image.html.erb +10 -0
  124. data/app/views/shared/icons/_file_import.html.erb +10 -0
  125. data/app/views/shared/icons/_file_question.html.erb +6 -0
  126. data/app/views/shared/icons/_film.html.erb +3 -0
  127. data/app/views/shared/icons/_filter.html.erb +3 -0
  128. data/app/views/shared/icons/_folder.html.erb +3 -0
  129. data/app/views/shared/icons/_folder_open.html.erb +3 -0
  130. data/app/views/shared/icons/_folder_plus.html.erb +3 -0
  131. data/app/views/shared/icons/_globe.html.erb +3 -0
  132. data/app/views/shared/icons/_google.html.erb +11 -0
  133. data/app/views/shared/icons/_heart.html.erb +3 -0
  134. data/app/views/shared/icons/_heart_broken.html.erb +11 -0
  135. data/app/views/shared/icons/_heart_pulse.html.erb +4 -0
  136. data/app/views/shared/icons/_history.html.erb +11 -0
  137. data/app/views/shared/icons/_home.html.erb +10 -0
  138. data/app/views/shared/icons/_image.html.erb +3 -0
  139. data/app/views/shared/icons/_inbox.html.erb +3 -0
  140. data/app/views/shared/icons/_info_circle.html.erb +10 -0
  141. data/app/views/shared/icons/_key.html.erb +3 -0
  142. data/app/views/shared/icons/_layers.html.erb +10 -0
  143. data/app/views/shared/icons/_lightbulb.html.erb +10 -0
  144. data/app/views/shared/icons/_lightning.html.erb +3 -0
  145. data/app/views/shared/icons/_list.html.erb +3 -0
  146. data/app/views/shared/icons/_lock.html.erb +3 -0
  147. data/app/views/shared/icons/_logout.html.erb +3 -0
  148. data/app/views/shared/icons/_magazine.html.erb +3 -0
  149. data/app/views/shared/icons/_magic.html.erb +3 -0
  150. data/app/views/shared/icons/_minus.html.erb +10 -0
  151. data/app/views/shared/icons/_mobile.html.erb +10 -0
  152. data/app/views/shared/icons/_moon.html.erb +3 -0
  153. data/app/views/shared/icons/_network.html.erb +10 -0
  154. data/app/views/shared/icons/_new_item_banner.html.erb +1 -0
  155. data/app/views/shared/icons/_ouroboros.html.erb +24 -0
  156. data/app/views/shared/icons/_package.html.erb +3 -0
  157. data/app/views/shared/icons/_palette.html.erb +3 -0
  158. data/app/views/shared/icons/_paper_plane.html.erb +10 -0
  159. data/app/views/shared/icons/_photo.html.erb +10 -0
  160. data/app/views/shared/icons/_play.html.erb +4 -0
  161. data/app/views/shared/icons/_plus.html.erb +3 -0
  162. data/app/views/shared/icons/_pocket.html.erb +11 -0
  163. data/app/views/shared/icons/_prysm-icon.html.erb +34 -0
  164. data/app/views/shared/icons/_prysm.html.erb +13 -0
  165. data/app/views/shared/icons/_pushbullet-1.html.erb +29 -0
  166. data/app/views/shared/icons/_pushbullet-2.html.erb +2 -0
  167. data/app/views/shared/icons/_puzzle.html.erb +10 -0
  168. data/app/views/shared/icons/_qrcode.html.erb +3 -0
  169. data/app/views/shared/icons/_question.html.erb +3 -0
  170. data/app/views/shared/icons/_receipt.html.erb +10 -0
  171. data/app/views/shared/icons/_redo.html.erb +3 -0
  172. data/app/views/shared/icons/_refresh.html.erb +3 -0
  173. data/app/views/shared/icons/_rocket.html.erb +10 -0
  174. data/app/views/shared/icons/_rss.html.erb +3 -0
  175. data/app/views/shared/icons/_save.html.erb +3 -0
  176. data/app/views/shared/icons/_search.html.erb +3 -0
  177. data/app/views/shared/icons/_search_minus.html.erb +10 -0
  178. data/app/views/shared/icons/_search_plus.html.erb +10 -0
  179. data/app/views/shared/icons/_server_error.html.erb +6 -0
  180. data/app/views/shared/icons/_share.html.erb +3 -0
  181. data/app/views/shared/icons/_shield_check.html.erb +3 -0
  182. data/app/views/shared/icons/_sign_in.html.erb +3 -0
  183. data/app/views/shared/icons/_spinner.html.erb +4 -0
  184. data/app/views/shared/icons/_star.html.erb +3 -0
  185. data/app/views/shared/icons/_store.html.erb +10 -0
  186. data/app/views/shared/icons/_sun.html.erb +3 -0
  187. data/app/views/shared/icons/_sync.html.erb +3 -0
  188. data/app/views/shared/icons/_table.html.erb +3 -0
  189. data/app/views/shared/icons/_tag.html.erb +3 -0
  190. data/app/views/shared/icons/_tags.html.erb +11 -0
  191. data/app/views/shared/icons/_tools.html.erb +4 -0
  192. data/app/views/shared/icons/_trash.html.erb +3 -0
  193. data/app/views/shared/icons/_undo.html.erb +3 -0
  194. data/app/views/shared/icons/_unlock.html.erb +3 -0
  195. data/app/views/shared/icons/_upload.html.erb +3 -0
  196. data/app/views/shared/icons/_user.html.erb +3 -0
  197. data/app/views/shared/icons/_user_circle.html.erb +10 -0
  198. data/app/views/shared/icons/_user_plus.html.erb +10 -0
  199. data/app/views/shared/icons/_video.html.erb +3 -0
  200. data/app/views/shared/icons/_wrench.html.erb +11 -0
  201. data/app/views/style_guide/index.html.erb +77 -0
  202. data/app/views/style_guide/sections/_alerts.html.erb +114 -0
  203. data/app/views/style_guide/sections/_badges.html.erb +78 -0
  204. data/app/views/style_guide/sections/_buttons.html.erb +130 -0
  205. data/app/views/style_guide/sections/_cards.html.erb +84 -0
  206. data/app/views/style_guide/sections/_colors.html.erb +106 -0
  207. data/app/views/style_guide/sections/_file_upload.html.erb +135 -0
  208. data/app/views/style_guide/sections/_forms.html.erb +129 -0
  209. data/app/views/style_guide/sections/_gradients.html.erb +253 -0
  210. data/app/views/style_guide/sections/_header.html.erb +12 -0
  211. data/app/views/style_guide/sections/_icons.html.erb +55 -0
  212. data/app/views/style_guide/sections/_images.html.erb +40 -0
  213. data/app/views/style_guide/sections/_loading.html.erb +242 -0
  214. data/app/views/style_guide/sections/_pagination.html.erb +212 -0
  215. data/app/views/style_guide/sections/_profile_components.html.erb +203 -0
  216. data/app/views/style_guide/sections/_theme_switcher.html.erb +72 -0
  217. data/app/views/style_guide/sections/_typography.html.erb +65 -0
  218. data/bin/ai-optimize-claude-md +540 -0
  219. data/bin/ai-review-local +345 -0
  220. data/bin/ai-security-review +585 -0
  221. data/bin/brakeman +9 -0
  222. data/bin/install-hooks +57 -0
  223. data/bin/rake +7 -0
  224. data/bin/rubocop +10 -0
  225. data/bin/verify_setup.rb +31 -0
  226. data/config/brakeman.ignore +28 -0
  227. data/config/initializers/neon_sakura.rb +15 -0
  228. data/config/license_overrides.yml +13 -0
  229. data/config/routes.rb +21 -0
  230. data/config/theme_mappings.yml +61 -0
  231. data/docs/PRYSM_ASSETS.md +210 -0
  232. data/docs/plans/extract_ai_reviewer_plan.md +151 -0
  233. data/docs/plans/neon_sakura_gem_plan.md +138 -0
  234. data/lib/neon_sakura/configuration.rb +94 -0
  235. data/lib/neon_sakura/engine.rb +48 -0
  236. data/lib/neon_sakura/icon_helper.rb +54 -0
  237. data/lib/neon_sakura/profile_helper.rb +24 -0
  238. data/lib/neon_sakura/stylesheet_helper.rb +40 -0
  239. data/lib/neon_sakura/theme_helper.rb +63 -0
  240. data/lib/neon_sakura/theme_importer.rb +112 -0
  241. data/lib/neon_sakura/version.rb +5 -0
  242. data/lib/neon_sakura.rb +13 -0
  243. data/neon_sakura.gemspec +50 -0
  244. data/package.json +18 -0
  245. data/scripts/git-hooks/post-merge +132 -0
  246. data/scripts/git-hooks/pre-commit +123 -0
  247. data/scripts/git-hooks/pre-push +127 -0
  248. data/scripts/license-check.rb +587 -0
  249. data/settings.local.json +12 -0
  250. data/yarn.lock +778 -0
  251. metadata +503 -0
data/README.md ADDED
@@ -0,0 +1,1209 @@
1
+ # Neon Sakura
2
+
3
+ <p align="center">
4
+ <img src="app/assets/images/cherry_blossom.svg" alt="Neon Sakura Logo" width="1024" />
5
+ </p>
6
+
7
+ A gem that extracts the styling and theming components from the Artemis Rails application for reuse across multiple projects.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'neon_sakura'
15
+ ```
16
+
17
+ And then execute:
18
+ ```bash
19
+ $ bundle install
20
+ ```
21
+
22
+ ## Requirements
23
+
24
+ This gem requires Propshaft for asset loading and management. Make sure you have Propshaft installed and configured in your Rails application.
25
+
26
+ ## Usage
27
+
28
+ ### Assets
29
+
30
+ The gem provides a set of CSS stylesheets and JavaScript components that can be used in any Rails application.
31
+
32
+ ### Stylesheets
33
+
34
+ The gem includes the following stylesheets:
35
+
36
+ - `base.css` - Core utility classes (spacing, colors, typography, responsive utilities)
37
+ - `components.css` - UI components (navigation, cards, badges, buttons, tables, etc.)
38
+ - `forms.css` - Form element styling
39
+ - `pagy-tailwind.css` - Pagination styling with Tailwind CSS
40
+
41
+ ### Layouts and Components
42
+
43
+ The gem also provides layout templates and view components:
44
+
45
+ - Layout templates for application and error pages
46
+ - SVG icon components for common UI elements
47
+ - Shared partials for common patterns in Artemis applications
48
+ - Header and footer components for consistent page structure
49
+
50
+ ### Integration
51
+
52
+ To integrate with your Rails application:
53
+
54
+ 1. Add the gem to your Gemfile
55
+ 2. Run `bundle install`
56
+ 3. Include the gem's assets in your application layout
57
+ 4. Use gem-provided layouts and components in your views
58
+
59
+ ### CSS Assets
60
+
61
+ The gem provides styling that needs to be included in your application's layout. Add the following to your application's layout file (e.g., `app/views/layouts/application.html.erb`):
62
+
63
+ ```erb
64
+ <%# Load neon_sakura stylesheets (Propshaft-compatible) %>
65
+ <%= neon_sakura_stylesheets %>
66
+
67
+ <%# Load your app-specific styles %>
68
+ <%= stylesheet_link_tag 'application', 'data-turbo-track': 'reload' %>
69
+ ```
70
+
71
+ The `neon_sakura_stylesheets` helper loads all CSS files in the correct order for proper cascade. This is necessary because Propshaft (Rails 8) does not process `@import` statements in CSS files like Sprockets did.
72
+
73
+ **IMPORTANT**: Do NOT use `@import "neon_sakura.css"` in your application.css - this does not work with Propshaft. Always use the `neon_sakura_stylesheets` helper in your layout.
74
+
75
+ ### JavaScript Assets
76
+
77
+ The gem includes JavaScript functionality for interactive components. To include the gem's JavaScript in your application, add the following to your application's layout file:
78
+
79
+ ```erb
80
+ <%= javascript_importmap_tags "neon_sakura", "data-turbo-track": "reload" %>
81
+ ```
82
+
83
+ Or if using the asset pipeline directly, you can include:
84
+
85
+ ```erb
86
+ <%= javascript_include_tag "neon_sakura", "data-turbo-track": "reload" %>
87
+ ```
88
+
89
+ The dropdown functionality and other interactive components will be available after including this JavaScript.
90
+
91
+ ### Pagination with Pagy
92
+
93
+ The gem includes styling for Pagy pagination with the `pagy-tailwind.css` stylesheet. For best results, use the `pagy_input_nav_js` helper which combines navigation and info in minimum space.
94
+
95
+ #### Setup
96
+
97
+ 1. Add pagy to your Gemfile:
98
+ ```ruby
99
+ gem 'pagy'
100
+ ```
101
+
102
+ 2. Enable the input_nav_js extra in `config/initializers/pagy.rb`:
103
+ ```ruby
104
+ require 'pagy/extras/input_nav_js'
105
+ ```
106
+
107
+ 3. Add pagy to your importmap in `config/importmap.rb`:
108
+ ```ruby
109
+ pin "pagy", to: "https://cdn.jsdelivr.net/npm/pagy@9.3.3/javascripts/pagy.min.js"
110
+ ```
111
+
112
+ 4. Import pagy in `app/javascript/application.js`:
113
+ ```javascript
114
+ import "pagy"
115
+ ```
116
+
117
+ #### Usage
118
+
119
+ In your controller:
120
+ ```ruby
121
+ @pagy, @records = pagy(YourModel.all)
122
+ ```
123
+
124
+ In your view:
125
+ ```erb
126
+ <%== pagy_input_nav_js(@pagy) %>
127
+ ```
128
+
129
+ The `pagy_input_nav_js` helper provides:
130
+ - Direct page number input for quick navigation
131
+ - Responsive navigation buttons
132
+ - Compact display combining navigation and info
133
+ - Fastest Pagy navigation option
134
+
135
+ #### Custom Search Pagination
136
+
137
+ For search results with custom parameters, you can still use `pagy_input_nav_js` by passing the pagy object. The gem's styling will automatically apply.
138
+
139
+ ### Style Guide (Development Only)
140
+
141
+ The gem includes a comprehensive style guide that showcases all UI components, themes, icons, and provides code examples. **This feature is only available in development and test environments.**
142
+
143
+ #### Accessing the Style Guide
144
+
145
+ In your Rails application (development environment), navigate to:
146
+
147
+ ```
148
+ http://localhost:3000/style-guide
149
+ ```
150
+
151
+ The style guide is automatically available and includes a navbar link (visible only in dev/test).
152
+
153
+ #### What's Included
154
+
155
+ - **Live Theme Previews**: Switch between all 6 themes (Green/Purple/Red × Light/Dark) in real-time
156
+ - **Icon Library**: All 94 SVG icons (including ouroboros) with clickable zoom modal and helper usage examples
157
+ - **Image Assets**: All gem images with clickable zoom modal and usage examples
158
+ - **Component Examples**: Each component shown with rendered preview AND code examples:
159
+ - Typography (headings, text colors, gradients)
160
+ - Colors (CSS custom properties reference)
161
+ - Gradients (primary, navbar, secondary blue, and secondary purple gradients with visual comparisons)
162
+ - Buttons (submit, primary, icon, link)
163
+ - Forms (inputs, textareas, selects, checkboxes, radios)
164
+ - Cards (basic, with headers, grids)
165
+ - Badges (status, outline, counter, tags)
166
+ - Alerts (success, error, warning, info)
167
+ - Loading Indicators (spinners, progress bars, skeleton loaders)
168
+ - Pagination (Pagy integration with working examples)
169
+
170
+ #### Configuration
171
+
172
+ The style guide is **enabled by default** in development and test environments. To disable it:
173
+
174
+ ```ruby
175
+ # config/initializers/neon_sakura.rb
176
+ NeonSakura.configure do |config|
177
+ config.enable_style_guide = false
178
+ end
179
+ ```
180
+
181
+ To control the navbar link position:
182
+
183
+ ```ruby
184
+ NeonSakura.configure do |config|
185
+ # Options: :start, :end, :before_logout, or integer position
186
+ config.style_guide_navbar_position = :start # Shows at beginning of navbar
187
+ end
188
+ ```
189
+
190
+ To control Pagy pagination examples:
191
+
192
+ ```ruby
193
+ NeonSakura.configure do |config|
194
+ # Default: true (auto-detect and show Pagy examples if available)
195
+ # Set to false to hide pagination section entirely
196
+ config.style_guide_pagy_examples = false
197
+ end
198
+ ```
199
+
200
+ **Pagy Integration Behavior:**
201
+ - When `true` (default): Automatically detects if Pagy is installed and shows working pagination examples
202
+ - If Pagy is not installed or fails to initialize: Shows helpful error message with configuration instructions
203
+ - When `false`: Hides the entire pagination section from the style guide
204
+ - **Note**: Pagy is not a required dependency - the style guide will work without it
205
+
206
+ #### Environment Restriction
207
+
208
+ The style guide is automatically restricted to development and test environments through multiple layers:
209
+
210
+ 1. **Route**: Only defined in development/test (returns Rails 404 in production)
211
+ 2. **Controller**: Returns 404 if somehow accessed in production
212
+ 3. **Navbar**: Link only visible in development/test via condition check
213
+
214
+ This ensures the style guide **never appears in production**, even if accidentally enabled.
215
+
216
+ ### Icon System
217
+
218
+ The gem includes 94+ SVG icon components based on [Heroicons](https://heroicons.com/) (MIT License), plus custom icons like ouroboros. All icons are commercially safe and require no attribution.
219
+
220
+ #### Usage
221
+
222
+ Icons are available as ERB partials in `app/views/shared/icons/`:
223
+
224
+ ```erb
225
+ <%= render "shared/icons/check", css_class: "w-4 h-4" %>
226
+ <%= render "shared/icons/heart", css_class: "w-6 h-6 text-red-500" %>
227
+ ```
228
+
229
+ #### Icon Helpers
230
+
231
+ Neon Sakura provides two helper modules for working with icons:
232
+
233
+ **NeonSakura::IconHelper** - General-purpose icon rendering:
234
+ ```erb
235
+ <%= render_icon("check", css_class: "w-4 h-4") %>
236
+ <%= render_icon("heart", css_class: "w-6 h-6", aria_label: "Favorite") %>
237
+ <%= render_icon("star", css_class: "w-5 h-5", aria_hidden: true) %>
238
+ ```
239
+
240
+ **NeonSakura::ThemeHelper** - Theme-aware icon rendering (includes IconHelper functionality):
241
+ ```erb
242
+ <%= render_theme_icon("palette", css_class: "w-4 h-4") %>
243
+ <%= render_theme_icon("moon", css_class: "w-4 h-4") %>
244
+ <%= render_theme_icon("chevron_down", css_class: "w-4 h-4") %>
245
+ ```
246
+
247
+ **Helper Methods:**
248
+
249
+ - `render_icon(name, options = {})` - Renders an icon partial with options
250
+ - `css_class` - CSS classes (default: "w-4 h-4")
251
+ - `aria_hidden` - Whether icon is aria-hidden
252
+ - `aria_label` - Accessibility label
253
+
254
+ - `render_theme_icon(name, options = {})` - Same as render_icon, used in ThemeHelper
255
+
256
+ - `available_icons` - Returns array of all available icon names
257
+
258
+ - `icon_exists?(name)` - Check if an icon exists
259
+
260
+ - `render_icon_list(icon_names, options = {})` - Renders multiple icons in a container
261
+ - `css_class` - Container CSS class (default: "flex items-center gap-2")
262
+ - `icon_css_class` - Individual icon CSS class
263
+
264
+ **Examples:**
265
+
266
+ ```erb
267
+ <%# Single icon with custom styling %>
268
+ <%= render_icon("check", css_class: "w-6 h-6 text-green-500") %>
269
+
270
+ <%# Icon with accessibility label %>
271
+ <%= render_icon("heart", css_class: "w-5 h-5", aria_label: "Add to favorites") %>
272
+
273
+ <%# Multiple icons in a list %>
274
+ <%= render_icon_list(["check", "heart", "star"], icon_css_class: "w-4 h-4") %>
275
+
276
+ <%# Check if icon exists before rendering %>
277
+ <% if icon_exists?("custom_icon") %>
278
+ <%= render_icon("custom_icon") %>
279
+ <% end %>
280
+
281
+ <%# Get all available icons %>
282
+ <% available_icons.each do |icon_name| %>
283
+ <%= render_icon(icon_name, css_class: "w-4 h-4") %>
284
+ <% end %>
285
+ ```
286
+
287
+ #### Available Icons
288
+
289
+ The icon library includes 94+ icons across categories:
290
+ - **Navigation & UI**: arrow_left, arrow_up, arrow_down, chevron_down, close, check, plus, etc.
291
+ - **Content & Actions**: edit, save, trash, bookmark, download, upload, sync, etc.
292
+ - **Tools & Features**: magic, qrcode, receipt, camera, search, filter, etc.
293
+ - **User & Auth**: user, lock, unlock, key, shield_check, sign_in, logout, etc.
294
+ - **Communication**: envelope, bell, share, chat, etc.
295
+ - **Data & Analytics**: chart_bar, calendar, clock, table, etc.
296
+ - **Files & Folders**: folder, folder_open, folder_plus, archive, etc.
297
+ - **Status & Alerts**: spinner, alert_circle, alert_triangle, check_circle, etc.
298
+ - **Custom**: ouroboros (serpent eating its own tail), cherry blossom variants, etc.
299
+
300
+ #### Icon Styling
301
+
302
+ Icons use `currentColor` by default and inherit the parent's text color:
303
+
304
+ ```erb
305
+ <div class="text-cyan-400">
306
+ <%= render "shared/icons/heart" %> <!-- Will be cyan-400 -->
307
+ </div>
308
+ ```
309
+
310
+ #### Size Classes
311
+
312
+ Use Tailwind-style sizing classes:
313
+ - `w-3 h-3` - 12px (extra small)
314
+ - `w-4 h-4` - 16px (small, default)
315
+ - `w-5 h-5` - 20px (medium)
316
+ - `w-6 h-6` - 24px (large)
317
+ - `w-8 h-8` - 32px (extra large)
318
+
319
+ #### License
320
+
321
+ All icons are based on Heroicons and distributed under the MIT License. Free for commercial use with no attribution required.
322
+
323
+ ### Loading Indicators
324
+
325
+ The gem provides a comprehensive set of loading indicators for async operations, all theme-aware using CSS custom properties.
326
+
327
+ #### Spinners
328
+
329
+ CSS classes for spinning loaders:
330
+
331
+ ```erb
332
+ <!-- Basic spinner (border color) -->
333
+ <div class="spinner-basic"></div>
334
+
335
+ <!-- Accent color spinner -->
336
+ <div class="spinner-accent"></div>
337
+
338
+ <!-- Small spinner (16px) -->
339
+ <div class="spinner-small"></div>
340
+
341
+ <!-- Large spinner (48px) -->
342
+ <div class="spinner-large"></div>
343
+ ```
344
+
345
+ All spinners use smooth rotation animations and adapt to the current theme.
346
+
347
+ #### Progress Bars
348
+
349
+ Determinate and indeterminate progress indicators:
350
+
351
+ ```erb
352
+ <!-- Indeterminate progress (animated sliding bar) -->
353
+ <div class="progress-bar-container">
354
+ <div class="progress-bar-indeterminate"></div>
355
+ </div>
356
+
357
+ <!-- Determinate progress (set width via inline style) -->
358
+ <div class="progress-bar-container">
359
+ <div class="progress-bar-fill" style="width: 50%"></div>
360
+ </div>
361
+ ```
362
+
363
+ Progress bars use theme gradients and smooth animations.
364
+
365
+ #### Skeleton Loaders
366
+
367
+ Placeholder content with shimmer animation:
368
+
369
+ ```erb
370
+ <!-- Text placeholder -->
371
+ <div class="skeleton-text"></div>
372
+ <div class="skeleton-text" style="width: 80%"></div>
373
+
374
+ <!-- Avatar/image placeholder -->
375
+ <div class="skeleton-avatar"></div>
376
+ ```
377
+
378
+ #### Loading States
379
+
380
+ Common loading state patterns:
381
+
382
+ ```erb
383
+ <!-- Button loading state -->
384
+ <button class="px-4 py-2 bg-accent text-white rounded" disabled>
385
+ <div class="spinner-small"></div>
386
+ <span>Loading...</span>
387
+ </button>
388
+
389
+ <!-- Card loading state -->
390
+ <div class="border rounded-lg p-6">
391
+ <div class="flex items-center justify-center">
392
+ <div class="spinner-accent"></div>
393
+ </div>
394
+ <p class="text-center mt-4">Loading content...</p>
395
+ </div>
396
+
397
+ <!-- Full-screen loading overlay -->
398
+ <div class="loading-overlay">
399
+ <div class="spinner-accent"></div>
400
+ </div>
401
+ ```
402
+
403
+ #### Available CSS Classes
404
+
405
+ - `.spinner-basic` - Border color spinner (32px)
406
+ - `.spinner-accent` - Accent color spinner (32px)
407
+ - `.spinner-small` - Small spinner (16px)
408
+ - `.spinner-large` - Large spinner (48px)
409
+ - `.progress-bar-container` - Progress bar wrapper
410
+ - `.progress-bar-fill` - Progress bar fill (set width via style)
411
+ - `.progress-bar-indeterminate` - Animated sliding progress
412
+ - `.skeleton-text` - Text placeholder with shimmer
413
+ - `.skeleton-avatar` - Circular avatar placeholder (48px)
414
+ - `.loading-overlay` - Full-screen loading overlay
415
+
416
+ All loading indicators are included in `loading.css` and automatically loaded via `neon_sakura_stylesheets`.
417
+
418
+ ### Navigation Bar Component
419
+
420
+ The gem provides a flexible, configurable navigation bar component that supports multiple positioning modes, dropdown menus, and custom content injection.
421
+
422
+ #### Basic Usage
423
+
424
+ ```erb
425
+ <!-- In your view (under page heading) -->
426
+ <%= render "shared/navbar" %>
427
+
428
+ <!-- In your layout (at top of page) -->
429
+ <%= render "shared/navbar" %>
430
+
431
+ <!-- With custom content (e.g., search form, quick import) -->
432
+ <%= render "shared/navbar" do %>
433
+ <form class="inline-flex items-center">
434
+ <input type="text" placeholder="Search..." class="px-2 py-1">
435
+ <button type="submit" class="btn-primary">Go</button>
436
+ </form>
437
+ <% end %>
438
+ ```
439
+
440
+ #### Configuration
441
+
442
+ Configure the navbar in `config/initializers/neon_sakura.rb`:
443
+
444
+ ```ruby
445
+ NeonSakura.configure do |config|
446
+ # Positioning: :none (hidden), :top (in layout), :under_heading (in views)
447
+ config.nav_position = :under_heading
448
+
449
+ # Optional: Add application icon
450
+ config.app_icon = "search" # Icon partial name
451
+
452
+ # Optional: Show search input (future feature)
453
+ config.show_search = false
454
+
455
+ # Navigation links
456
+ config.nav_links = [
457
+ # Simple link
458
+ {
459
+ type: "link",
460
+ name: "Home",
461
+ path: "/",
462
+ icon: "home", # Optional
463
+ active_paths: ["/", "/dashboard"], # Optional
464
+ condition: "user_signed_in?" # Optional
465
+ },
466
+
467
+ # Dropdown menu
468
+ {
469
+ type: "dropdown",
470
+ name: "Tools",
471
+ icon: "tools",
472
+ items: [
473
+ { name: "Jobs", path: "/jobs", icon: "briefcase" },
474
+ { name: "Health", path: "/health", icon: "heart_pulse" },
475
+ {
476
+ name: "Metrics",
477
+ path: "/metrics",
478
+ icon: "chart_bar",
479
+ condition: "current_user&.admin?"
480
+ }
481
+ ]
482
+ },
483
+
484
+ # Form button (for logout, etc.)
485
+ {
486
+ type: "button",
487
+ name: "Logout",
488
+ path: "/users/sign_out",
489
+ icon: "logout",
490
+ method: :delete,
491
+ condition: "user_signed_in?"
492
+ }
493
+ ]
494
+ end
495
+ ```
496
+
497
+ #### Navigation Positioning Modes
498
+
499
+ **`:under_heading` (Default)**
500
+ - Renders navbar in views below page headings
501
+ - Used by: Artemis (below page titles)
502
+ - Add `<%= render "shared/navbar" %>` in individual views
503
+
504
+ **`:top`**
505
+ - Renders navbar at top of layout (sticky navigation)
506
+ - Used by: prys-m, Mission Control
507
+ - Add to layout file (e.g., `app/views/layouts/application.html.erb`)
508
+
509
+ **`:none`**
510
+ - Navigation hidden (must be manually rendered)
511
+ - Use when you want complete control over navbar placement
512
+
513
+ #### Link Configuration Schema
514
+
515
+ **Simple Link**
516
+ ```ruby
517
+ {
518
+ type: "link",
519
+ name: "Link Name",
520
+ path: "/path",
521
+ icon: "icon_name", # Optional: icon partial name
522
+ active_paths: ["/alt"], # Optional: additional active paths
523
+ condition: "ruby_code", # Optional: eval for conditional rendering
524
+ target: "_blank", # Optional: link target
525
+ aria_label: "Custom label" # Optional: accessibility label
526
+ }
527
+ ```
528
+
529
+ **Dropdown Menu**
530
+ ```ruby
531
+ {
532
+ type: "dropdown",
533
+ name: "Menu Name",
534
+ icon: "icon_name", # Optional
535
+ aria_label: "Menu label", # Optional
536
+ items: [
537
+ { name: "Item 1", path: "/path1", icon: "icon1" },
538
+ { name: "Item 2", path: "/path2", icon: "icon2", target: "_blank" },
539
+ { name: "Item 3", path: "/path3", condition: "user_signed_in?" }
540
+ ]
541
+ }
542
+ ```
543
+
544
+ **Form Button**
545
+ ```ruby
546
+ {
547
+ type: "button",
548
+ name: "Button Name",
549
+ path: "/path",
550
+ icon: "icon_name", # Optional
551
+ method: :delete, # :get, :post, :patch, :delete
552
+ condition: "ruby_code", # Optional
553
+ aria_label: "Button label" # Optional
554
+ }
555
+ ```
556
+
557
+ #### Active State Detection
558
+
559
+ The navbar automatically highlights the current page link:
560
+ - Matches `request.path` against link `path`
561
+ - Checks `active_paths` array for additional matches
562
+ - Uses `current_page?(path)` helper when available
563
+ - Applies `nav-active` CSS class (cyan bottom border)
564
+
565
+ #### Conditional Links
566
+
567
+ Use the `condition` parameter to conditionally render links based on runtime evaluation:
568
+
569
+ ```ruby
570
+ {
571
+ type: "link",
572
+ name: "Admin Panel",
573
+ path: "/admin",
574
+ condition: "current_user&.admin?" # Only shown to admins
575
+ }
576
+
577
+ {
578
+ type: "link",
579
+ name: "Profile",
580
+ path: "/profile",
581
+ condition: "defined?(user_signed_in?) && user_signed_in?" # Only when authenticated
582
+ }
583
+
584
+ {
585
+ type: "link",
586
+ name: "Settings",
587
+ path: "/settings",
588
+ condition: "defined?(settings_path)" # Only if route exists
589
+ }
590
+ ```
591
+
592
+ The condition is evaluated as Ruby code in the view context, allowing access to helpers and instance variables.
593
+
594
+ #### Custom Content Block
595
+
596
+ Pass a block to `render "shared/navbar"` to inject custom HTML between navigation links:
597
+
598
+ ```erb
599
+ <%= render "shared/navbar" do %>
600
+ <!-- Quick Import Form (prys-m example) -->
601
+ <form class="inline-flex items-center gap-2" onsubmit="handleQuickImport(event)">
602
+ <input type="url"
603
+ class="px-3 py-1 bg-gray-800 border border-gray-700 rounded text-white text-sm"
604
+ placeholder="Quick Import URL"
605
+ required>
606
+ <button type="submit" class="btn-primary btn-sm">
607
+ <%= render "shared/icons/plus", css_class: "w-4 h-4" %> Import
608
+ </button>
609
+ </form>
610
+ <% end %>
611
+ ```
612
+
613
+ The custom content appears as part of the navbar with proper styling and responsive behavior.
614
+
615
+ #### Accessibility
616
+
617
+ All navigation elements include proper ARIA attributes:
618
+ - `role="navigation"` on `<nav>` element
619
+ - `aria-label` on all links and buttons (auto-generated or custom)
620
+ - `aria-haspopup` and `aria-expanded` on dropdown toggles
621
+ - `aria-current="page"` on active links
622
+ - Keyboard support (Escape key closes dropdowns)
623
+
624
+ #### Styling
625
+
626
+ Navigation uses the dark theme styles from `components.css`:
627
+ - Cyan links (`text-cyan-300`) with hover states (`hover:text-cyan-200`)
628
+ - Gray dropdown backgrounds (`bg-gray-800`) with borders (`border-gray-700`)
629
+ - Active state: white text, bold font, cyan bottom border (`.nav-active`)
630
+ - Smooth transitions on hover and focus
631
+ - Focus rings for keyboard navigation (`focus:ring-cyan-500`)
632
+ - Responsive design with flexbox wrapping
633
+ - Mobile-friendly touch targets
634
+
635
+ #### JavaScript
636
+
637
+ The navbar includes vanilla JavaScript for dropdown functionality (no dependencies):
638
+ - Auto-closes other dropdowns when opening one
639
+ - Click-outside detection to close menus
640
+ - Escape key support
641
+ - Turbo-compatible (works with Turbo page loads)
642
+ - No Stimulus required
643
+
644
+ Include the navbar JavaScript in your application:
645
+
646
+ ```erb
647
+ <%= javascript_include_tag "neon_sakura/navbar", "data-turbo-track": "reload" %>
648
+ ```
649
+
650
+ Or with importmap:
651
+
652
+ ```ruby
653
+ # config/importmap.rb
654
+ pin "neon_sakura/navbar", to: "neon_sakura/navbar.js"
655
+ ```
656
+
657
+ ## Architecture
658
+
659
+ This gem is structured as a Rails engine that provides:
660
+
661
+ 1. **Theming Components**: Dark-themed styling for consistent UI across Rails applications
662
+ 2. **Reusable UI Elements**: Components like navigation, cards, badges, buttons, and tables
663
+ 3. **Form Styling**: Consistent form element styling
664
+ 4. **Layout Templates**: Application and error page layouts for consistent page structure
665
+ 5. **Icon System**: 87+ Heroicons-based SVG icon components (MIT licensed) for common UI elements that can be reused across projects
666
+ 6. **Responsive Design**: Tailwind-inspired utility classes for responsive layouts
667
+
668
+ The gem is designed for code reusability, making it possible to maintain consistent UI themes and components across multiple Rails projects.
669
+
670
+ ## CSS Architecture
671
+
672
+ Neon Sakura uses a modular CSS architecture with separate files for themes and utilities.
673
+
674
+ ### File Organization
675
+
676
+ - **Themes** (`themes/*.css`): Color definitions for each theme (purple, green, red)
677
+ - **Utilities** (`utilities/*.css`): Reusable utility classes (layout, spacing, typography, etc.)
678
+ - **Components** (`components.css`): UI component styles (cards, navigation, badges, etc.)
679
+ - **Forms** (`forms.css`): Form element styling
680
+ - **Pagination** (`pagy-tailwind.css`): Pagination styling
681
+
682
+ ### Customizing Themes
683
+
684
+ Each theme file contains both light and dark variants. To customize:
685
+
686
+ 1. Edit the appropriate theme file in `app/assets/stylesheets/themes/`
687
+ 2. Update CSS custom properties (e.g., `--color-accent`, `--gradient-from`)
688
+ 3. Test in both light and dark modes
689
+
690
+ See [CLAUDE.md](CLAUDE.md#css-file-structure) for detailed contribution guidelines.
691
+
692
+ ## Configuration
693
+
694
+ The gem provides several configuration options that can be customized in your Rails application's initializer (`config/initializers/neon_sakura.rb`):
695
+
696
+ ```ruby
697
+ NeonSakura.configure do |config|
698
+ # Application name (used in header, footer, and error pages)
699
+ config.app_name = "My Application"
700
+
701
+ # Show version in header
702
+ config.show_version = true
703
+
704
+ # Show header component
705
+ config.show_header = true
706
+
707
+ # Enable automatic error page routes
708
+ config.enable_error_pages = true
709
+
710
+ # Navigation links for header
711
+ config.nav_links = [
712
+ { name: "Home", path: "/" },
713
+ { name: "About", path: "/about" }
714
+ ]
715
+ end
716
+ ```
717
+
718
+ ### Configuration Options
719
+
720
+ - `app_name` (String, default: `"My Application"`) - Application name displayed in header, footer, and error pages
721
+ - `show_version` (Boolean, default: `true`) - Show version information in the header
722
+ - `show_header` (Boolean, default: `true`) - Enable the header component
723
+ - `enable_error_pages` (Boolean, default: `true`) - Automatically mount error page routes
724
+ - `nav_links` (Array, default: `[{ name: "Home", path: "/" }, { name: "About", path: "/about" }]`) - Navigation links (see Navigation Bar Component section for full schema)
725
+ - `nav_position` (Symbol, default: `:under_heading`) - Navigation positioning (:none, :top, :under_heading)
726
+ - `app_icon` (String, default: `nil`) - Icon partial name for app branding
727
+ - `show_search` (Boolean, default: `false`) - Show search input in navbar (future feature)
728
+ - `default_theme` (Hash, default: `{ name: "purple", mode: "dark" }`) - Default theme to use
729
+ - `available_themes` (Array, default: `[{ name: "purple", mode: "dark", label: "Purple Dark" }]`) - Available themes for switching
730
+ - `enable_theme_persistence` (Boolean, default: `true`) - Enable localStorage theme persistence
731
+ - `theme_api_endpoint` (String, default: `nil`) - Optional API endpoint for backend theme persistence
732
+
733
+ ## Theme System
734
+
735
+ Neon Sakura includes a comprehensive multi-theme system with 6 built-in themes that support both light and dark modes.
736
+
737
+ ### Built-in Themes
738
+
739
+ The gem provides six professionally designed themes:
740
+
741
+ | Theme | Name | Mode | Primary Color | Gradient | Best For |
742
+ |-------|------|------|---------------|----------|----------|
743
+ | **Purple Dark** | `purple` | `dark` | #a855f7 (Purple) | Blue → Purple | Default theme, developer tools, admin panels |
744
+ | **Purple Light** | `purple` | `light` | #a855f7 (Purple) | Purple → Purple | Light mode alternative, business applications |
745
+ | **Green Dark** | `green` | `dark` | #10b981 (Emerald) | Green → Cyan | Content-focused apps, reading interfaces |
746
+ | **Green Light** | `green` | `light` | #009990 (Teal) | Green → Cyan | Clean, professional applications |
747
+ | **Red Dark** | `red` | `dark` | #ef4444 (Red) | Red → Orange | Bold, energetic applications |
748
+ | **Red Light** | `red` | `light` | #dc2626 (Red) | Red → Orange | Dynamic, attention-grabbing interfaces |
749
+
750
+ ### Theme Configuration
751
+
752
+ Configure themes in `config/initializers/neon_sakura.rb`:
753
+
754
+ ```ruby
755
+ NeonSakura.configure do |config|
756
+ # Set default theme
757
+ config.default_theme = { name: "purple", mode: "dark" }
758
+
759
+ # Configure available themes for switching
760
+ config.available_themes = [
761
+ { name: "green", mode: "light", label: "Green Light" },
762
+ { name: "green", mode: "dark", label: "Green Dark" },
763
+ { name: "purple", mode: "light", label: "Purple Light" },
764
+ { name: "purple", mode: "dark", label: "Purple Dark" },
765
+ { name: "red", mode: "light", label: "Red Light" },
766
+ { name: "red", mode: "dark", label: "Red Dark" }
767
+ ]
768
+
769
+ # Enable localStorage persistence (default: true)
770
+ config.enable_theme_persistence = true
771
+
772
+ # Optional: API endpoint for backend persistence
773
+ # config.theme_api_endpoint = "/settings/theme"
774
+ end
775
+ ```
776
+
777
+ ### Theme Color Palettes
778
+
779
+ Each theme defines a complete color system using CSS custom properties:
780
+
781
+ **Purple Dark** (Default):
782
+ ```css
783
+ --color-text-primary: #ffffff
784
+ --color-background: #111827
785
+ --color-surface: #1f2937
786
+ --color-accent: #a855f7
787
+ --color-border: #374151
788
+ ```
789
+
790
+ **Purple Light**:
791
+ ```css
792
+ --color-text-primary: #374151
793
+ --color-background: #FFFFFF
794
+ --color-surface: #f9fafb
795
+ --color-accent: #a855f7
796
+ --color-border: #e5e7eb
797
+ ```
798
+
799
+ **Green Dark**:
800
+ ```css
801
+ --color-text-primary: #e2e8f0
802
+ --color-background: #0f172a
803
+ --color-surface: #1e293b
804
+ --color-accent: #10b981
805
+ --color-border: #334155
806
+ ```
807
+
808
+ **Green Light**:
809
+ ```css
810
+ --color-text-primary: #545E75
811
+ --color-background: #FFFFFF
812
+ --color-surface: #f8f9fa
813
+ --color-accent: #009990
814
+ --color-border: #dee2e6
815
+ ```
816
+
817
+ **Red Dark**:
818
+ ```css
819
+ --color-text-primary: #ffffff
820
+ --color-background: #111827
821
+ --color-surface: #1f2937
822
+ --color-accent: #ef4444
823
+ --color-border: #374151
824
+ ```
825
+
826
+ **Red Light**:
827
+ ```css
828
+ --color-text-primary: #374151
829
+ --color-background: #FFFFFF
830
+ --color-surface: #f9fafb
831
+ --color-accent: #dc2626
832
+ --color-border: #e5e7eb
833
+ ```
834
+
835
+ ### Using Themes
836
+
837
+ Themes are applied via `data-theme-name` and `data-theme-mode` attributes on the `<html>` element:
838
+
839
+ ```html
840
+ <!-- Purple Dark (default) -->
841
+ <html data-theme-name="purple" data-theme-mode="dark">
842
+
843
+ <!-- Green Light -->
844
+ <html data-theme-name="green" data-theme-mode="light">
845
+ ```
846
+
847
+ The theme switcher JavaScript automatically manages these attributes based on user selection and localStorage persistence.
848
+
849
+ ### Theme Switcher Component
850
+
851
+ Add the theme switcher to your navbar dropdown:
852
+
853
+ ```ruby
854
+ config.nav_links = [
855
+ {
856
+ type: "dropdown",
857
+ name: "Settings",
858
+ icon: "cog",
859
+ items: [
860
+ { name: "Profile", path: "/profile", icon: "user" },
861
+ { type: "divider" },
862
+ { type: "theme_selector" } # Renders theme list with checkmarks
863
+ ]
864
+ }
865
+ ]
866
+ ```
867
+
868
+ The theme selector automatically:
869
+ - Displays all available themes with icons (sun/moon)
870
+ - Shows checkmark next to current theme
871
+ - Persists selection to localStorage
872
+ - Updates all UI components dynamically
873
+
874
+ ### Theme Persistence
875
+
876
+ **localStorage (Default)**:
877
+ Theme selection is automatically saved to localStorage and restored on page load.
878
+
879
+ **Backend Persistence (Optional)**:
880
+ Configure an API endpoint to save theme preferences to your database:
881
+
882
+ ```ruby
883
+ config.theme_api_endpoint = "/settings/theme"
884
+ ```
885
+
886
+ The theme switcher will send POST requests with:
887
+ ```json
888
+ {
889
+ "theme_name": "green",
890
+ "theme_mode": "dark"
891
+ }
892
+ ```
893
+
894
+ ### CSS Custom Properties
895
+
896
+ All colors use CSS custom properties (CSS variables) for dynamic theming:
897
+
898
+ ```css
899
+ /* Using theme colors in your CSS */
900
+ .my-component {
901
+ color: var(--color-text-primary);
902
+ background: var(--color-surface);
903
+ border: 1px solid var(--color-border);
904
+ }
905
+
906
+ .my-button {
907
+ background: linear-gradient(to right, var(--gradient-from), var(--gradient-to));
908
+ }
909
+
910
+ .my-button:hover {
911
+ background: linear-gradient(to right, var(--gradient-from-hover), var(--gradient-to-hover));
912
+ }
913
+ ```
914
+
915
+ Available CSS variables:
916
+
917
+ **Colors:**
918
+ - `--color-text-primary` - Primary text color
919
+ - `--color-text-secondary` - Secondary text color
920
+ - `--color-text-muted` - Muted/disabled text
921
+ - `--color-background` - Page background
922
+ - `--color-surface` - Card/panel background
923
+ - `--color-accent` - Primary accent color
924
+ - `--color-notification` - Success/notification color
925
+ - `--color-alert` - Error/alert color
926
+ - `--color-warning` - Warning color
927
+ - `--color-border` - Border color
928
+ - `--color-white` - Pure white
929
+
930
+ **Gradients:**
931
+ - `--gradient-from` / `--gradient-to` - Primary button gradients
932
+ - `--gradient-from-hover` / `--gradient-to-hover` - Primary button hover gradients
933
+ - `--text-gradient-from` / `--text-gradient-to` - Text gradients for headings
934
+ - `--gradient-navbar-from` / `--gradient-navbar-to` - Navbar gradient (accent to notification)
935
+ - `--gradient-secondary-blue-from` / `--gradient-secondary-blue-to` - Secondary blue gradient (cyan to blue, all themes)
936
+ - `--gradient-secondary-start` / `--gradient-secondary-mid` / `--gradient-secondary-end` - Alternative purple gradient (purple themes only)
937
+
938
+ ### Creating Custom Themes
939
+
940
+ You can define custom themes by extending the CSS:
941
+
942
+ ```css
943
+ /* custom_theme.css */
944
+ :root[data-theme-name="custom"][data-theme-mode="dark"] {
945
+ --color-text-primary: #your-color;
946
+ --color-background: #your-color;
947
+ --color-surface: #your-color;
948
+ --color-accent: #your-color;
949
+ --color-notification: #your-color;
950
+ --color-alert: #your-color;
951
+ --color-warning: #your-color;
952
+ --color-border: #your-color;
953
+ --gradient-from: #your-color;
954
+ --gradient-to: #your-color;
955
+ --gradient-from-hover: #your-color;
956
+ --gradient-to-hover: #your-color;
957
+ --text-gradient-from: #your-color;
958
+ --text-gradient-to: #your-color;
959
+ }
960
+ ```
961
+
962
+ Then configure it:
963
+
964
+ ```ruby
965
+ config.available_themes = [
966
+ { name: "custom", mode: "dark", label: "My Custom Theme" },
967
+ # ... other themes
968
+ ]
969
+ ```
970
+
971
+ ## Header Component Usage
972
+
973
+ The gem includes a header component that can be used in your layouts by rendering:
974
+
975
+ ```erb
976
+ <%= render 'shared/header' %>
977
+ ```
978
+
979
+ The header component accepts configuration parameters through Rails initializer settings:
980
+
981
+ ### Configuration
982
+
983
+ To customize the header component, add the following to your Rails application's initializer (`config/initializers/neon_sakura.rb`):
984
+
985
+ ```ruby
986
+ NeonSakura.configure do |config|
987
+ config.app_name = "My Application"
988
+ config.show_version = true
989
+ config.show_header = true
990
+ config.nav_links = [
991
+ { name: "Home", path: "/" },
992
+ { name: "About", path: "/about" }
993
+ ]
994
+ end
995
+ ```
996
+
997
+ To disable the header component completely, set `config.show_header = false`.
998
+
999
+ ## Footer Component Usage
1000
+
1001
+ The gem includes a footer component that can be used in your layouts by rendering:
1002
+
1003
+ ```erb
1004
+ <%= render 'shared/footer' %>
1005
+ ```
1006
+
1007
+ The footer component is dynamic and automatically detects the application name from the Rails configuration or defaults to "Shared Application".
1008
+
1009
+ ### Configuration
1010
+
1011
+ To customize the footer component, add the following to your Rails application's initializer (`config/initializers/neon_sakura.rb`):
1012
+
1013
+ ```ruby
1014
+ NeonSakura.configure do |config|
1015
+ config.app_name = "My Application"
1016
+ end
1017
+ ```
1018
+
1019
+ ## Error Handling
1020
+
1021
+ The gem includes an `ErrorsController` that provides consistent error pages for common HTTP error codes. The controller handles both HTML and JSON responses, making it suitable for both web and API applications.
1022
+
1023
+ ### Supported Error Codes
1024
+
1025
+ - `400` - Bad Request
1026
+ - `401` - Unauthorized
1027
+ - `403` - Forbidden
1028
+ - `404` - Not Found
1029
+ - `406` - Not Acceptable (browser compatibility issues)
1030
+ - `422` - Unprocessable Entity
1031
+ - `500` - Internal Server Error
1032
+ - `503` - Service Unavailable
1033
+
1034
+ ### Configuration
1035
+
1036
+ Error pages are enabled by default. To configure error page behavior, add the following to your Rails application's initializer (`config/initializers/neon_sakura.rb`):
1037
+
1038
+ ```ruby
1039
+ NeonSakura.configure do |config|
1040
+ config.enable_error_pages = true # Set to false to disable error routes
1041
+ end
1042
+ ```
1043
+
1044
+ ### Mounting the Engine (Required)
1045
+
1046
+ **IMPORTANT**: You must mount the NeonSakura engine in your application's routes to enable error pages and other features.
1047
+
1048
+ Add this to your `config/routes.rb` file (typically near the top):
1049
+
1050
+ ```ruby
1051
+ # config/routes.rb
1052
+ Rails.application.routes.draw do
1053
+ # Mount NeonSakura engine for error pages and other routes
1054
+ mount NeonSakura::Engine => "/", as: "neon_sakura"
1055
+
1056
+ # ... rest of your routes
1057
+ end
1058
+ ```
1059
+
1060
+ ### Available Error Routes
1061
+
1062
+ After mounting the engine, the following error routes are available when `enable_error_pages` is `true` (default):
1063
+
1064
+ - `/400` - Bad Request
1065
+ - `/401` - Unauthorized
1066
+ - `/403` - Forbidden
1067
+ - `/404` - Not Found
1068
+ - `/406` - Not Acceptable
1069
+ - `/422` - Unprocessable Entity
1070
+ - `/500` - Internal Server Error
1071
+ - `/503` - Service Unavailable
1072
+
1073
+ You can use them in your application by calling the route helpers:
1074
+
1075
+ ```ruby
1076
+ redirect_to neon_sakura.not_found_path
1077
+ redirect_to neon_sakura.internal_server_error_path
1078
+ redirect_to neon_sakura.not_acceptable_path
1079
+ ```
1080
+
1081
+ **Note**: If you prefer to define your own error routes, set `config.enable_error_pages = false` in your initializer and define custom routes:
1082
+
1083
+ ```ruby
1084
+ # In config/routes.rb
1085
+ get "/404", to: "errors#not_found", as: :not_found
1086
+ get "/500", to: "errors#internal_server_error", as: :internal_server_error
1087
+ # ... other error routes
1088
+ ```
1089
+
1090
+ ### Configuring Rails Error Pages
1091
+
1092
+ To use these error pages in production, configure your `config/environments/production.rb`:
1093
+
1094
+ ```ruby
1095
+ config.exceptions_app = routes
1096
+ ```
1097
+
1098
+ This will route Rails exceptions to your error controller actions.
1099
+
1100
+ ### Custom Error Messages
1101
+
1102
+ You can provide custom error messages and details by passing parameters:
1103
+
1104
+ ```ruby
1105
+ # In your controller
1106
+ redirect_to not_found_path(message: "The resource you're looking for doesn't exist")
1107
+
1108
+ # With additional details
1109
+ redirect_to unprocessable_entity_path(
1110
+ message: "Validation failed",
1111
+ details: { field1: "is required", field2: "is invalid" }
1112
+ )
1113
+ ```
1114
+
1115
+ ### API Support
1116
+
1117
+ The error controller automatically responds with JSON for API requests:
1118
+
1119
+ ```json
1120
+ {
1121
+ "success": false,
1122
+ "error": "The page you're looking for doesn't exist.",
1123
+ "details": {
1124
+ "field1": "is required",
1125
+ "field2": "is invalid"
1126
+ }
1127
+ }
1128
+ ```
1129
+
1130
+ ### Customization
1131
+
1132
+ To customize the error pages, you can override the views in your application by creating files in `app/views/errors/`:
1133
+
1134
+ - `app/views/errors/show.html.erb` - Main error page template
1135
+ - `app/views/layouts/error.html.erb` - Error page layout
1136
+
1137
+ The error views use the gem's icon system and styling components for a consistent dark-themed appearance.
1138
+
1139
+ ## Testing
1140
+
1141
+ To run the tests:
1142
+
1143
+ ```bash
1144
+ rake test
1145
+ ```
1146
+
1147
+ To run tests with coverage:
1148
+
1149
+ ```bash
1150
+ rake test:coverage
1151
+ ```
1152
+
1153
+ ## Development
1154
+
1155
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
1156
+
1157
+ To install this gem onto your local machine, run `bundle exec rake install`.
1158
+
1159
+ ### CSS Linting
1160
+
1161
+ The gem uses Stylelint with the industry-standard configuration for CSS code quality.
1162
+
1163
+ **Setup** (first time only):
1164
+ ```bash
1165
+ yarn install
1166
+ ```
1167
+
1168
+ **Run linting**:
1169
+ ```bash
1170
+ yarn lint:css # Check for CSS issues
1171
+ yarn lint:css:fix # Auto-fix CSS issues
1172
+ ```
1173
+
1174
+ **Git hooks**: Stylelint runs automatically on `git commit` to ensure code quality. The pre-commit hook includes:
1175
+ - RuboCop (Ruby linting)
1176
+ - Stylelint (CSS linting)
1177
+ - Brakeman (security scanning)
1178
+ - Bundle Audit (dependency security)
1179
+ - Secrets detection
1180
+
1181
+ To install git hooks:
1182
+ ```bash
1183
+ ./bin/install-hooks
1184
+ ```
1185
+
1186
+ ## Contributing
1187
+
1188
+ Bug reports and pull requests are welcome on GitHub at https://github.com/trex22/neon_sakura.
1189
+
1190
+ ## License
1191
+
1192
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
1193
+
1194
+ ### Icon Attribution
1195
+
1196
+ Icons are based on [Heroicons](https://heroicons.com/) by Tailwind Labs, also licensed under the MIT License. No attribution required for commercial use.
1197
+
1198
+ ## AI PR Reviewer
1199
+
1200
+ This gem includes an AI PR reviewer that helps ensure code quality and adherence to project guidelines. The reviewer uses a comprehensive system prompt to provide feedback that's specific to the neon_sakura gem project.
1201
+
1202
+ ### Review Configuration
1203
+
1204
+ The AI reviewer is configured using:
1205
+ - `CLAUDE.md` - Project documentation and guidelines
1206
+ - `build-system-prompt.sh` - System prompt for AI review
1207
+ - `extract-claude-sections.sh` - Script to extract relevant sections from documentation
1208
+
1209
+ The AI reviewer ensures code quality and security adherence by running checks before each PR merge.