plutonium 0.28.0 → 0.30.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/CHANGELOG.md +16 -0
- data/app/assets/plutonium.css +2 -2
- data/app/assets/plutonium.js +13 -3
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +1 -1
- data/app/assets/plutonium.min.js.map +2 -2
- data/docs/guide/theming.md +431 -0
- data/lib/plutonium/core/controller.rb +5 -0
- data/lib/plutonium/interaction/response/redirect.rb +8 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +1 -0
- data/lib/plutonium/ui/action_button.rb +11 -5
- data/lib/plutonium/ui/block.rb +4 -1
- data/lib/plutonium/ui/breadcrumbs.rb +10 -8
- data/lib/plutonium/ui/color_mode_selector.rb +2 -2
- data/lib/plutonium/ui/component/behaviour.rb +8 -0
- data/lib/plutonium/ui/component/kit.rb +1 -1
- data/lib/plutonium/ui/component/theme.rb +47 -0
- data/lib/plutonium/ui/display/components/attachment.rb +2 -2
- data/lib/plutonium/ui/display/theme.rb +16 -16
- data/lib/plutonium/ui/empty_card.rb +5 -2
- data/lib/plutonium/ui/form/components/key_value_store.rb +11 -11
- data/lib/plutonium/ui/form/components/secure_association.rb +2 -2
- data/lib/plutonium/ui/form/components/uppy.rb +5 -5
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +5 -5
- data/lib/plutonium/ui/form/query.rb +11 -11
- data/lib/plutonium/ui/form/resource.rb +2 -2
- data/lib/plutonium/ui/form/theme.rb +17 -17
- data/lib/plutonium/ui/layout/base.rb +2 -2
- data/lib/plutonium/ui/layout/header.rb +10 -7
- data/lib/plutonium/ui/layout/rodauth_layout.rb +5 -5
- data/lib/plutonium/ui/layout/sidebar.rb +2 -2
- data/lib/plutonium/ui/nav_grid_menu.rb +6 -6
- data/lib/plutonium/ui/nav_user.rb +8 -7
- data/lib/plutonium/ui/page/interactive_action.rb +4 -4
- data/lib/plutonium/ui/page_header.rb +7 -4
- data/lib/plutonium/ui/panel.rb +3 -3
- data/lib/plutonium/ui/sidebar_menu.rb +12 -12
- data/lib/plutonium/ui/skeleton_table.rb +8 -8
- data/lib/plutonium/ui/tab_list.rb +5 -3
- data/lib/plutonium/ui/table/components/attachment.rb +2 -2
- data/lib/plutonium/ui/table/components/pagy_info.rb +3 -3
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +3 -3
- data/lib/plutonium/ui/table/components/scopes_bar.rb +14 -14
- data/lib/plutonium/ui/table/display_theme.rb +3 -3
- data/lib/plutonium/ui/table/resource.rb +2 -2
- data/lib/plutonium/ui/table/theme.rb +13 -13
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +48 -5
- data/package.json +1 -1
- data/src/css/core.css +2 -2
- data/src/css/easymde.css +8 -8
- data/src/css/intl_tel_input.css +7 -7
- data/src/css/slim_select.css +5 -5
- data/src/js/controllers/color_mode_controller.js +19 -3
- data/tailwind.options.js +75 -47
- metadata +4 -2
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Theming Guide
|
|
2
|
+
|
|
3
|
+
Plutonium uses a semantic design token system built on Tailwind CSS, making it easy to customize the appearance of your application while maintaining design consistency.
|
|
4
|
+
|
|
5
|
+
## Design Philosophy
|
|
6
|
+
|
|
7
|
+
Plutonium's theming system is built on these principles:
|
|
8
|
+
|
|
9
|
+
- **Semantic tokens** - Colors and spacing have meaningful names (e.g., `surface`, `elevated`) rather than generic values
|
|
10
|
+
- **Cross-property consistency** - The same size name means the same value across all utilities (`p-md` = `gap-md` = `my-md`)
|
|
11
|
+
- **Minimal, modern aesthetic** - Subtle corners, clean lines, and restrained use of shadows
|
|
12
|
+
- **Easy customization** - Override tokens in your Tailwind config to theme the entire application
|
|
13
|
+
|
|
14
|
+
## Semantic Design Tokens
|
|
15
|
+
|
|
16
|
+
### Spacing Scale
|
|
17
|
+
|
|
18
|
+
Plutonium extends Tailwind's spacing scale with semantic values:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
spacing: {
|
|
22
|
+
'xs': '0.5rem', // 8px - extra small spacing
|
|
23
|
+
'sm': '0.75rem', // 12px - small spacing (inputs, buttons, small gaps)
|
|
24
|
+
'md': '1rem', // 16px - medium spacing (cards, tabs, standard gaps)
|
|
25
|
+
'lg': '1.5rem', // 24px - large spacing (forms, displays, large spacing)
|
|
26
|
+
'xl': '2rem', // 32px - extra large spacing
|
|
27
|
+
'2xl': '2.5rem', // 40px - 2x extra large spacing
|
|
28
|
+
'3xl': '3rem', // 48px - 3x extra large spacing
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
These work across **all** spacing utilities:
|
|
33
|
+
- Padding: `p-md`, `px-sm`, `py-lg`
|
|
34
|
+
- Margin: `m-md`, `mx-sm`, `my-lg`
|
|
35
|
+
- Gap: `gap-md`, `gap-x-sm`, `gap-y-lg`
|
|
36
|
+
- Space: `space-x-md`, `space-y-sm`
|
|
37
|
+
|
|
38
|
+
### Background Colors
|
|
39
|
+
|
|
40
|
+
Semantic background colors provide consistent theming across light and dark modes:
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
colors: {
|
|
44
|
+
// Semantic background colors
|
|
45
|
+
surface: {
|
|
46
|
+
DEFAULT: '#ffffff', // Light mode: cards, forms, tables, panels
|
|
47
|
+
dark: '#1f2937', // Dark mode: gray-800
|
|
48
|
+
},
|
|
49
|
+
page: {
|
|
50
|
+
DEFAULT: 'rgb(248 248 248)', // Light mode: page background
|
|
51
|
+
dark: '#111827', // Dark mode: gray-900
|
|
52
|
+
},
|
|
53
|
+
elevated: {
|
|
54
|
+
DEFAULT: 'rgb(244 244 245)', // Light mode: subtle elevation
|
|
55
|
+
dark: '#374151', // Dark mode: gray-700
|
|
56
|
+
},
|
|
57
|
+
interactive: {
|
|
58
|
+
DEFAULT: 'rgb(244 244 245)', // Light mode: hover states
|
|
59
|
+
dark: '#374151', // Dark mode: gray-700
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Where each color is used:**
|
|
65
|
+
- `surface` - Cards, forms, tables, panels, modals
|
|
66
|
+
- `page` - Main page background
|
|
67
|
+
- `elevated` - Slightly elevated elements like dropdowns, selected items
|
|
68
|
+
- `interactive` - Hover and focus states
|
|
69
|
+
|
|
70
|
+
### Border Radius
|
|
71
|
+
|
|
72
|
+
Plutonium uses subtle border radius values for a modern, minimal look:
|
|
73
|
+
|
|
74
|
+
- `rounded-sm` (2px) - Most UI elements (buttons, inputs, cards)
|
|
75
|
+
- `rounded` (4px) - Slightly larger elements
|
|
76
|
+
- `rounded-lg` (8px) - Special cases needing more rounding
|
|
77
|
+
|
|
78
|
+
## Getting Started
|
|
79
|
+
|
|
80
|
+
By default, Plutonium is completely self-contained and uses its own bundled assets - you don't need to do anything to get started. The gem includes pre-compiled CSS and JavaScript that work out of the box.
|
|
81
|
+
|
|
82
|
+
### When to Integrate Assets
|
|
83
|
+
|
|
84
|
+
You only need to integrate Plutonium's assets into your application if you want to:
|
|
85
|
+
- Customize the theme (colors, spacing, fonts)
|
|
86
|
+
- Override component styles
|
|
87
|
+
- Extend Tailwind with your own utilities
|
|
88
|
+
|
|
89
|
+
To integrate Plutonium's assets with your Rails application, run:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
rails generate pu:core:assets
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This generator will:
|
|
96
|
+
- Install required npm packages (`@radioactive-labs/plutonium`, Tailwind CSS, PostCSS)
|
|
97
|
+
- Create a `tailwind.config.js` that loads Plutonium's theme configuration
|
|
98
|
+
- Configure your application to import Plutonium's CSS and register its Stimulus controllers
|
|
99
|
+
- Set up the build pipeline to compile everything together
|
|
100
|
+
|
|
101
|
+
After running the generator, you can customize the theme as described below.
|
|
102
|
+
|
|
103
|
+
## Customizing Your Theme
|
|
104
|
+
|
|
105
|
+
To customize Plutonium's theme, you need to extend the Tailwind configuration in your application (created by `rails generate pu:core:assets`):
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
// tailwind.config.js
|
|
109
|
+
const { execSync } = require('child_process');
|
|
110
|
+
const plutoniumGemPath = execSync("bundle show plutonium").toString().trim();
|
|
111
|
+
const plutoniumTailwindConfig = require(`${plutoniumGemPath}/tailwind.options.js`)
|
|
112
|
+
const tailwindPlugin = require('tailwindcss/plugin')
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
darkMode: 'class',
|
|
116
|
+
plugins: [
|
|
117
|
+
// Add your custom plugins here
|
|
118
|
+
].concat(plutoniumTailwindConfig.plugins.map(function (plugin) {
|
|
119
|
+
switch (typeof plugin) {
|
|
120
|
+
case "function":
|
|
121
|
+
return tailwindPlugin(plugin)
|
|
122
|
+
case "string":
|
|
123
|
+
return require(plugin)
|
|
124
|
+
default:
|
|
125
|
+
throw Error(`unsupported plugin: ${plugin}: ${(typeof plugin)}`)
|
|
126
|
+
}
|
|
127
|
+
})),
|
|
128
|
+
theme: plutoniumTailwindConfig.merge(
|
|
129
|
+
plutoniumTailwindConfig.theme,
|
|
130
|
+
{
|
|
131
|
+
extend: {
|
|
132
|
+
colors: {
|
|
133
|
+
// Override brand colors - example using blue
|
|
134
|
+
primary: {
|
|
135
|
+
50: '#eff6ff',
|
|
136
|
+
100: '#dbeafe',
|
|
137
|
+
200: '#bfdbfe',
|
|
138
|
+
300: '#93c5fd',
|
|
139
|
+
400: '#60a5fa',
|
|
140
|
+
500: '#3b82f6',
|
|
141
|
+
600: '#2563eb',
|
|
142
|
+
700: '#1d4ed8',
|
|
143
|
+
800: '#1e40af',
|
|
144
|
+
900: '#1e3a8a',
|
|
145
|
+
950: '#172554',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
),
|
|
151
|
+
content: [
|
|
152
|
+
`${__dirname}/app/**/*.{erb,haml,html,slim,rb}`,
|
|
153
|
+
`${__dirname}/app/javascript/**/*.js`,
|
|
154
|
+
].concat(plutoniumTailwindConfig.content),
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Dark Mode Customization
|
|
159
|
+
|
|
160
|
+
Customize just the dark mode colors:
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
theme: plutoniumTailwindConfig.merge(
|
|
164
|
+
plutoniumTailwindConfig.theme,
|
|
165
|
+
{
|
|
166
|
+
extend: {
|
|
167
|
+
colors: {
|
|
168
|
+
surface: {
|
|
169
|
+
dark: '#1e293b', // slate-800 for a cooler dark mode
|
|
170
|
+
},
|
|
171
|
+
page: {
|
|
172
|
+
dark: '#0f172a', // slate-900
|
|
173
|
+
},
|
|
174
|
+
elevated: {
|
|
175
|
+
dark: '#334155', // slate-700
|
|
176
|
+
},
|
|
177
|
+
interactive: {
|
|
178
|
+
dark: '#475569', // slate-600 for more contrast on hover
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Brand Color Customization
|
|
187
|
+
|
|
188
|
+
Replace Plutonium's turquoise/navy palette with your brand colors:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
theme: plutoniumTailwindConfig.merge(
|
|
192
|
+
plutoniumTailwindConfig.theme,
|
|
193
|
+
{
|
|
194
|
+
extend: {
|
|
195
|
+
colors: {
|
|
196
|
+
primary: {
|
|
197
|
+
// Your primary brand color scale
|
|
198
|
+
50: '#fef2f2',
|
|
199
|
+
100: '#fee2e2',
|
|
200
|
+
500: '#ef4444', // Your primary color
|
|
201
|
+
900: '#7f1d1d',
|
|
202
|
+
},
|
|
203
|
+
secondary: {
|
|
204
|
+
// Your secondary brand color scale
|
|
205
|
+
50: '#f0fdf4',
|
|
206
|
+
100: '#dcfce7',
|
|
207
|
+
500: '#22c55e', // Your secondary color
|
|
208
|
+
900: '#14532d',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Component-Level Theming
|
|
217
|
+
|
|
218
|
+
Every Plutonium component supports CSS class customization through the theme system. Component classes follow the pattern:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
pu-{component}[-{variant}][-{element}]
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Available Component Classes
|
|
225
|
+
|
|
226
|
+
**Forms:**
|
|
227
|
+
- `pu-form` - Form container
|
|
228
|
+
- `pu-form-input` - Text input fields
|
|
229
|
+
- `pu-form-label` - Input labels
|
|
230
|
+
- `pu-form-hint` - Help text below inputs
|
|
231
|
+
- `pu-form-error` - Validation error messages
|
|
232
|
+
- `pu-form-button` - Primary form button
|
|
233
|
+
- `pu-form-button_secondary` - Secondary form button
|
|
234
|
+
- `pu-form-fieldset` - Field grouping container
|
|
235
|
+
- `pu-form-fields` - Fields wrapper
|
|
236
|
+
|
|
237
|
+
**Tables:**
|
|
238
|
+
- `pu-table` - Table container
|
|
239
|
+
- `pu-table-wrapper` - Scrollable wrapper
|
|
240
|
+
- `pu-table-header` - Table header row
|
|
241
|
+
- `pu-table-header-cell` - Header cell
|
|
242
|
+
- `pu-table-body` - Table body
|
|
243
|
+
- `pu-table-row` - Table row
|
|
244
|
+
- `pu-table-cell` - Table cell
|
|
245
|
+
- `pu-table-footer` - Footer with pagination
|
|
246
|
+
|
|
247
|
+
**Display (Detail Views):**
|
|
248
|
+
- `pu-display` - Display container
|
|
249
|
+
- `pu-display-fields` - Fields grid wrapper
|
|
250
|
+
- `pu-display-field` - Individual field wrapper
|
|
251
|
+
- `pu-display-label` - Field label
|
|
252
|
+
- `pu-display-value` - Field value
|
|
253
|
+
|
|
254
|
+
**Layout:**
|
|
255
|
+
- `pu-layout-body` - Main layout body
|
|
256
|
+
- `pu-layout-main` - Main content area
|
|
257
|
+
- `pu-layout-header` - Page header
|
|
258
|
+
- `pu-layout-sidebar` - Sidebar navigation
|
|
259
|
+
|
|
260
|
+
**Navigation:**
|
|
261
|
+
- `pu-breadcrumbs` - Breadcrumb navigation
|
|
262
|
+
- `pu-nav-menu` - Navigation menu
|
|
263
|
+
- `pu-nav-user` - User menu dropdown
|
|
264
|
+
- `pu-sidebar-menu` - Sidebar menu items
|
|
265
|
+
|
|
266
|
+
**Panels:**
|
|
267
|
+
- `pu-panel` - Generic panel container
|
|
268
|
+
- `pu-panel-header` - Panel header
|
|
269
|
+
- `pu-panel-body` - Panel content
|
|
270
|
+
|
|
271
|
+
#### Customization Examples
|
|
272
|
+
|
|
273
|
+
```css
|
|
274
|
+
/* Custom form styling */
|
|
275
|
+
.pu-form {
|
|
276
|
+
@apply shadow-xl border-2;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.pu-form-input {
|
|
280
|
+
@apply text-lg rounded-lg;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.pu-form-error {
|
|
284
|
+
@apply text-sm font-semibold;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Custom table styling */
|
|
288
|
+
.pu-table-row:hover {
|
|
289
|
+
@apply bg-primary-50 dark:bg-primary-950;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.pu-table-header-cell {
|
|
293
|
+
@apply font-bold uppercase tracking-wider;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Custom display styling */
|
|
297
|
+
.pu-display-field {
|
|
298
|
+
@apply border-l-4 border-primary-500 pl-4;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.pu-display-label {
|
|
302
|
+
@apply text-xs uppercase tracking-wide text-gray-500;
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
You can also use Ruby to customize theme classes programmatically:
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
# In your resource controller or view
|
|
310
|
+
class Admin::UsersController < Plutonium::ResourceController
|
|
311
|
+
def self.form_theme
|
|
312
|
+
{
|
|
313
|
+
input: "pu-form-input w-full rounded-lg border-2 border-primary-300"
|
|
314
|
+
}
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Common Theming Scenarios
|
|
320
|
+
|
|
321
|
+
### Warmer Color Palette
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
theme: plutoniumTailwindConfig.merge(
|
|
325
|
+
plutoniumTailwindConfig.theme,
|
|
326
|
+
{
|
|
327
|
+
extend: {
|
|
328
|
+
colors: {
|
|
329
|
+
surface: {
|
|
330
|
+
DEFAULT: '#fefefe',
|
|
331
|
+
dark: '#1c1917', // stone-900
|
|
332
|
+
},
|
|
333
|
+
page: {
|
|
334
|
+
DEFAULT: '#fafaf9', // stone-50
|
|
335
|
+
dark: '#0c0a09', // stone-950
|
|
336
|
+
},
|
|
337
|
+
elevated: {
|
|
338
|
+
DEFAULT: '#f5f5f4', // stone-100
|
|
339
|
+
dark: '#292524', // stone-800
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
}
|
|
344
|
+
)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### High Contrast Mode
|
|
348
|
+
|
|
349
|
+
```javascript
|
|
350
|
+
theme: plutoniumTailwindConfig.merge(
|
|
351
|
+
plutoniumTailwindConfig.theme,
|
|
352
|
+
{
|
|
353
|
+
extend: {
|
|
354
|
+
colors: {
|
|
355
|
+
surface: {
|
|
356
|
+
DEFAULT: '#ffffff',
|
|
357
|
+
dark: '#000000', // Pure black
|
|
358
|
+
},
|
|
359
|
+
page: {
|
|
360
|
+
DEFAULT: '#fafafa',
|
|
361
|
+
dark: '#0a0a0a', // Almost black
|
|
362
|
+
},
|
|
363
|
+
elevated: {
|
|
364
|
+
DEFAULT: '#f0f0f0',
|
|
365
|
+
dark: '#1a1a1a',
|
|
366
|
+
},
|
|
367
|
+
interactive: {
|
|
368
|
+
DEFAULT: '#e5e5e5',
|
|
369
|
+
dark: '#2a2a2a',
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Monochrome Theme
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
theme: plutoniumTailwindConfig.merge(
|
|
381
|
+
plutoniumTailwindConfig.theme,
|
|
382
|
+
{
|
|
383
|
+
extend: {
|
|
384
|
+
colors: {
|
|
385
|
+
primary: {
|
|
386
|
+
50: '#fafafa',
|
|
387
|
+
100: '#f5f5f5',
|
|
388
|
+
500: '#737373',
|
|
389
|
+
900: '#171717',
|
|
390
|
+
},
|
|
391
|
+
secondary: {
|
|
392
|
+
50: '#f5f5f5',
|
|
393
|
+
100: '#e5e5e5',
|
|
394
|
+
500: '#525252',
|
|
395
|
+
900: '#0a0a0a',
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Tips and Best Practices
|
|
404
|
+
|
|
405
|
+
1. **Use semantic tokens** - Prefer `bg-surface` over `bg-white` so dark mode works automatically
|
|
406
|
+
2. **Test both modes** - Always verify your changes in both light and dark mode
|
|
407
|
+
3. **Maintain contrast** - Ensure text remains readable against your background colors
|
|
408
|
+
4. **Extend, don't replace** - Use the `merge` helper to extend rather than replace the theme
|
|
409
|
+
5. **Stay consistent** - Use the same spacing scale throughout your application
|
|
410
|
+
|
|
411
|
+
## Migration from Hardcoded Values
|
|
412
|
+
|
|
413
|
+
If you have existing components using hardcoded Tailwind values, migrate them to semantic tokens:
|
|
414
|
+
|
|
415
|
+
**Before:**
|
|
416
|
+
```ruby
|
|
417
|
+
div(class: "bg-white dark:bg-gray-800 p-4 rounded-lg")
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**After:**
|
|
421
|
+
```ruby
|
|
422
|
+
div(class: "bg-surface dark:bg-surface-dark p-md rounded-sm")
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
This ensures your components automatically adapt to theme changes.
|
|
426
|
+
|
|
427
|
+
## Further Resources
|
|
428
|
+
|
|
429
|
+
- [Tailwind CSS Customization](https://tailwindcss.com/docs/configuration)
|
|
430
|
+
- [Dark Mode Guide](https://tailwindcss.com/docs/dark-mode)
|
|
431
|
+
- [Color Palette Generator](https://uicolors.app/create)
|
|
@@ -100,6 +100,11 @@ module Plutonium
|
|
|
100
100
|
url_args[scoped_entity_param_key] = current_scoped_entity
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
# Preserve the request format unless explicitly specified
|
|
104
|
+
if !url_args.key?(:format) && request.present? && request.format.present? && request.format.symbol != :html
|
|
105
|
+
url_args[:format] = request.format.symbol
|
|
106
|
+
end
|
|
107
|
+
|
|
103
108
|
url_args
|
|
104
109
|
end
|
|
105
110
|
|
|
@@ -17,6 +17,14 @@ module Plutonium
|
|
|
17
17
|
redirect_options = @options
|
|
18
18
|
|
|
19
19
|
controller.instance_eval do
|
|
20
|
+
# Preserve the request format unless explicitly specified
|
|
21
|
+
url_options = redirect_args.last.is_a?(Hash) ? redirect_args.last : {}
|
|
22
|
+
if !url_options.key?(:format) && request.format.symbol != :html
|
|
23
|
+
url_options = url_options.merge(format: request.format.symbol)
|
|
24
|
+
redirect_args = [*redirect_args[0...-1], url_options] if redirect_args.last.is_a?(Hash)
|
|
25
|
+
redirect_args = [*redirect_args, url_options] unless redirect_args.last.is_a?(Hash)
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
url = url_for(*redirect_args)
|
|
21
29
|
|
|
22
30
|
respond_to do |format|
|
|
@@ -54,6 +54,7 @@ module Plutonium
|
|
|
54
54
|
notice: "#{resource_class.model_name.human} was successfully created."
|
|
55
55
|
end
|
|
56
56
|
format.any do
|
|
57
|
+
@current_policy = nil # Reset cached policy so it uses the instance instead of class
|
|
57
58
|
render :show,
|
|
58
59
|
status: :created,
|
|
59
60
|
location: redirect_url_after_submit
|
|
@@ -70,23 +70,29 @@ module Plutonium
|
|
|
70
70
|
base_classes,
|
|
71
71
|
color_classes,
|
|
72
72
|
size_classes,
|
|
73
|
-
-> { @action.icon && @variant != :table } => "space-x-
|
|
73
|
+
-> { @action.icon && @variant != :table } => "space-x-xs"
|
|
74
74
|
)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def base_classes
|
|
78
78
|
if @variant == :table
|
|
79
|
-
|
|
79
|
+
tokens(
|
|
80
|
+
theme_class(:button, variant: :table),
|
|
81
|
+
"inline-flex items-center justify-center py-xs px-sm rounded-sm focus:outline-none focus:ring-2"
|
|
82
|
+
)
|
|
80
83
|
else
|
|
81
|
-
|
|
84
|
+
tokens(
|
|
85
|
+
theme_class(:button),
|
|
86
|
+
"flex items-center justify-center px-md py-sm text-sm font-medium rounded-sm focus:outline-none focus:ring-4"
|
|
87
|
+
)
|
|
82
88
|
end
|
|
83
89
|
end
|
|
84
90
|
|
|
85
91
|
def icon_classes
|
|
86
92
|
if @variant == :table
|
|
87
|
-
"h-4 w-4 mr-
|
|
93
|
+
"h-4 w-4 mr-xs"
|
|
88
94
|
else
|
|
89
|
-
"h-3.5 w-3.5 -ml-
|
|
95
|
+
"h-3.5 w-3.5 -ml-xs"
|
|
90
96
|
end
|
|
91
97
|
end
|
|
92
98
|
|
data/lib/plutonium/ui/block.rb
CHANGED
|
@@ -4,7 +4,10 @@ module Plutonium
|
|
|
4
4
|
def view_template(&)
|
|
5
5
|
raise ArgumentError, "Block requires a content block" unless block_given?
|
|
6
6
|
|
|
7
|
-
div class:
|
|
7
|
+
div class: tokens(
|
|
8
|
+
theme_class(:block),
|
|
9
|
+
"relative bg-surface dark:bg-surface-dark shadow-md sm:rounded-sm my-sm"
|
|
10
|
+
) do
|
|
8
11
|
yield
|
|
9
12
|
end
|
|
10
13
|
end
|
|
@@ -6,13 +6,15 @@ module Plutonium
|
|
|
6
6
|
|
|
7
7
|
def view_template
|
|
8
8
|
nav(
|
|
9
|
-
class:
|
|
10
|
-
|
|
9
|
+
class: tokens(
|
|
10
|
+
theme_class(:breadcrumbs),
|
|
11
|
+
"flex py-sm text-gray-700 mb-sm"
|
|
12
|
+
),
|
|
11
13
|
aria_label: "Breadcrumb"
|
|
12
14
|
) do
|
|
13
15
|
ol(
|
|
14
16
|
class:
|
|
15
|
-
"inline-flex items-center space-x-
|
|
17
|
+
"inline-flex items-center space-x-xs md:space-x-sm rtl:space-x-reverse"
|
|
16
18
|
) do
|
|
17
19
|
# Dashboard
|
|
18
20
|
li(class: "inline-flex items-center") do
|
|
@@ -42,7 +44,7 @@ module Plutonium
|
|
|
42
44
|
# Parent Resource
|
|
43
45
|
li(class: "flex items-center") do
|
|
44
46
|
svg(
|
|
45
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-
|
|
47
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-xs text-gray-400",
|
|
46
48
|
aria_hidden: "true",
|
|
47
49
|
xmlns: "http://www.w3.org/2000/svg",
|
|
48
50
|
fill: "none",
|
|
@@ -65,7 +67,7 @@ module Plutonium
|
|
|
65
67
|
# Parent Itself
|
|
66
68
|
li(class: "flex items-center") do
|
|
67
69
|
svg(
|
|
68
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-
|
|
70
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-xs text-gray-400",
|
|
69
71
|
aria_hidden: "true",
|
|
70
72
|
xmlns: "http://www.w3.org/2000/svg",
|
|
71
73
|
fill: "none",
|
|
@@ -92,7 +94,7 @@ module Plutonium
|
|
|
92
94
|
# Record Resource
|
|
93
95
|
li(class: "flex items-center") do
|
|
94
96
|
svg(
|
|
95
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-
|
|
97
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-xs text-gray-400",
|
|
96
98
|
aria_hidden: "true",
|
|
97
99
|
xmlns: "http://www.w3.org/2000/svg",
|
|
98
100
|
fill: "none",
|
|
@@ -117,7 +119,7 @@ module Plutonium
|
|
|
117
119
|
if resource_record!.persisted? && action_name != "show"
|
|
118
120
|
li(class: "flex items-center") do
|
|
119
121
|
svg(
|
|
120
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-
|
|
122
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-xs text-gray-400",
|
|
121
123
|
aria_hidden: "true",
|
|
122
124
|
xmlns: "http://www.w3.org/2000/svg",
|
|
123
125
|
fill: "none",
|
|
@@ -142,7 +144,7 @@ module Plutonium
|
|
|
142
144
|
# Trailing Caret
|
|
143
145
|
li(class: "flex items-center") do
|
|
144
146
|
svg(
|
|
145
|
-
class: "rtl:rotate-180 block w-3 h-3 mx-
|
|
147
|
+
class: "rtl:rotate-180 block w-3 h-3 mx-xs text-gray-400",
|
|
146
148
|
aria_hidden: "true",
|
|
147
149
|
xmlns: "http://www.w3.org/2000/svg",
|
|
148
150
|
fill: "none",
|
|
@@ -8,8 +8,8 @@ module Plutonium
|
|
|
8
8
|
class ColorModeSelector < Plutonium::UI::Component::Base
|
|
9
9
|
# Common CSS classes used across the component
|
|
10
10
|
COMMON_CLASSES = {
|
|
11
|
-
button: "inline-flex justify-center items-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-200 hover:text-gray-900 hover:bg-
|
|
12
|
-
icon: "w-5 h-5"
|
|
11
|
+
button: "pu-color-mode-button inline-flex justify-center items-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-200 hover:text-gray-900 hover:bg-interactive dark:hover:bg-interactive-dark transition-colors duration-200",
|
|
12
|
+
icon: "pu-color-mode-icon w-5 h-5"
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
15
|
# Available color modes with their associated icons and actions
|
|
@@ -10,6 +10,14 @@ module Plutonium
|
|
|
10
10
|
include Kit
|
|
11
11
|
include Tokens
|
|
12
12
|
|
|
13
|
+
# Generate custom CSS class for theming
|
|
14
|
+
# @example
|
|
15
|
+
# theme_class(:button) # => "pu-button"
|
|
16
|
+
# theme_class(:button, variant: :table) # => "pu-button-table"
|
|
17
|
+
def theme_class(component, variant: nil, element: nil)
|
|
18
|
+
Theme.custom_class(component, variant:, element:)
|
|
19
|
+
end
|
|
20
|
+
|
|
13
21
|
if Rails.env.development?
|
|
14
22
|
def around_template(&)
|
|
15
23
|
comment { "open:#{self.class.name}" }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plutonium
|
|
4
|
+
module UI
|
|
5
|
+
module Component
|
|
6
|
+
# Provides custom CSS class names for Plutonium UI components
|
|
7
|
+
# to enable CSS-based theming without overriding components
|
|
8
|
+
#
|
|
9
|
+
# Users can customize via:
|
|
10
|
+
# 1. Tailwind config (for ALL design tokens - radius, shadows, spacing, colors, etc.)
|
|
11
|
+
# 2. CSS overrides using custom classes (e.g., .pu-button, .pu-card)
|
|
12
|
+
#
|
|
13
|
+
# @example Customizing via Tailwind config
|
|
14
|
+
# // tailwind.config.js
|
|
15
|
+
# module.exports = {
|
|
16
|
+
# theme: {
|
|
17
|
+
# extend: {
|
|
18
|
+
# borderRadius: {
|
|
19
|
+
# 'lg': '1rem', // Makes all rounded-sm bigger
|
|
20
|
+
# },
|
|
21
|
+
# boxShadow: {
|
|
22
|
+
# 'md': '0 8px 16px rgba(0,0,0,0.1)', // Customize shadow-md
|
|
23
|
+
# }
|
|
24
|
+
# }
|
|
25
|
+
# }
|
|
26
|
+
# }
|
|
27
|
+
#
|
|
28
|
+
# @example Customizing specific components via CSS
|
|
29
|
+
# .pu-button {
|
|
30
|
+
# text-transform: uppercase;
|
|
31
|
+
# }
|
|
32
|
+
#
|
|
33
|
+
# .pu-card {
|
|
34
|
+
# border-radius: 0; /* Sharp corners only on cards */
|
|
35
|
+
# }
|
|
36
|
+
#
|
|
37
|
+
module Theme
|
|
38
|
+
# Custom class names for CSS targeting
|
|
39
|
+
# Format: pu-{component}[-{variant}][-{element}]
|
|
40
|
+
def self.custom_class(component, variant: nil, element: nil)
|
|
41
|
+
parts = ["pu", component, variant, element].compact
|
|
42
|
+
parts.join("-")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -34,12 +34,12 @@ module Plutonium
|
|
|
34
34
|
return unless attachment.url.present?
|
|
35
35
|
|
|
36
36
|
div(
|
|
37
|
-
class: "w-full aspect-square bg-white border border-gray-200 rounded-
|
|
37
|
+
class: "w-full aspect-square bg-white border border-gray-200 rounded-sm shadow hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700 transition-all duration-300",
|
|
38
38
|
data: {attachment_preview_target: "thumbnail"}
|
|
39
39
|
) do
|
|
40
40
|
a(
|
|
41
41
|
href: attachment.url,
|
|
42
|
-
class: "block aspect-square overflow-hidden rounded-
|
|
42
|
+
class: "block aspect-square overflow-hidden rounded-sm",
|
|
43
43
|
target: :blank,
|
|
44
44
|
data: {attachment_preview_target: "thumbnailLink"}
|
|
45
45
|
) do
|