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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +574 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +167 -302
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +674 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +9 -6
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +44 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1010 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +38 -29
  18. data/docs/.vitepress/theme/components/HomeAudienceSplit.vue +53 -0
  19. data/docs/.vitepress/theme/components/HomeCta.vue +108 -0
  20. data/docs/.vitepress/theme/components/HomeHero.vue +70 -0
  21. data/docs/.vitepress/theme/components/HomeInTheBox.vue +74 -0
  22. data/docs/.vitepress/theme/components/HomePillars.vue +42 -0
  23. data/docs/.vitepress/theme/components/HomeStopWriting.vue +49 -0
  24. data/docs/.vitepress/theme/components/HomeWalkthrough.vue +111 -0
  25. data/docs/.vitepress/theme/components/SectionLanding.vue +115 -0
  26. data/docs/.vitepress/theme/custom.css +144 -0
  27. data/docs/.vitepress/theme/index.ts +58 -1
  28. data/docs/getting-started/index.md +33 -57
  29. data/docs/getting-started/installation.md +37 -80
  30. data/docs/getting-started/tutorial/02-first-resource.md +17 -8
  31. data/docs/getting-started/tutorial/03-authentication.md +31 -23
  32. data/docs/getting-started/tutorial/05-custom-actions.md +9 -4
  33. data/docs/getting-started/tutorial/06-nested-resources.md +7 -1
  34. data/docs/getting-started/tutorial/07-author-portal.md +8 -0
  35. data/docs/getting-started/tutorial/08-customizing-ui.md +4 -0
  36. data/docs/getting-started/tutorial/index.md +4 -5
  37. data/docs/guides/adding-resources.md +66 -377
  38. data/docs/guides/authentication.md +98 -462
  39. data/docs/guides/authorization.md +124 -370
  40. data/docs/guides/creating-packages.md +93 -298
  41. data/docs/guides/custom-actions.md +126 -441
  42. data/docs/guides/customizing-ui.md +258 -0
  43. data/docs/guides/index.md +49 -52
  44. data/docs/guides/multi-tenancy.md +123 -186
  45. data/docs/guides/nested-resources.md +137 -396
  46. data/docs/guides/search-filtering.md +127 -238
  47. data/docs/guides/testing.md +10 -5
  48. data/docs/guides/theming.md +168 -405
  49. data/docs/guides/troubleshooting.md +5 -3
  50. data/docs/guides/user-invites.md +112 -425
  51. data/docs/guides/user-profile.md +82 -241
  52. data/docs/index.md +10 -219
  53. data/docs/public/asciinema/home-scaffold.cast +305 -0
  54. data/docs/public/images/guides/custom-actions-bulk.png +0 -0
  55. data/docs/public/images/guides/multi-tenancy-dashboard.png +0 -0
  56. data/docs/public/images/guides/multi-tenancy-welcome.png +0 -0
  57. data/docs/public/images/guides/nested-inputs.png +0 -0
  58. data/docs/public/images/guides/nested-resources-tab.png +0 -0
  59. data/docs/public/images/guides/search-filtering-index.png +0 -0
  60. data/docs/public/images/guides/search-filtering-panel.png +0 -0
  61. data/docs/public/images/guides/theming-after.png +0 -0
  62. data/docs/public/images/guides/theming-before.png +0 -0
  63. data/docs/public/images/guides/user-invites-landing.png +0 -0
  64. data/docs/public/images/guides/user-profile-edit.png +0 -0
  65. data/docs/public/images/guides/user-profile-show.png +0 -0
  66. data/docs/public/images/home-index.png +0 -0
  67. data/docs/public/images/home-new.png +0 -0
  68. data/docs/public/images/home-show.png +0 -0
  69. data/docs/public/images/tutorial/02-empty-index.png +0 -0
  70. data/docs/public/images/tutorial/02-index-with-posts.png +0 -0
  71. data/docs/public/images/tutorial/02-new-form-modal.png +0 -0
  72. data/docs/public/images/tutorial/02-new-form.png +0 -0
  73. data/docs/public/images/tutorial/03-create-account.png +0 -0
  74. data/docs/public/images/tutorial/03-login.png +0 -0
  75. data/docs/public/images/tutorial/04-admin-index.png +0 -0
  76. data/docs/public/images/tutorial/05-actions-menu.png +0 -0
  77. data/docs/public/images/tutorial/05-row-actions.png +0 -0
  78. data/docs/public/images/tutorial/06-comments-tab.png +0 -0
  79. data/docs/public/images/tutorial/06-post-with-comments.png +0 -0
  80. data/docs/public/images/tutorial/07-author-dashboard.png +0 -0
  81. data/docs/public/images/tutorial/07-author-portal.png +0 -0
  82. data/docs/public/images/tutorial/08-customized-index.png +0 -0
  83. data/docs/reference/app/generators.md +517 -0
  84. data/docs/reference/app/index.md +158 -0
  85. data/docs/reference/app/packages.md +146 -0
  86. data/docs/reference/app/portals.md +377 -0
  87. data/docs/reference/auth/accounts.md +229 -0
  88. data/docs/reference/auth/index.md +88 -0
  89. data/docs/reference/auth/profile.md +185 -0
  90. data/docs/reference/behavior/controllers.md +395 -0
  91. data/docs/reference/behavior/index.md +22 -0
  92. data/docs/reference/behavior/interactions.md +341 -0
  93. data/docs/reference/behavior/policies.md +417 -0
  94. data/docs/reference/index.md +67 -48
  95. data/docs/reference/resource/actions.md +423 -0
  96. data/docs/reference/resource/definition.md +508 -0
  97. data/docs/reference/resource/index.md +50 -0
  98. data/docs/reference/resource/model.md +348 -0
  99. data/docs/reference/resource/query.md +305 -0
  100. data/docs/reference/tenancy/entity-scoping.md +368 -0
  101. data/docs/reference/tenancy/index.md +36 -0
  102. data/docs/reference/tenancy/invites.md +400 -0
  103. data/docs/reference/tenancy/nested-resources.md +267 -0
  104. data/docs/reference/testing/index.md +287 -0
  105. data/docs/reference/ui/assets.md +400 -0
  106. data/docs/reference/ui/components.md +165 -0
  107. data/docs/reference/ui/displays.md +104 -0
  108. data/docs/reference/ui/forms.md +284 -0
  109. data/docs/reference/ui/index.md +30 -0
  110. data/docs/reference/ui/layouts.md +106 -0
  111. data/docs/reference/ui/pages.md +189 -0
  112. data/docs/reference/ui/tables.md +121 -0
  113. data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md +1648 -0
  114. data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md.tasks.json +109 -0
  115. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  116. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  117. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  118. data/docs/superpowers/specs/2026-05-15-public-pages-overhaul-design.md +263 -0
  119. data/gemfiles/rails_7.gemfile.lock +1 -1
  120. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  121. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  122. data/lib/generators/pu/core/assets/assets_generator.rb +10 -0
  123. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  124. data/lib/generators/pu/invites/install_generator.rb +45 -0
  125. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +1 -0
  126. data/lib/generators/pu/profile/conn_generator.rb +2 -2
  127. data/lib/generators/pu/res/conn/conn_generator.rb +33 -6
  128. data/lib/generators/pu/res/model/templates/model.rb.tt +4 -0
  129. data/lib/generators/pu/rodauth/account_generator.rb +2 -1
  130. data/lib/generators/pu/rodauth/admin_generator.rb +0 -2
  131. data/lib/generators/pu/rodauth/migration_generator.rb +0 -2
  132. data/lib/generators/pu/rodauth/views_generator.rb +0 -2
  133. data/lib/generators/pu/saas/membership/USAGE +4 -1
  134. data/lib/generators/pu/saas/setup_generator.rb +16 -4
  135. data/lib/generators/pu/saas/welcome/templates/app/controllers/welcome_controller.rb.tt +1 -1
  136. data/lib/plutonium/definition/base.rb +1 -1
  137. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  138. data/lib/plutonium/helpers/turbo_helper.rb +30 -0
  139. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  140. data/lib/plutonium/resource/controller.rb +1 -0
  141. data/lib/plutonium/resource/controllers/crud_actions.rb +23 -5
  142. data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
  143. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  144. data/lib/plutonium/resource/policy.rb +7 -0
  145. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  146. data/lib/plutonium/ui/component/methods.rb +5 -0
  147. data/lib/plutonium/ui/form/base.rb +23 -3
  148. data/lib/plutonium/ui/form/components/json.rb +58 -0
  149. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  150. data/lib/plutonium/ui/form/components/secure_association.rb +103 -22
  151. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  152. data/lib/plutonium/ui/form/interaction.rb +1 -1
  153. data/lib/plutonium/ui/form/resource.rb +0 -4
  154. data/lib/plutonium/ui/form/theme.rb +1 -1
  155. data/lib/plutonium/ui/grid/resource.rb +1 -1
  156. data/lib/plutonium/ui/layout/base.rb +1 -0
  157. data/lib/plutonium/ui/page/base.rb +0 -7
  158. data/lib/plutonium/ui/page/edit.rb +1 -1
  159. data/lib/plutonium/ui/page/index.rb +4 -4
  160. data/lib/plutonium/ui/page/new.rb +1 -1
  161. data/lib/plutonium/ui/table/components/filter_form.rb +12 -4
  162. data/lib/plutonium/ui/table/resource.rb +1 -1
  163. data/lib/plutonium/version.rb +1 -1
  164. data/lib/plutonium.rb +8 -0
  165. data/lib/tasks/release.rake +15 -1
  166. data/package.json +13 -10
  167. data/src/css/slim_select.css +4 -0
  168. data/src/js/controllers/form_controller.js +5 -4
  169. data/src/js/controllers/slim_select_controller.js +61 -0
  170. data/src/js/turbo/turbo_actions.js +33 -0
  171. data/yarn.lock +661 -544
  172. metadata +86 -33
  173. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  174. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  175. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  176. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  177. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  178. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  179. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  180. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  181. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  182. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  183. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  184. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  185. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  186. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  187. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  188. data/docs/reference/assets/index.md +0 -496
  189. data/docs/reference/controller/index.md +0 -412
  190. data/docs/reference/definition/actions.md +0 -462
  191. data/docs/reference/definition/fields.md +0 -383
  192. data/docs/reference/definition/index.md +0 -326
  193. data/docs/reference/definition/query.md +0 -351
  194. data/docs/reference/generators/index.md +0 -648
  195. data/docs/reference/interaction/index.md +0 -449
  196. data/docs/reference/model/features.md +0 -248
  197. data/docs/reference/model/index.md +0 -218
  198. data/docs/reference/policy/index.md +0 -456
  199. data/docs/reference/portal/index.md +0 -379
  200. data/docs/reference/views/forms.md +0 -411
  201. data/docs/reference/views/index.md +0 -544
