plutonium 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +574 -0
- data/.claude/skills/plutonium-auth/SKILL.md +167 -302
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +674 -0
- data/.claude/skills/plutonium-testing/SKILL.md +9 -6
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +44 -2
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1010 -1214
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +52 -51
- data/app/assets/plutonium.min.js.map +3 -3
- data/docs/.vitepress/config.ts +38 -29
- data/docs/.vitepress/theme/components/HomeAudienceSplit.vue +53 -0
- data/docs/.vitepress/theme/components/HomeCta.vue +108 -0
- data/docs/.vitepress/theme/components/HomeHero.vue +70 -0
- data/docs/.vitepress/theme/components/HomeInTheBox.vue +74 -0
- data/docs/.vitepress/theme/components/HomePillars.vue +42 -0
- data/docs/.vitepress/theme/components/HomeStopWriting.vue +49 -0
- data/docs/.vitepress/theme/components/HomeWalkthrough.vue +111 -0
- data/docs/.vitepress/theme/components/SectionLanding.vue +115 -0
- data/docs/.vitepress/theme/custom.css +144 -0
- data/docs/.vitepress/theme/index.ts +58 -1
- data/docs/getting-started/index.md +33 -57
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/02-first-resource.md +17 -8
- data/docs/getting-started/tutorial/03-authentication.md +31 -23
- data/docs/getting-started/tutorial/05-custom-actions.md +9 -4
- data/docs/getting-started/tutorial/06-nested-resources.md +7 -1
- data/docs/getting-started/tutorial/07-author-portal.md +8 -0
- data/docs/getting-started/tutorial/08-customizing-ui.md +4 -0
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +98 -462
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +93 -298
- data/docs/guides/custom-actions.md +126 -441
- data/docs/guides/customizing-ui.md +258 -0
- data/docs/guides/index.md +49 -52
- data/docs/guides/multi-tenancy.md +123 -186
- data/docs/guides/nested-resources.md +137 -396
- data/docs/guides/search-filtering.md +127 -238
- data/docs/guides/testing.md +10 -5
- data/docs/guides/theming.md +168 -405
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +112 -425
- data/docs/guides/user-profile.md +82 -241
- data/docs/index.md +10 -219
- data/docs/public/asciinema/home-scaffold.cast +305 -0
- data/docs/public/images/guides/custom-actions-bulk.png +0 -0
- data/docs/public/images/guides/multi-tenancy-dashboard.png +0 -0
- data/docs/public/images/guides/multi-tenancy-welcome.png +0 -0
- data/docs/public/images/guides/nested-inputs.png +0 -0
- data/docs/public/images/guides/nested-resources-tab.png +0 -0
- data/docs/public/images/guides/search-filtering-index.png +0 -0
- data/docs/public/images/guides/search-filtering-panel.png +0 -0
- data/docs/public/images/guides/theming-after.png +0 -0
- data/docs/public/images/guides/theming-before.png +0 -0
- data/docs/public/images/guides/user-invites-landing.png +0 -0
- data/docs/public/images/guides/user-profile-edit.png +0 -0
- data/docs/public/images/guides/user-profile-show.png +0 -0
- data/docs/public/images/home-index.png +0 -0
- data/docs/public/images/home-new.png +0 -0
- data/docs/public/images/home-show.png +0 -0
- data/docs/public/images/tutorial/02-empty-index.png +0 -0
- data/docs/public/images/tutorial/02-index-with-posts.png +0 -0
- data/docs/public/images/tutorial/02-new-form-modal.png +0 -0
- data/docs/public/images/tutorial/02-new-form.png +0 -0
- data/docs/public/images/tutorial/03-create-account.png +0 -0
- data/docs/public/images/tutorial/03-login.png +0 -0
- data/docs/public/images/tutorial/04-admin-index.png +0 -0
- data/docs/public/images/tutorial/05-actions-menu.png +0 -0
- data/docs/public/images/tutorial/05-row-actions.png +0 -0
- data/docs/public/images/tutorial/06-comments-tab.png +0 -0
- data/docs/public/images/tutorial/06-post-with-comments.png +0 -0
- data/docs/public/images/tutorial/07-author-dashboard.png +0 -0
- data/docs/public/images/tutorial/07-author-portal.png +0 -0
- data/docs/public/images/tutorial/08-customized-index.png +0 -0
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +229 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +67 -48
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +368 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +400 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +121 -0
- data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md +1648 -0
- data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md.tasks.json +109 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/docs/superpowers/specs/2026-05-15-public-pages-overhaul-design.md +263 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +10 -0
- data/lib/generators/pu/core/update/update_generator.rb +0 -20
- data/lib/generators/pu/invites/install_generator.rb +45 -0
- data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +1 -0
- data/lib/generators/pu/profile/conn_generator.rb +2 -2
- data/lib/generators/pu/res/conn/conn_generator.rb +33 -6
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -0
- data/lib/generators/pu/rodauth/account_generator.rb +2 -1
- data/lib/generators/pu/rodauth/admin_generator.rb +0 -2
- data/lib/generators/pu/rodauth/migration_generator.rb +0 -2
- data/lib/generators/pu/rodauth/views_generator.rb +0 -2
- data/lib/generators/pu/saas/membership/USAGE +4 -1
- data/lib/generators/pu/saas/setup_generator.rb +16 -4
- data/lib/generators/pu/saas/welcome/templates/app/controllers/welcome_controller.rb.tt +1 -1
- data/lib/plutonium/definition/base.rb +1 -1
- data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
- data/lib/plutonium/helpers/turbo_helper.rb +30 -0
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +23 -5
- data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/ui/component/methods.rb +5 -0
- data/lib/plutonium/ui/form/base.rb +23 -3
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
- data/lib/plutonium/ui/form/components/secure_association.rb +103 -22
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/interaction.rb +1 -1
- data/lib/plutonium/ui/form/resource.rb +0 -4
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/ui/grid/resource.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +1 -0
- data/lib/plutonium/ui/page/base.rb +0 -7
- data/lib/plutonium/ui/page/edit.rb +1 -1
- data/lib/plutonium/ui/page/index.rb +4 -4
- data/lib/plutonium/ui/page/new.rb +1 -1
- data/lib/plutonium/ui/table/components/filter_form.rb +12 -4
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +8 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +13 -10
- data/src/css/slim_select.css +4 -0
- data/src/js/controllers/form_controller.js +5 -4
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +661 -544
- metadata +86 -33
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -331
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -651
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -462
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -326
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -544
data/docs/guides/theming.md
CHANGED
|
@@ -1,440 +1,197 @@
|
|
|
1
1
|
# Theming
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Customize colors, styles, dark mode, and branding.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Goal
|
|
6
6
|
|
|
7
|
-
Plutonium
|
|
7
|
+
Adapt Plutonium's defaults to match your brand: primary color, fonts, logo, dark mode behavior, optionally per-component theming.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- **Component Classes** - Pre-built `.pu-*` classes for buttons, inputs, tables
|
|
11
|
-
- **Tailwind Configuration** - Extend colors and design tokens
|
|
12
|
-
- **Component Themes** - Override form, table, and display styling
|
|
9
|
+
## How theming layers stack
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
| Layer | What to edit | When to use |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **Asset config** | `Plutonium.configure` | Logo / favicon / asset file paths |
|
|
14
|
+
| **CSS tokens** | `--pu-*` variables in your CSS | Colors that should auto-switch with dark mode |
|
|
15
|
+
| **Tailwind theme** | `tailwind.config.js` | Brand color palettes, custom fonts |
|
|
16
|
+
| **`.pu-*` classes** | Use in markup | Pre-styled buttons / inputs / cards |
|
|
17
|
+
| **Phlexi component themes** | Per-resource `Theme` class | Override Form/Display/Table per resource |
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
## 🚨 Critical
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
- **Always register Stimulus controllers** — `registerControllers(application)`. Without it, the entire interactive layer is dead.
|
|
22
|
+
- **Use `plutoniumTailwindConfig.merge`** when overriding Tailwind theme — plain object spread drops Plutonium's defaults.
|
|
23
|
+
- **Tokens are CSS variables, not Tailwind keys** — `bg-[var(--pu-surface)]`, NOT `bg-pu-surface`.
|
|
24
|
+
- **Dark mode is `selector`, not `class`** — toggle by adding/removing `dark` on `<html>`.
|
|
25
|
+
- **Prefer `.pu-*` classes and `var(--pu-*)` tokens** over hardcoded `gray-X/dark:gray-Y` pairs — they switch with dark mode automatically.
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
## Step 1: Run the assets generator
|
|
21
28
|
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
--pu-surface /* Card/panel backgrounds */
|
|
25
|
-
--pu-surface-alt /* Alternate surface (headers, sidebars) */
|
|
26
|
-
--pu-surface-raised /* Elevated surfaces */
|
|
27
|
-
--pu-surface-overlay /* Modal/dropdown overlays */
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
#### Border Colors
|
|
31
|
-
|
|
32
|
-
```css
|
|
33
|
-
--pu-border /* Default borders */
|
|
34
|
-
--pu-border-muted /* Subtle borders */
|
|
35
|
-
--pu-border-strong /* Emphasized borders */
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
#### Text Colors
|
|
39
|
-
|
|
40
|
-
```css
|
|
41
|
-
--pu-text /* Primary text */
|
|
42
|
-
--pu-text-muted /* Secondary text */
|
|
43
|
-
--pu-text-subtle /* Tertiary/placeholder text */
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
#### Table Tokens
|
|
47
|
-
|
|
48
|
-
```css
|
|
49
|
-
--pu-table-header-bg /* Header background */
|
|
50
|
-
--pu-table-header-text /* Header text color */
|
|
51
|
-
--pu-table-row-bg /* Row background */
|
|
52
|
-
--pu-table-row-hover /* Row hover state */
|
|
53
|
-
--pu-table-row-selected /* Selected row */
|
|
54
|
-
--pu-table-border /* Row borders */
|
|
29
|
+
```bash
|
|
30
|
+
rails generate pu:core:assets
|
|
55
31
|
```
|
|
56
32
|
|
|
57
|
-
|
|
33
|
+
This installs npm packages, creates `tailwind.config.js`, imports Plutonium CSS, registers Stimulus controllers, and points `Plutonium.configure` at your asset files. Run once per app.
|
|
58
34
|
|
|
59
|
-
|
|
60
|
-
--pu-input-bg /* Input background */
|
|
61
|
-
--pu-input-border /* Input border */
|
|
62
|
-
--pu-input-focus-ring /* Focus ring color */
|
|
63
|
-
--pu-input-placeholder /* Placeholder text */
|
|
64
|
-
```
|
|
35
|
+
## Step 2: Asset configuration
|
|
65
36
|
|
|
66
|
-
|
|
37
|
+
```ruby
|
|
38
|
+
# config/initializers/plutonium.rb
|
|
39
|
+
Plutonium.configure do |config|
|
|
40
|
+
config.load_defaults 1.0
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
42
|
+
config.assets.stylesheet = "application"
|
|
43
|
+
config.assets.script = "application"
|
|
44
|
+
config.assets.logo = "my_logo.png"
|
|
45
|
+
config.assets.favicon = "my_favicon.ico"
|
|
46
|
+
end
|
|
71
47
|
```
|
|
72
48
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```css
|
|
76
|
-
--pu-shadow-sm /* Subtle shadow */
|
|
77
|
-
--pu-shadow-md /* Medium shadow */
|
|
78
|
-
--pu-shadow-lg /* Large shadow */
|
|
79
|
-
```
|
|
49
|
+
Logo / favicon resolved from `app/assets/images/`.
|
|
80
50
|
|
|
81
|
-
|
|
51
|
+
Default palette is turquoise on the left; swapping `primary` to indigo gives every `.pu-btn-primary`, focus ring, and selected-row highlight the new colour without touching any markup:
|
|
82
52
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
53
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
|
|
54
|
+
<figure>
|
|
55
|
+
<img src="/images/guides/theming-before.png" alt="Default turquoise palette" />
|
|
56
|
+
<figcaption><em>Default (turquoise)</em></figcaption>
|
|
57
|
+
</figure>
|
|
58
|
+
<figure>
|
|
59
|
+
<img src="/images/guides/theming-after.png" alt="Indigo palette via tailwind override" />
|
|
60
|
+
<figcaption><em>After indigo override</em></figcaption>
|
|
61
|
+
</figure>
|
|
62
|
+
</div>
|
|
90
63
|
|
|
91
|
-
|
|
64
|
+
## Step 3: Customize colors via Tailwind
|
|
92
65
|
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
66
|
+
```javascript
|
|
67
|
+
// tailwind.config.js
|
|
68
|
+
theme: plutoniumTailwindConfig.merge(plutoniumTailwindConfig.theme, {
|
|
69
|
+
extend: {
|
|
70
|
+
colors: {
|
|
71
|
+
primary: { 50: '#eff6ff', 500: '#3b82f6', 900: '#1e3a8a' },
|
|
72
|
+
secondary: { 50: '#f3f4f6', 500: '#6b7280', 900: '#111827' },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
})
|
|
99
76
|
```
|
|
100
77
|
|
|
101
|
-
|
|
78
|
+
### Default palette
|
|
102
79
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
80
|
+
| Color | Use |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `primary` | Brand primary (turquoise default) |
|
|
83
|
+
| `secondary` | Brand secondary (navy default) |
|
|
84
|
+
| `success` | Success states (green) |
|
|
85
|
+
| `info` | Informational (blue) |
|
|
86
|
+
| `warning` | Warning (amber) |
|
|
87
|
+
| `danger` | Error (red) |
|
|
88
|
+
| `accent` | Highlight (coral pink) |
|
|
110
89
|
|
|
111
|
-
|
|
90
|
+
## Step 4: Customize design tokens (dark-mode-aware)
|
|
112
91
|
|
|
113
92
|
```css
|
|
114
|
-
/* app/assets/stylesheets/application.css */
|
|
115
|
-
@import "tailwindcss";
|
|
93
|
+
/* app/assets/stylesheets/application.tailwind.css */
|
|
116
94
|
@import "gem:plutonium/src/css/plutonium.css";
|
|
95
|
+
@import "tailwindcss";
|
|
117
96
|
|
|
118
|
-
/* Light mode overrides */
|
|
119
97
|
:root {
|
|
120
98
|
--pu-surface: #fafafa;
|
|
121
|
-
--pu-border:
|
|
122
|
-
--pu-input-focus-ring: #6366f1;
|
|
99
|
+
--pu-border: #d1d5db;
|
|
123
100
|
}
|
|
124
101
|
|
|
125
|
-
/* Dark mode overrides */
|
|
126
102
|
.dark {
|
|
127
|
-
--pu-surface: #
|
|
128
|
-
--pu-border:
|
|
103
|
+
--pu-surface: #111827;
|
|
104
|
+
--pu-border: #374151;
|
|
129
105
|
}
|
|
130
106
|
```
|
|
131
107
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Plutonium provides pre-built component classes for common UI elements.
|
|
135
|
-
|
|
136
|
-
### Buttons
|
|
137
|
-
|
|
138
|
-
```html
|
|
139
|
-
<!-- Sizes -->
|
|
140
|
-
<button class="pu-btn pu-btn-md pu-btn-primary">Medium</button>
|
|
141
|
-
<button class="pu-btn pu-btn-sm pu-btn-primary">Small</button>
|
|
142
|
-
<button class="pu-btn pu-btn-xs pu-btn-primary">Extra Small</button>
|
|
143
|
-
|
|
144
|
-
<!-- Solid variants -->
|
|
145
|
-
<button class="pu-btn pu-btn-md pu-btn-primary">Primary</button>
|
|
146
|
-
<button class="pu-btn pu-btn-md pu-btn-secondary">Secondary</button>
|
|
147
|
-
<button class="pu-btn pu-btn-md pu-btn-success">Success</button>
|
|
148
|
-
<button class="pu-btn pu-btn-md pu-btn-danger">Danger</button>
|
|
149
|
-
<button class="pu-btn pu-btn-md pu-btn-warning">Warning</button>
|
|
150
|
-
<button class="pu-btn pu-btn-md pu-btn-info">Info</button>
|
|
151
|
-
<button class="pu-btn pu-btn-md pu-btn-accent">Accent</button>
|
|
152
|
-
|
|
153
|
-
<!-- Soft variants (tinted backgrounds) -->
|
|
154
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-primary">Soft Primary</button>
|
|
155
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-secondary">Soft Secondary</button>
|
|
156
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-success">Soft Success</button>
|
|
157
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-danger">Soft Danger</button>
|
|
158
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-warning">Soft Warning</button>
|
|
159
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-info">Soft Info</button>
|
|
160
|
-
<button class="pu-btn pu-btn-md pu-btn-soft-accent">Soft Accent</button>
|
|
161
|
-
|
|
162
|
-
<!-- Other styles -->
|
|
163
|
-
<button class="pu-btn pu-btn-md pu-btn-ghost">Ghost</button>
|
|
164
|
-
<button class="pu-btn pu-btn-md pu-btn-outline">Outline</button>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Form Inputs
|
|
168
|
-
|
|
169
|
-
```html
|
|
170
|
-
<label class="pu-label">Email</label>
|
|
171
|
-
<input type="email" class="pu-input" placeholder="you@example.com">
|
|
172
|
-
<p class="pu-hint">We'll never share your email.</p>
|
|
173
|
-
|
|
174
|
-
<!-- Validation states -->
|
|
175
|
-
<input type="text" class="pu-input pu-input-invalid">
|
|
176
|
-
<p class="pu-error">This field is required.</p>
|
|
177
|
-
|
|
178
|
-
<input type="text" class="pu-input pu-input-valid">
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Checkboxes
|
|
108
|
+
Tokens auto-switch when the user toggles dark mode. See [Reference › UI › Assets › Design tokens](/reference/ui/assets#design-tokens) for the full token catalog.
|
|
182
109
|
|
|
183
|
-
|
|
184
|
-
<input type="checkbox" class="pu-checkbox">
|
|
185
|
-
```
|
|
110
|
+
## Using tokens in your code
|
|
186
111
|
|
|
187
|
-
|
|
112
|
+
```erb
|
|
113
|
+
<h1 class="text-[var(--pu-text)]">Title</h1>
|
|
114
|
+
<p class="text-[var(--pu-text-muted)]">Description</p>
|
|
188
115
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
<div class="pu-card-body">
|
|
192
|
-
Card content here
|
|
193
|
-
</div>
|
|
116
|
+
<div class="bg-[var(--pu-surface)] border border-[var(--pu-border)] rounded-[var(--pu-radius-lg)]">
|
|
117
|
+
Content
|
|
194
118
|
</div>
|
|
195
119
|
```
|
|
196
120
|
|
|
197
|
-
### Tables
|
|
198
|
-
|
|
199
|
-
```html
|
|
200
|
-
<div class="pu-table-wrapper">
|
|
201
|
-
<table class="pu-table">
|
|
202
|
-
<thead class="pu-table-header">
|
|
203
|
-
<tr>
|
|
204
|
-
<th class="pu-table-header-cell">Name</th>
|
|
205
|
-
</tr>
|
|
206
|
-
</thead>
|
|
207
|
-
<tbody>
|
|
208
|
-
<tr class="pu-table-body-row">
|
|
209
|
-
<td class="pu-table-body-cell">John</td>
|
|
210
|
-
</tr>
|
|
211
|
-
</tbody>
|
|
212
|
-
</table>
|
|
213
|
-
</div>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Empty States
|
|
217
|
-
|
|
218
|
-
```html
|
|
219
|
-
<div class="pu-empty-state">
|
|
220
|
-
<svg class="pu-empty-state-icon">...</svg>
|
|
221
|
-
<h3 class="pu-empty-state-title">No items found</h3>
|
|
222
|
-
<p class="pu-empty-state-description">Get started by creating a new item.</p>
|
|
223
|
-
</div>
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Setup Custom Assets
|
|
227
|
-
|
|
228
|
-
Run the assets generator to set up your own TailwindCSS build:
|
|
229
|
-
|
|
230
|
-
```bash
|
|
231
|
-
rails generate pu:core:assets
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
This:
|
|
235
|
-
1. Installs required npm packages (`@radioactive-labs/plutonium`, TailwindCSS plugins)
|
|
236
|
-
2. Creates `tailwind.config.js` that extends Plutonium's config
|
|
237
|
-
3. Imports Plutonium CSS into your `application.tailwind.css`
|
|
238
|
-
4. Registers Plutonium's Stimulus controllers
|
|
239
|
-
5. Updates Plutonium config to use your assets
|
|
240
|
-
|
|
241
|
-
## Asset Configuration
|
|
242
|
-
|
|
243
|
-
Configure assets in the initializer:
|
|
244
|
-
|
|
245
121
|
```ruby
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
122
|
+
class MyComponent < Plutonium::UI::Component::Base
|
|
123
|
+
def view_template
|
|
124
|
+
div(
|
|
125
|
+
class: "bg-[var(--pu-surface)] border border-[var(--pu-border)] rounded-[var(--pu-radius-lg)]",
|
|
126
|
+
style: "box-shadow: var(--pu-shadow-md)"
|
|
127
|
+
) do
|
|
128
|
+
h2(class: "text-lg font-semibold text-[var(--pu-text)]") { "Title" }
|
|
129
|
+
p(class: "text-[var(--pu-text-muted)]") { "Description" }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
254
132
|
end
|
|
255
133
|
```
|
|
256
134
|
|
|
257
|
-
##
|
|
258
|
-
|
|
259
|
-
### Generated Config
|
|
260
|
-
|
|
261
|
-
```javascript
|
|
262
|
-
// tailwind.config.js
|
|
263
|
-
const { execSync } = require('child_process');
|
|
264
|
-
const plutoniumGemPath = execSync("bundle show plutonium").toString().trim();
|
|
265
|
-
const plutoniumTailwindConfig = require(`${plutoniumGemPath}/tailwind.options.js`)
|
|
266
|
-
|
|
267
|
-
module.exports = {
|
|
268
|
-
darkMode: plutoniumTailwindConfig.darkMode,
|
|
269
|
-
plugins: [
|
|
270
|
-
// Add your plugins here
|
|
271
|
-
].concat(plutoniumTailwindConfig.plugins),
|
|
272
|
-
theme: plutoniumTailwindConfig.merge(
|
|
273
|
-
plutoniumTailwindConfig.theme,
|
|
274
|
-
{
|
|
275
|
-
// Your custom theme overrides
|
|
276
|
-
},
|
|
277
|
-
),
|
|
278
|
-
content: [
|
|
279
|
-
`${__dirname}/app/**/*.{erb,haml,html,slim,rb}`,
|
|
280
|
-
`${__dirname}/app/javascript/**/*.js`,
|
|
281
|
-
`${__dirname}/packages/**/app/**/*.{erb,haml,html,slim,rb}`,
|
|
282
|
-
].concat(plutoniumTailwindConfig.content),
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Customizing Colors
|
|
135
|
+
## Use `.pu-*` component classes
|
|
287
136
|
|
|
288
|
-
|
|
137
|
+
Pre-styled ready-to-use components:
|
|
289
138
|
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
theme: plutoniumTailwindConfig.merge(
|
|
293
|
-
plutoniumTailwindConfig.theme,
|
|
294
|
-
{
|
|
295
|
-
extend: {
|
|
296
|
-
colors: {
|
|
297
|
-
primary: {
|
|
298
|
-
50: '#eff6ff',
|
|
299
|
-
100: '#dbeafe',
|
|
300
|
-
200: '#bfdbfe',
|
|
301
|
-
300: '#93c5fd',
|
|
302
|
-
400: '#60a5fa',
|
|
303
|
-
500: '#3b82f6', // Your brand color
|
|
304
|
-
600: '#2563eb',
|
|
305
|
-
700: '#1d4ed8',
|
|
306
|
-
800: '#1e40af',
|
|
307
|
-
900: '#1e3a8a',
|
|
308
|
-
950: '#172554',
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
),
|
|
139
|
+
```erb
|
|
140
|
+
<%= form.submit "Save", class: "pu-btn pu-btn-md pu-btn-primary" %>
|
|
314
141
|
```
|
|
315
142
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
|
321
|
-
|
|
322
|
-
|
|
|
323
|
-
| `secondary` | Secondary color |
|
|
324
|
-
| `success` | Success states (green) |
|
|
325
|
-
| `info` | Informational states (blue) |
|
|
326
|
-
| `warning` | Warning states (amber) |
|
|
327
|
-
| `danger` | Error/danger states (red) |
|
|
328
|
-
| `accent` | Accent highlights |
|
|
329
|
-
|
|
330
|
-
## CSS Imports
|
|
143
|
+
| Family | Classes |
|
|
144
|
+
|---|---|
|
|
145
|
+
| Buttons | `.pu-btn`, `.pu-btn-md/-sm/-xs`, `.pu-btn-primary/-secondary/-danger/-success/-warning/-info/-accent`, `.pu-btn-ghost/-outline`, `.pu-btn-soft-*` |
|
|
146
|
+
| Inputs | `.pu-input/-invalid/-valid`, `.pu-label/-required`, `.pu-hint`, `.pu-error`, `.pu-checkbox` |
|
|
147
|
+
| Cards | `.pu-card`, `.pu-card-body`, `.pu-panel-header`, `.pu-panel-title`, `.pu-panel-description` |
|
|
148
|
+
| Tables | `.pu-table-wrapper`, `.pu-table`, `-header`, `-header-cell`, `-body-row`, `-body-row-selected`, `-body-cell`, `.pu-selection-cell` |
|
|
149
|
+
| Toolbars / empty states | `.pu-toolbar`, `-text`, `-actions`; `.pu-empty-state`, `-icon`, `-title`, `-description` |
|
|
331
150
|
|
|
332
|
-
|
|
151
|
+
Full catalog: [Reference › UI › Assets › Component classes](/reference/ui/assets#component-classes-pu-).
|
|
333
152
|
|
|
334
|
-
|
|
335
|
-
/* app/assets/stylesheets/application.tailwind.css */
|
|
336
|
-
@import "gem:plutonium/src/css/plutonium.css";
|
|
337
|
-
|
|
338
|
-
@import "tailwindcss";
|
|
339
|
-
@config '../../../tailwind.config.js';
|
|
153
|
+
## Migrating from hardcoded classes
|
|
340
154
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
155
|
+
| Old | New |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `text-gray-900 dark:text-white` | `text-[var(--pu-text)]` |
|
|
158
|
+
| `text-gray-500 dark:text-gray-400` | `text-[var(--pu-text-muted)]` |
|
|
159
|
+
| `bg-gray-50 dark:bg-gray-700` | `bg-[var(--pu-surface)]` |
|
|
160
|
+
| `border-gray-300 dark:border-gray-600` | `border-[var(--pu-border)]` |
|
|
161
|
+
| Long input class chain | `pu-input` |
|
|
162
|
+
| Long button class chain | `pu-btn pu-btn-md pu-btn-primary` |
|
|
345
163
|
|
|
346
|
-
|
|
164
|
+
## Per-resource theming (Phlexi themes)
|
|
347
165
|
|
|
348
|
-
|
|
166
|
+
Override Form/Display/Table appearance per resource via a nested `Theme` class:
|
|
349
167
|
|
|
350
168
|
```ruby
|
|
351
169
|
class PostDefinition < ResourceDefinition
|
|
352
170
|
class Form < Form
|
|
353
171
|
class Theme < Plutonium::UI::Form::Theme
|
|
354
172
|
def self.theme
|
|
355
|
-
super.merge(
|
|
356
|
-
base:
|
|
357
|
-
fields_wrapper:
|
|
173
|
+
super.merge(
|
|
174
|
+
base: "bg-[var(--pu-card-bg)] shadow-md rounded-lg p-6",
|
|
175
|
+
fields_wrapper: "grid grid-cols-2 gap-6",
|
|
358
176
|
actions_wrapper: "flex justify-end mt-6 space-x-2",
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
error: "pu-error",
|
|
363
|
-
button: "pu-btn pu-btn-md pu-btn-primary",
|
|
364
|
-
})
|
|
177
|
+
input: "pu-input",
|
|
178
|
+
button: "pu-btn pu-btn-md pu-btn-primary"
|
|
179
|
+
)
|
|
365
180
|
end
|
|
366
181
|
end
|
|
367
182
|
end
|
|
368
183
|
end
|
|
369
184
|
```
|
|
370
185
|
|
|
371
|
-
|
|
186
|
+
::: warning Always `super.merge(...)`
|
|
187
|
+
Don't replace the theme wholesale — Plutonium's defaults handle invalid states, focus rings, and dark mode. `super.merge` keeps them.
|
|
188
|
+
:::
|
|
372
189
|
|
|
373
|
-
|
|
374
|
-
class PostDefinition < ResourceDefinition
|
|
375
|
-
class Display < Display
|
|
376
|
-
class Theme < Plutonium::UI::Display::Theme
|
|
377
|
-
def self.theme
|
|
378
|
-
super.merge({
|
|
379
|
-
fields_wrapper: "grid grid-cols-3 gap-8",
|
|
380
|
-
label: "text-sm font-bold text-[var(--pu-text-muted)] mb-1",
|
|
381
|
-
string: "text-lg text-[var(--pu-text)]",
|
|
382
|
-
link: "text-primary-600 hover:underline",
|
|
383
|
-
})
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
```
|
|
190
|
+
Full theme key catalog: [Reference › UI › Assets › Phlexi component themes](/reference/ui/assets#phlexi-component-themes).
|
|
389
191
|
|
|
390
|
-
|
|
192
|
+
## Typography
|
|
391
193
|
|
|
392
|
-
|
|
393
|
-
class PostDefinition < ResourceDefinition
|
|
394
|
-
class Table < Table
|
|
395
|
-
class Theme < Plutonium::UI::Table::Theme
|
|
396
|
-
def self.theme
|
|
397
|
-
super.merge({
|
|
398
|
-
wrapper: "pu-table-wrapper",
|
|
399
|
-
base: "pu-table",
|
|
400
|
-
header: "pu-table-header",
|
|
401
|
-
header_cell: "pu-table-header-cell",
|
|
402
|
-
body_row: "pu-table-body-row",
|
|
403
|
-
body_cell: "pu-table-body-cell",
|
|
404
|
-
})
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## Branding
|
|
412
|
-
|
|
413
|
-
### Application Name
|
|
414
|
-
|
|
415
|
-
```ruby
|
|
416
|
-
# config/initializers/plutonium.rb
|
|
417
|
-
Plutonium.application_name = "My Application"
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
### Custom Logo
|
|
421
|
-
|
|
422
|
-
Override the logo in your layout:
|
|
423
|
-
|
|
424
|
-
```ruby
|
|
425
|
-
# packages/admin_portal/app/views/layouts/admin_portal/application.rb
|
|
426
|
-
module AdminPortal
|
|
427
|
-
class ApplicationLayout < Plutonium::UI::Layout::Application
|
|
428
|
-
def render_logo
|
|
429
|
-
img(src: helpers.asset_path("logo.svg"), alt: "My App", class: "h-8")
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
end
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
### Custom Fonts
|
|
436
|
-
|
|
437
|
-
Override in your layout:
|
|
194
|
+
Default font: Lato. Override via the layout:
|
|
438
195
|
|
|
439
196
|
```ruby
|
|
440
197
|
class MyLayout < Plutonium::UI::Layout::ResourceLayout
|
|
@@ -445,73 +202,79 @@ class MyLayout < Plutonium::UI::Layout::ResourceLayout
|
|
|
445
202
|
end
|
|
446
203
|
```
|
|
447
204
|
|
|
448
|
-
|
|
205
|
+
Then configure Tailwind to use it:
|
|
449
206
|
|
|
450
207
|
```javascript
|
|
451
|
-
theme: {
|
|
208
|
+
theme: plutoniumTailwindConfig.merge(plutoniumTailwindConfig.theme, {
|
|
452
209
|
fontFamily: {
|
|
453
|
-
|
|
454
|
-
|
|
210
|
+
body: ['Inter', 'sans-serif'],
|
|
211
|
+
sans: ['Inter', 'sans-serif']
|
|
455
212
|
}
|
|
456
|
-
}
|
|
213
|
+
})
|
|
457
214
|
```
|
|
458
215
|
|
|
459
|
-
## Dark
|
|
216
|
+
## Dark mode
|
|
217
|
+
|
|
218
|
+
`selector` strategy. The bundled `color-mode` Stimulus controller handles toggling; Plutonium ships a switcher in the topbar.
|
|
460
219
|
|
|
461
|
-
|
|
220
|
+
Manual toggle:
|
|
462
221
|
|
|
463
222
|
```javascript
|
|
464
|
-
document.documentElement.classList.toggle('dark')
|
|
223
|
+
document.documentElement.classList.toggle('dark')
|
|
465
224
|
```
|
|
466
225
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
Design tokens automatically adapt to dark mode - override the `.dark` selector in your CSS to customize dark mode colors.
|
|
226
|
+
If you've overridden tokens via `:root` and `.dark`, both modes Just Work.
|
|
470
227
|
|
|
471
|
-
##
|
|
228
|
+
## Per-portal chrome — eject the shell
|
|
472
229
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
```javascript
|
|
476
|
-
// app/javascript/controllers/index.js
|
|
477
|
-
import { application } from "./application"
|
|
478
|
-
|
|
479
|
-
import { registerControllers } from "@radioactive-labs/plutonium"
|
|
480
|
-
registerControllers(application)
|
|
230
|
+
For per-portal headers/sidebars:
|
|
481
231
|
|
|
482
|
-
|
|
232
|
+
```bash
|
|
233
|
+
rails generate pu:eject:shell --dest=admin_portal
|
|
483
234
|
```
|
|
484
235
|
|
|
485
|
-
|
|
236
|
+
Copies `_resource_header.html.erb` and `_resource_sidebar.html.erb` into the portal's `app/views/plutonium/`. Edit directly.
|
|
486
237
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
- `slim-select` - Enhanced select boxes
|
|
491
|
-
- `flatpickr` - Date/time pickers
|
|
492
|
-
- `easymde` - Markdown editor
|
|
238
|
+
```bash
|
|
239
|
+
rails generate pu:eject:layout
|
|
240
|
+
```
|
|
493
241
|
|
|
494
|
-
|
|
242
|
+
Copies `layouts/resource.html.erb` for layout-level edits.
|
|
495
243
|
|
|
496
|
-
|
|
497
|
-
// app/javascript/controllers/custom_controller.js
|
|
498
|
-
import { Controller } from "@hotwired/stimulus"
|
|
244
|
+
## Shell config
|
|
499
245
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
246
|
+
```ruby
|
|
247
|
+
Plutonium.configure do |config|
|
|
248
|
+
config.shell = :modern # default — topbar + icon rail
|
|
249
|
+
# config.shell = :classic # legacy header + sidebar (only when upgrading)
|
|
250
|
+
end
|
|
505
251
|
```
|
|
506
252
|
|
|
507
|
-
|
|
253
|
+
## Stimulus
|
|
508
254
|
|
|
509
255
|
```javascript
|
|
256
|
+
// app/javascript/controllers/index.js
|
|
257
|
+
import { application } from "./application"
|
|
258
|
+
import { registerControllers } from "@radioactive-labs/plutonium"
|
|
259
|
+
|
|
260
|
+
registerControllers(application) // ← mandatory
|
|
261
|
+
|
|
262
|
+
// Your custom controllers...
|
|
510
263
|
import CustomController from "./custom_controller"
|
|
511
264
|
application.register("custom", CustomController)
|
|
512
265
|
```
|
|
513
266
|
|
|
267
|
+
Bundled controllers: `color-mode`, `form` (pre-submit), `nested-resource-form-fields`, `slim-select`, `flatpickr`, `easymde`.
|
|
268
|
+
|
|
269
|
+
## Common issues
|
|
270
|
+
|
|
271
|
+
- **Stimulus controllers silently fail** — if `registerControllers(application)` isn't called, the entire UI's interactive layer is dead (color-mode toggle, slim-select, flatpickr, easymde, pre-submit). No error — just no behavior.
|
|
272
|
+
- **`plutoniumTailwindConfig.merge` is mandatory** — plain spread drops defaults silently.
|
|
273
|
+
- **Tokens not switching in dark mode** — you used `bg-pu-surface` instead of `bg-[var(--pu-surface)]`. Tokens are CSS variables, not Tailwind keys.
|
|
274
|
+
- **`.pu-btn` styles not applying** — check that Plutonium CSS is imported BEFORE Tailwind: `@import "gem:plutonium/src/css/plutonium.css";` then `@import "tailwindcss";`.
|
|
275
|
+
|
|
514
276
|
## Related
|
|
515
277
|
|
|
516
|
-
- [
|
|
517
|
-
- [
|
|
278
|
+
- [Reference › UI › Assets](/reference/ui/assets) — full Tailwind / Stimulus / design tokens / component classes surface
|
|
279
|
+
- [Reference › UI › Layouts](/reference/ui/layouts) — shell, eject, ResourceLayout
|
|
280
|
+
- [Reference › UI › Forms › Theming](/reference/ui/forms#theming) — Form theme keys
|