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.
- checksums.yaml +7 -0
- data/.ai-reviewer/README.md +182 -0
- data/.ai-reviewer/ai-reviewer.sh +56 -0
- data/.ai-reviewer/build-system-prompt.sh +136 -0
- data/.ai-reviewer/extract-claude-sections.sh +32 -0
- data/.ai-reviewer/test-ai-reviewer.sh +40 -0
- data/.ai-reviewer-config.yml +190 -0
- data/.github/dependabot.yml +12 -0
- data/.github/settings.yml +70 -0
- data/.github/workflows/ai-pr-review-on-comment.yml +384 -0
- data/.github/workflows/ai-pr-review.yml +328 -0
- data/.github/workflows/license-check.yml +78 -0
- data/.github/workflows/lint.yml +79 -0
- data/.github/workflows/security.yml +131 -0
- data/.github/workflows/semgrep.yml +26 -0
- data/.github/workflows/test.yml +44 -0
- data/.gitignore +75 -0
- data/.rubocop.yml +33 -0
- data/.ruby-version +1 -0
- data/.simplecov +14 -0
- data/.stylelintignore +10 -0
- data/.stylelintrc.json +37 -0
- data/AGENTS.md +51 -0
- data/CHANGELOG.md +568 -0
- data/CLAUDE.md +632 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +327 -0
- data/LICENSE +21 -0
- data/README.md +1209 -0
- data/Rakefile +25 -0
- data/app/assets/images/cherry_blossom.svg +1525 -0
- data/app/assets/images/cherry_blossom_tree.png +0 -0
- data/app/assets/images/prysm-icon.png +0 -0
- data/app/assets/stylesheets/base.css +29 -0
- data/app/assets/stylesheets/components.css +1652 -0
- data/app/assets/stylesheets/forms.css +152 -0
- data/app/assets/stylesheets/loading.css +145 -0
- data/app/assets/stylesheets/neon_sakura.css +40 -0
- data/app/assets/stylesheets/pagy-tailwind.css +120 -0
- data/app/assets/stylesheets/theme-default.css +40 -0
- data/app/assets/stylesheets/theme-green.css +84 -0
- data/app/assets/stylesheets/theme-purple.css +94 -0
- data/app/assets/stylesheets/theme-red.css +84 -0
- data/app/assets/stylesheets/utility-borders.css +29 -0
- data/app/assets/stylesheets/utility-colors.css +185 -0
- data/app/assets/stylesheets/utility-effects.css +123 -0
- data/app/assets/stylesheets/utility-gradients.css +158 -0
- data/app/assets/stylesheets/utility-layout.css +132 -0
- data/app/assets/stylesheets/utility-reset.css +13 -0
- data/app/assets/stylesheets/utility-responsive.css +145 -0
- data/app/assets/stylesheets/utility-sizing.css +99 -0
- data/app/assets/stylesheets/utility-spacing.css +174 -0
- data/app/assets/stylesheets/utility-typography.css +97 -0
- data/app/controllers/errors_controller.rb +120 -0
- data/app/controllers/style_guide_controller.rb +117 -0
- data/app/helpers/errors_helper.rb +12 -0
- data/app/helpers/neon_sakura/navbar_helper.rb +43 -0
- data/app/helpers/style_guide_helper.rb +36 -0
- data/app/javascript/neon_sakura/dropdown.js +22 -0
- data/app/javascript/neon_sakura/navbar.js +71 -0
- data/app/javascript/neon_sakura/theme_switcher.js +187 -0
- data/app/views/errors/show.html.erb +105 -0
- data/app/views/layouts/error.html.erb +19 -0
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +14 -0
- data/app/views/layouts/mission_control/jobs/_navigation.html.erb +21 -0
- data/app/views/layouts/mission_control/jobs/application.html.erb +453 -0
- data/app/views/layouts/style_guide.html.erb +416 -0
- data/app/views/shared/_file_upload.html.erb +184 -0
- data/app/views/shared/_footer.html.erb +23 -0
- data/app/views/shared/_header.html.erb +42 -0
- data/app/views/shared/_navbar.html.erb +306 -0
- data/app/views/shared/_profile_image_selector.html.erb +165 -0
- data/app/views/shared/_theme_switcher.html.erb +64 -0
- data/app/views/shared/icons/_adjustments.html.erb +10 -0
- data/app/views/shared/icons/_alert_circle.html.erb +3 -0
- data/app/views/shared/icons/_alert_triangle.html.erb +3 -0
- data/app/views/shared/icons/_archive.html.erb +3 -0
- data/app/views/shared/icons/_arrow_down.html.erb +3 -0
- data/app/views/shared/icons/_arrow_left.html.erb +3 -0
- data/app/views/shared/icons/_arrow_up.html.erb +3 -0
- data/app/views/shared/icons/_arrows_pointing_in.html.erb +10 -0
- data/app/views/shared/icons/_arrows_pointing_out.html.erb +10 -0
- data/app/views/shared/icons/_artemis_logo.html.erb +26 -0
- data/app/views/shared/icons/_auth_banner.html.erb +1 -0
- data/app/views/shared/icons/_bars.html.erb +10 -0
- data/app/views/shared/icons/_bell.html.erb +3 -0
- data/app/views/shared/icons/_book.html.erb +3 -0
- data/app/views/shared/icons/_bookmark.html.erb +3 -0
- data/app/views/shared/icons/_box.html.erb +3 -0
- data/app/views/shared/icons/_brain.html.erb +3 -0
- data/app/views/shared/icons/_briefcase.html.erb +3 -0
- data/app/views/shared/icons/_calendar.html.erb +3 -0
- data/app/views/shared/icons/_camera.html.erb +4 -0
- data/app/views/shared/icons/_chart_bar.html.erb +3 -0
- data/app/views/shared/icons/_chart_line.html.erb +10 -0
- data/app/views/shared/icons/_chart_pie.html.erb +11 -0
- data/app/views/shared/icons/_chat.html.erb +3 -0
- data/app/views/shared/icons/_check.html.erb +3 -0
- data/app/views/shared/icons/_check_circle.html.erb +3 -0
- data/app/views/shared/icons/_cherry_blossom.html.erb +1516 -0
- data/app/views/shared/icons/_cherry_blossom_silhouette.html.erb +1016 -0
- data/app/views/shared/icons/_cherry_blossom_single_flower.html.erb +1125 -0
- data/app/views/shared/icons/_cherry_blossom_tree.html.erb +159 -0
- data/app/views/shared/icons/_chevron_down.html.erb +3 -0
- data/app/views/shared/icons/_chevron_right.html.erb +9 -0
- data/app/views/shared/icons/_clipboard.html.erb +3 -0
- data/app/views/shared/icons/_clock.html.erb +3 -0
- data/app/views/shared/icons/_close.html.erb +3 -0
- data/app/views/shared/icons/_cog.html.erb +4 -0
- data/app/views/shared/icons/_crop.html.erb +10 -0
- data/app/views/shared/icons/_crown.html.erb +3 -0
- data/app/views/shared/icons/_disc.html.erb +3 -0
- data/app/views/shared/icons/_download.html.erb +3 -0
- data/app/views/shared/icons/_dragonfly.html.erb +58 -0
- data/app/views/shared/icons/_duplicate.html.erb +4 -0
- data/app/views/shared/icons/_edit.html.erb +3 -0
- data/app/views/shared/icons/_envelope.html.erb +3 -0
- data/app/views/shared/icons/_eraser.html.erb +10 -0
- data/app/views/shared/icons/_external_link.html.erb +3 -0
- data/app/views/shared/icons/_eye.html.erb +4 -0
- data/app/views/shared/icons/_file_csv.html.erb +10 -0
- data/app/views/shared/icons/_file_export.html.erb +10 -0
- data/app/views/shared/icons/_file_image.html.erb +10 -0
- data/app/views/shared/icons/_file_import.html.erb +10 -0
- data/app/views/shared/icons/_file_question.html.erb +6 -0
- data/app/views/shared/icons/_film.html.erb +3 -0
- data/app/views/shared/icons/_filter.html.erb +3 -0
- data/app/views/shared/icons/_folder.html.erb +3 -0
- data/app/views/shared/icons/_folder_open.html.erb +3 -0
- data/app/views/shared/icons/_folder_plus.html.erb +3 -0
- data/app/views/shared/icons/_globe.html.erb +3 -0
- data/app/views/shared/icons/_google.html.erb +11 -0
- data/app/views/shared/icons/_heart.html.erb +3 -0
- data/app/views/shared/icons/_heart_broken.html.erb +11 -0
- data/app/views/shared/icons/_heart_pulse.html.erb +4 -0
- data/app/views/shared/icons/_history.html.erb +11 -0
- data/app/views/shared/icons/_home.html.erb +10 -0
- data/app/views/shared/icons/_image.html.erb +3 -0
- data/app/views/shared/icons/_inbox.html.erb +3 -0
- data/app/views/shared/icons/_info_circle.html.erb +10 -0
- data/app/views/shared/icons/_key.html.erb +3 -0
- data/app/views/shared/icons/_layers.html.erb +10 -0
- data/app/views/shared/icons/_lightbulb.html.erb +10 -0
- data/app/views/shared/icons/_lightning.html.erb +3 -0
- data/app/views/shared/icons/_list.html.erb +3 -0
- data/app/views/shared/icons/_lock.html.erb +3 -0
- data/app/views/shared/icons/_logout.html.erb +3 -0
- data/app/views/shared/icons/_magazine.html.erb +3 -0
- data/app/views/shared/icons/_magic.html.erb +3 -0
- data/app/views/shared/icons/_minus.html.erb +10 -0
- data/app/views/shared/icons/_mobile.html.erb +10 -0
- data/app/views/shared/icons/_moon.html.erb +3 -0
- data/app/views/shared/icons/_network.html.erb +10 -0
- data/app/views/shared/icons/_new_item_banner.html.erb +1 -0
- data/app/views/shared/icons/_ouroboros.html.erb +24 -0
- data/app/views/shared/icons/_package.html.erb +3 -0
- data/app/views/shared/icons/_palette.html.erb +3 -0
- data/app/views/shared/icons/_paper_plane.html.erb +10 -0
- data/app/views/shared/icons/_photo.html.erb +10 -0
- data/app/views/shared/icons/_play.html.erb +4 -0
- data/app/views/shared/icons/_plus.html.erb +3 -0
- data/app/views/shared/icons/_pocket.html.erb +11 -0
- data/app/views/shared/icons/_prysm-icon.html.erb +34 -0
- data/app/views/shared/icons/_prysm.html.erb +13 -0
- data/app/views/shared/icons/_pushbullet-1.html.erb +29 -0
- data/app/views/shared/icons/_pushbullet-2.html.erb +2 -0
- data/app/views/shared/icons/_puzzle.html.erb +10 -0
- data/app/views/shared/icons/_qrcode.html.erb +3 -0
- data/app/views/shared/icons/_question.html.erb +3 -0
- data/app/views/shared/icons/_receipt.html.erb +10 -0
- data/app/views/shared/icons/_redo.html.erb +3 -0
- data/app/views/shared/icons/_refresh.html.erb +3 -0
- data/app/views/shared/icons/_rocket.html.erb +10 -0
- data/app/views/shared/icons/_rss.html.erb +3 -0
- data/app/views/shared/icons/_save.html.erb +3 -0
- data/app/views/shared/icons/_search.html.erb +3 -0
- data/app/views/shared/icons/_search_minus.html.erb +10 -0
- data/app/views/shared/icons/_search_plus.html.erb +10 -0
- data/app/views/shared/icons/_server_error.html.erb +6 -0
- data/app/views/shared/icons/_share.html.erb +3 -0
- data/app/views/shared/icons/_shield_check.html.erb +3 -0
- data/app/views/shared/icons/_sign_in.html.erb +3 -0
- data/app/views/shared/icons/_spinner.html.erb +4 -0
- data/app/views/shared/icons/_star.html.erb +3 -0
- data/app/views/shared/icons/_store.html.erb +10 -0
- data/app/views/shared/icons/_sun.html.erb +3 -0
- data/app/views/shared/icons/_sync.html.erb +3 -0
- data/app/views/shared/icons/_table.html.erb +3 -0
- data/app/views/shared/icons/_tag.html.erb +3 -0
- data/app/views/shared/icons/_tags.html.erb +11 -0
- data/app/views/shared/icons/_tools.html.erb +4 -0
- data/app/views/shared/icons/_trash.html.erb +3 -0
- data/app/views/shared/icons/_undo.html.erb +3 -0
- data/app/views/shared/icons/_unlock.html.erb +3 -0
- data/app/views/shared/icons/_upload.html.erb +3 -0
- data/app/views/shared/icons/_user.html.erb +3 -0
- data/app/views/shared/icons/_user_circle.html.erb +10 -0
- data/app/views/shared/icons/_user_plus.html.erb +10 -0
- data/app/views/shared/icons/_video.html.erb +3 -0
- data/app/views/shared/icons/_wrench.html.erb +11 -0
- data/app/views/style_guide/index.html.erb +77 -0
- data/app/views/style_guide/sections/_alerts.html.erb +114 -0
- data/app/views/style_guide/sections/_badges.html.erb +78 -0
- data/app/views/style_guide/sections/_buttons.html.erb +130 -0
- data/app/views/style_guide/sections/_cards.html.erb +84 -0
- data/app/views/style_guide/sections/_colors.html.erb +106 -0
- data/app/views/style_guide/sections/_file_upload.html.erb +135 -0
- data/app/views/style_guide/sections/_forms.html.erb +129 -0
- data/app/views/style_guide/sections/_gradients.html.erb +253 -0
- data/app/views/style_guide/sections/_header.html.erb +12 -0
- data/app/views/style_guide/sections/_icons.html.erb +55 -0
- data/app/views/style_guide/sections/_images.html.erb +40 -0
- data/app/views/style_guide/sections/_loading.html.erb +242 -0
- data/app/views/style_guide/sections/_pagination.html.erb +212 -0
- data/app/views/style_guide/sections/_profile_components.html.erb +203 -0
- data/app/views/style_guide/sections/_theme_switcher.html.erb +72 -0
- data/app/views/style_guide/sections/_typography.html.erb +65 -0
- data/bin/ai-optimize-claude-md +540 -0
- data/bin/ai-review-local +345 -0
- data/bin/ai-security-review +585 -0
- data/bin/brakeman +9 -0
- data/bin/install-hooks +57 -0
- data/bin/rake +7 -0
- data/bin/rubocop +10 -0
- data/bin/verify_setup.rb +31 -0
- data/config/brakeman.ignore +28 -0
- data/config/initializers/neon_sakura.rb +15 -0
- data/config/license_overrides.yml +13 -0
- data/config/routes.rb +21 -0
- data/config/theme_mappings.yml +61 -0
- data/docs/PRYSM_ASSETS.md +210 -0
- data/docs/plans/extract_ai_reviewer_plan.md +151 -0
- data/docs/plans/neon_sakura_gem_plan.md +138 -0
- data/lib/neon_sakura/configuration.rb +94 -0
- data/lib/neon_sakura/engine.rb +48 -0
- data/lib/neon_sakura/icon_helper.rb +54 -0
- data/lib/neon_sakura/profile_helper.rb +24 -0
- data/lib/neon_sakura/stylesheet_helper.rb +40 -0
- data/lib/neon_sakura/theme_helper.rb +63 -0
- data/lib/neon_sakura/theme_importer.rb +112 -0
- data/lib/neon_sakura/version.rb +5 -0
- data/lib/neon_sakura.rb +13 -0
- data/neon_sakura.gemspec +50 -0
- data/package.json +18 -0
- data/scripts/git-hooks/post-merge +132 -0
- data/scripts/git-hooks/pre-commit +123 -0
- data/scripts/git-hooks/pre-push +127 -0
- data/scripts/license-check.rb +587 -0
- data/settings.local.json +12 -0
- data/yarn.lock +778 -0
- 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.
|