@@ -1,440 +1,197 @@
1
1
  # Theming
2
2
 
3
- This guide covers customizing colors, styles, and branding.
3
+ Customize colors, styles, dark mode, and branding.
4
4
 
5
- ## Overview
5
+ ## Goal
6
6
 
7
- Plutonium uses TailwindCSS 4 with a design token system for consistent styling. Customization happens through:
7
+ Adapt Plutonium's defaults to match your brand: primary color, fonts, logo, dark mode behavior, optionally per-component theming.
8
8
 
9
- - **Design Tokens** - CSS custom properties for colors, spacing, shadows
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
- ## Design Token System
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
- Plutonium uses CSS custom properties (design tokens) for theming. These tokens automatically adapt to light and dark modes.
19
+ ## 🚨 Critical
17
20
 
18
- ### Available Tokens
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
- #### Surface Colors
27
+ ## Step 1: Run the assets generator
21
28
 
22
- ```css
23
- --pu-body /* Page background */
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
- #### Form Tokens
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
- ```css
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
- #### Card Tokens
37
+ ```ruby
38
+ # config/initializers/plutonium.rb
39
+ Plutonium.configure do |config|
40
+ config.load_defaults 1.0
67
41
 
68
- ```css
69
- --pu-card-bg /* Card background */
70
- --pu-card-border /* Card border */
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
- #### Shadow System
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
- #### Spacing Scale
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
- ```css
84
- --pu-space-xs /* 0.25rem */
85
- --pu-space-sm /* 0.5rem */
86
- --pu-space-md /* 1rem */
87
- --pu-space-lg /* 1.5rem */
88
- --pu-space-xl /* 2rem */
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
- #### Border Radius
64
+ ## Step 3: Customize colors via Tailwind
92
65
 
93
- ```css
94
- --pu-radius-sm /* 0.375rem */
95
- --pu-radius-md /* 0.5rem */
96
- --pu-radius-lg /* 0.75rem */
97
- --pu-radius-xl /* 1rem */
98
- --pu-radius-full /* 9999px */
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
- #### Transitions
78
+ ### Default palette
102
79
 
103
- ```css
104
- --pu-transition-fast /* 150ms */
105
- --pu-transition-normal /* 200ms */
106
- --pu-transition-slow /* 300ms */
107
- ```
108
-
109
- ### Overriding Tokens
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
- Override tokens in your CSS to customize the theme:
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: #d4d4d4;
122
- --pu-input-focus-ring: #6366f1;
99
+ --pu-border: #d1d5db;
123
100
  }
124
101
 
125
- /* Dark mode overrides */
126
102
  .dark {
127
- --pu-surface: #18181b;
128
- --pu-border: #3f3f46;
103
+ --pu-surface: #111827;
104
+ --pu-border: #374151;
129
105
  }
130
106
  ```
131
107
 
132
- ## Component Classes
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
- ```html
184
- <input type="checkbox" class="pu-checkbox">
185
- ```
110
+ ## Using tokens in your code
186
111
 
187
- ### Cards
112
+ ```erb
113
+ <h1 class="text-[var(--pu-text)]">Title</h1>
114
+ <p class="text-[var(--pu-text-muted)]">Description</p>
188
115
 
189
- ```html
190
- <div class="pu-card">
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
- # config/initializers/plutonium.rb
247
- Plutonium.configure do |config|
248
- config.load_defaults 1.0
249
-
250
- config.assets.stylesheet = "application" # Your CSS file
251
- config.assets.script = "application" # Your JS file
252
- config.assets.logo = "my_logo.png" # Logo image
253
- config.assets.favicon = "my_favicon.ico" # Favicon
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
- ## TailwindCSS Configuration
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
- Override Plutonium's color palette:
137
+ Pre-styled ready-to-use components:
289
138
 
290
- ```javascript
291
- // tailwind.config.js
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
- ### Semantic Colors
317
-
318
- Plutonium includes these semantic colors:
319
-
320
- | Color | Usage |
321
- |-------|-------|
322
- | `primary` | Primary brand color |
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
- ### Application Stylesheet
151
+ Full catalog: [Reference › UI › Assets › Component classes](/reference/ui/assets#component-classes-pu-).
333
152
 
334
- ```css
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
- /* Your custom styles and token overrides */
342
- ```
343
-
344
- ## Component Themes
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
- Plutonium components use a theme system. Override themes by defining nested Theme classes in your definitions.
164
+ ## Per-resource theming (Phlexi themes)
347
165
 
348
- ### Form Theme
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: "pu-card my-4 p-8 space-y-8",
357
- fields_wrapper: "grid grid-cols-2 gap-6",
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
- label: "pu-label",
360
- input: "pu-input",
361
- hint: "pu-hint",
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
- ### Display Theme
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
- ```ruby
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
- ### Table Theme
192
+ ## Typography
391
193
 
392
- ```ruby
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
- Update Tailwind config:
205
+ Then configure Tailwind to use it:
449
206
 
450
207
  ```javascript
451
- theme: {
208
+ theme: plutoniumTailwindConfig.merge(plutoniumTailwindConfig.theme, {
452
209
  fontFamily: {
453
- 'body': ['Inter', 'sans-serif'],
454
- 'sans': ['Inter', 'sans-serif'],
210
+ body: ['Inter', 'sans-serif'],
211
+ sans: ['Inter', 'sans-serif']
455
212
  }
456
- }
213
+ })
457
214
  ```
458
215
 
459
- ## Dark Mode
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
- Plutonium uses `selector` strategy for dark mode. Toggle by adding/removing the `dark` class on `<html>`:
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
- Plutonium includes a color mode selector component that handles this automatically.
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
- ## Stimulus Controllers
228
+ ## Per-portal chrome — eject the shell
472
229
 
473
- Register Plutonium's Stimulus controllers in your application:
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
- // Your custom controllers...
232
+ ```bash
233
+ rails generate pu:eject:shell --dest=admin_portal
483
234
  ```
484
235
 
485
- ### Available Controllers
236
+ Copies `_resource_header.html.erb` and `_resource_sidebar.html.erb` into the portal's `app/views/plutonium/`. Edit directly.
486
237
 
487
- - `color-mode` - Dark/light mode toggle
488
- - `form` - Form handling
489
- - `nested-resource-form-fields` - Nested form management
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
- ### Custom Controllers
242
+ Copies `layouts/resource.html.erb` for layout-level edits.
495
243
 
496
- ```javascript
497
- // app/javascript/controllers/custom_controller.js
498
- import { Controller } from "@hotwired/stimulus"
244
+ ## Shell config
499
245
 
500
- export default class extends Controller {
501
- connect() {
502
- console.log("Connected")
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
- Register in your index:
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
- - [Custom Actions](./custom-actions)
517
- - [Creating Packages](./creating-packages)
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