nanoui 0.2.0 → 0.4.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +189 -199
  4. data/lib/generators/nanoui/component_generator.rb +7 -54
  5. data/lib/generators/nanoui/install_generator.rb +0 -18
  6. data/lib/generators/nanoui/templates/css/components/alert.css +1 -1
  7. data/lib/generators/nanoui/templates/css/components/button.css +36 -7
  8. data/lib/generators/nanoui/templates/css/components/card.css +2 -1
  9. data/lib/generators/nanoui/templates/css/components/dialog.css +1 -1
  10. data/lib/generators/nanoui/templates/css/components/input.css +62 -8
  11. data/lib/generators/nanoui/templates/css/components/label.css +2 -1
  12. data/lib/generators/nanoui/templates/css/components/select.css +12 -7
  13. data/lib/generators/nanoui/templates/css/layout/container.css +14 -0
  14. data/lib/nanoui/version.rb +1 -1
  15. metadata +2 -22
  16. data/lib/generators/nanoui/templates/css/nanoui.css +0 -33
  17. data/lib/generators/nanoui/templates/css/nanoui.install.css +0 -15
  18. data/lib/generators/nanoui/templates/views/components/_accordion.html.erb +0 -40
  19. data/lib/generators/nanoui/templates/views/components/_alert.html.erb +0 -33
  20. data/lib/generators/nanoui/templates/views/components/_badge.html.erb +0 -18
  21. data/lib/generators/nanoui/templates/views/components/_button.html.erb +0 -27
  22. data/lib/generators/nanoui/templates/views/components/_card.html.erb +0 -43
  23. data/lib/generators/nanoui/templates/views/components/_checkbox.html.erb +0 -36
  24. data/lib/generators/nanoui/templates/views/components/_dialog.html.erb +0 -65
  25. data/lib/generators/nanoui/templates/views/components/_dropdown.html.erb +0 -29
  26. data/lib/generators/nanoui/templates/views/components/_input.html.erb +0 -42
  27. data/lib/generators/nanoui/templates/views/components/_label.html.erb +0 -20
  28. data/lib/generators/nanoui/templates/views/components/_progress.html.erb +0 -29
  29. data/lib/generators/nanoui/templates/views/components/_radio_group.html.erb +0 -46
  30. data/lib/generators/nanoui/templates/views/components/_select.html.erb +0 -67
  31. data/lib/generators/nanoui/templates/views/components/_switch.html.erb +0 -32
  32. data/lib/generators/nanoui/templates/views/components/_table.html.erb +0 -41
  33. data/lib/generators/nanoui/templates/views/components/_tabs.html.erb +0 -46
  34. data/lib/generators/nanoui/templates/views/components/_toast.html.erb +0 -33
  35. data/lib/generators/nanoui/templates/views/components/_toast_container.html.erb +0 -17
  36. data/lib/generators/nanoui/templates/views/components/_tooltip.html.erb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e93a8fd3240f6ca51636ddf4607fa5b98d75ffd3d9d3d89f5a07e587ccf5dd41
4
- data.tar.gz: 678d26f0b38587acf444cb685cd9ae3d6587f14868b515ca92ff019058f7c162
3
+ metadata.gz: f766927e1424d01ab34dd6f17a48b877f19de856399f5cb2584e86b6cc5accad
4
+ data.tar.gz: a09a0124675316288e9ac788c0207f035b379ecf305f388f9da619a82002aeae
5
5
  SHA512:
6
- metadata.gz: 32cf0b6c85d29c62a970991e2970acb7e9f0f6fa013aef378f8f21c1317f9f07a92fb8c7f3ec2524fe24fea8dc4b35614dcaff20e5f671c717c533fab739e512
7
- data.tar.gz: 75bfb7626afff70a8734733979f5bf372074d4ce7c0e43217498d74140d5d9b165032e8311be11959c6d71c7ba44f2e5010a667e71bb6076bf7ab40ea12f51da
6
+ metadata.gz: ce86b42ceaf868b5fcc626d2f7cabcce77bfe273636e78cca191adbf783473c1b92fa0e44de1e0c1d44360aa9095048d78a0f959fb287a7aca4a55d576ae7d53
7
+ data.tar.gz: 50b79fafd8493e2981d864cce626b121096eebd24a0206c670496bdace202bc6d85196bedd235ca2963b612a9418775c444b55fed8f7ae92f723057e46212e52
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-03-12)
4
+
5
+ ### Breaking changes
6
+
7
+ - Removed `@import`-based CSS entry point (incompatible with Propshaft)
8
+ - Removed ERB view partials
9
+
10
+ ### Added
11
+
12
+ - Native HTML element styling (button, input, select, label styled without classes)
13
+ - GitHub Pages documentation site with live component previews
14
+
15
+ ### Changed
16
+
17
+ - Simplified generators (no more import management)
18
+
19
+ ---
20
+
3
21
  ## 0.1.0 (2026-03-09)
4
22
 
5
23
  ### Initial release
data/README.md CHANGED
@@ -4,6 +4,14 @@ Vanilla CSS + Stimulus component library for Rails. Zero runtime dependencies.
4
4
 
5
5
  18 components. Semantic HTML. Accessible by default. No build step.
6
6
 
7
+ **[Documentation & Live Previews](https://chille1987.github.io/nanoui/)** — Browse all 18 components with interactive examples.
8
+
9
+ ## What's New in v0.3.0
10
+
11
+ - **Native element styling** — Bare `<button>`, `<input>`, `<select>`, and `<label>` elements are styled out of the box. No `.nano-*` classes needed for default styling. Classes still work for variants.
12
+ - **No @import needed** — Propshaft auto-loads all CSS files. Just run the generators and go.
13
+ - **HTML-only components** — No ERB partials. Use plain HTML with CSS classes directly.
14
+
7
15
  ## Installation
8
16
 
9
17
  ### Option A: Rubygem with Generators (recommended)
@@ -19,46 +27,28 @@ rails generate nanoui:install # Base styles and fonts
19
27
  rails generate nanoui:component --all # All components
20
28
  ```
21
29
 
30
+ Propshaft auto-loads the CSS files — no imports or entry points to manage.
31
+
22
32
  ### Option B: Manual Installation
23
33
 
24
34
  Copy files directly into your Rails 8 app:
25
35
 
26
36
  ```bash
27
- # 1. CSS (base, components, entrypoint, Inter Variable font)
37
+ # 1. CSS (base, components, Inter Variable font)
28
38
  mkdir -p app/assets/stylesheets/nanoui
29
39
  cp -r lib/generators/nanoui/templates/css/base app/assets/stylesheets/nanoui/
30
40
  cp -r lib/generators/nanoui/templates/css/components app/assets/stylesheets/nanoui/
31
41
  cp -r lib/generators/nanoui/templates/css/fonts app/assets/stylesheets/nanoui/
32
- cp lib/generators/nanoui/templates/css/nanoui.css app/assets/stylesheets/nanoui/
33
42
 
34
43
  # 2. Stimulus controllers
35
44
  cp lib/generators/nanoui/templates/js/controllers/*_controller.js \
36
45
  app/javascript/controllers/
37
-
38
- # 3. ERB partials (optional — you can use CSS classes directly)
39
- cp lib/generators/nanoui/templates/views/components/ app/views/components/
40
- ```
41
-
42
- ### Import CSS
43
-
44
- Add to `app/assets/stylesheets/application.css`:
45
-
46
- ```css
47
- @import "nanoui/nanoui.css";
48
- ```
49
-
50
- `rails generate nanoui:install` now creates a foundation-only `nanoui.css`, and each `nanoui:component` run rewrites the component imports so the file only references CSS that actually exists.
51
-
52
- NanoUI ships with Inter Variable and expects it at `app/assets/stylesheets/nanoui/fonts/inter-variable.ttf`, which `rails generate nanoui:install` now copies automatically.
53
-
54
- Or if using Propshaft, add the import to your layout:
55
-
56
- ```erb
57
- <%= stylesheet_link_tag "nanoui/nanoui.css" %>
58
46
  ```
59
47
 
60
48
  ### Troubleshooting Fonts
61
49
 
50
+ NanoUI ships with Inter Variable and expects it at `app/assets/stylesheets/nanoui/fonts/inter-variable.ttf`, which `rails generate nanoui:install` copies automatically.
51
+
62
52
  If NanoUI falls back to system fonts instead of Inter:
63
53
 
64
54
  - Confirm the font file exists at `app/assets/stylesheets/nanoui/fonts/inter-variable.ttf`
@@ -93,9 +83,8 @@ application.register("nanoui-dialog", DialogController)
93
83
 
94
84
  Add the `.dark` class to `<html>` to toggle dark mode. All color tokens swap automatically.
95
85
 
96
- ```erb
97
- <!-- In your layout -->
98
- <html class="<%= "dark" if user_prefers_dark? %>">
86
+ ```html
87
+ <html class="dark">
99
88
  ```
100
89
 
101
90
  Or toggle via JavaScript:
@@ -106,94 +95,98 @@ document.documentElement.classList.toggle("dark")
106
95
 
107
96
  ---
108
97
 
109
- ## Component Reference
110
-
111
- ### Essentials
112
-
113
- #### Button
98
+ ## Native Element Styling
114
99
 
115
- 6 variants, 3 sizes, icon-only, loading state.
100
+ NanoUI styles bare HTML elements automatically. No classes required for the defaults:
116
101
 
117
- ```erb
118
- <%= render "components/button", variant: "primary" do %>
119
- Save Changes
120
- <% end %>
102
+ ```html
103
+ <!-- Renders as a styled primary button -->
104
+ <button>Save Changes</button>
121
105
 
122
- <%= render "components/button", variant: "outline", size: "sm" do %>
123
- Cancel
124
- <% end %>
106
+ <!-- Renders with input styling -->
107
+ <input type="text" placeholder="Enter your name">
125
108
 
126
- <%= render "components/button", variant: "destructive" do %>
127
- Delete
128
- <% end %>
109
+ <!-- Renders with select styling -->
110
+ <select>
111
+ <option>Option 1</option>
112
+ <option>Option 2</option>
113
+ </select>
129
114
 
130
- <%# Link styled as button %>
131
- <%= render "components/button", href: "/about", variant: "ghost" do %>
132
- Learn More
133
- <% end %>
115
+ <!-- Renders with label styling -->
116
+ <label for="name">Name</label>
134
117
  ```
135
118
 
136
- Or use classes directly:
119
+ Use `.nano-*` classes when you need variants:
137
120
 
138
121
  ```html
139
- <button class="nano-btn nano-btn--primary" type="button">Save</button>
140
- <button class="nano-btn nano-btn--outline nano-btn--sm" type="button">Cancel</button>
141
- <button class="nano-btn nano-btn--primary" disabled>Disabled</button>
122
+ <button class="nano-btn--outline">Cancel</button>
123
+ <button class="nano-btn--destructive">Delete</button>
124
+ <button class="nano-btn--sm">Small</button>
142
125
  ```
143
126
 
144
- **Options:** `variant` (primary, secondary, destructive, outline, ghost, link), `size` (sm, lg, icon), `tag` (:button), `href`, `class`, `html`
127
+ ---
145
128
 
146
- #### Input
129
+ ## Component Reference
147
130
 
148
- Text fields wrapped in `.nano-field` with label and error support.
131
+ ### Essentials
149
132
 
150
- ```erb
151
- <%= render "components/input", label: "Email", type: "email", required: true,
152
- html: { placeholder: "you@example.com" } %>
133
+ #### Button
153
134
 
154
- <%= render "components/input", label: "Name", error: "Name is required" %>
135
+ 6 variants, 3 sizes, icon-only, loading state. Bare `<button>` renders as a primary button.
136
+
137
+ ```html
138
+ <button>Save Changes</button>
139
+ <button class="nano-btn--outline nano-btn--sm">Cancel</button>
140
+ <button class="nano-btn--destructive">Delete</button>
141
+ <a href="/about" class="nano-btn nano-btn--ghost">Learn More</a>
142
+ <button disabled>Disabled</button>
155
143
  ```
156
144
 
145
+ **Variants:** primary (default), secondary, destructive, outline, ghost, link
146
+ **Sizes:** sm, lg, icon
147
+
148
+ #### Input
149
+
150
+ Text fields wrapped in `.nano-field` with label and error support. Bare `<input>` gets input styling automatically.
151
+
157
152
  ```html
158
153
  <div class="nano-field">
159
- <label for="email" class="nano-label nano-label--required">Email</label>
160
- <input id="email" type="email" class="nano-input" placeholder="you@example.com" required>
154
+ <label for="email">Email</label>
155
+ <input id="email" type="email" placeholder="you@example.com" required>
161
156
  </div>
162
- ```
163
157
 
164
- **Options:** `label`, `type` ("text"), `required`, `error`, `id`, `class`, `html`
158
+ <div class="nano-field nano-field--error">
159
+ <label for="name">Name</label>
160
+ <input id="name" type="text">
161
+ <p class="nano-field__error">Name is required</p>
162
+ </div>
163
+ ```
165
164
 
166
165
  #### Label
167
166
 
168
- ```erb
169
- <%= render "components/label", for: "name", required: true do %>Name<% end %>
170
- ```
167
+ Bare `<label>` gets label styling. Add `nano-label--required` for the required indicator.
171
168
 
172
169
  ```html
173
- <label for="name" class="nano-label nano-label--required">Name</label>
170
+ <label for="name">Name</label>
171
+ <label for="email" class="nano-label--required">Email</label>
174
172
  ```
175
173
 
176
174
  #### Card
177
175
 
178
176
  Container with header, content, and footer sections.
179
177
 
180
- ```erb
181
- <%= render "components/card",
182
- title: "Settings",
183
- description: "Manage your account.",
184
- footer: render("components/button", variant: "primary") { "Save" } do %>
185
- <p>Card body content here.</p>
186
- <% end %>
187
- ```
188
-
189
178
  ```html
190
179
  <article class="nano-card">
191
180
  <div class="nano-card__header">
192
181
  <h3 class="nano-card__title">Settings</h3>
193
182
  <p class="nano-card__description">Manage your account.</p>
194
183
  </div>
195
- <div class="nano-card__content">...</div>
196
- <div class="nano-card__footer">...</div>
184
+ <div class="nano-card__content">
185
+ <p>Card body content here.</p>
186
+ </div>
187
+ <div class="nano-card__footer">
188
+ <button>Save</button>
189
+ </div>
197
190
  </article>
198
191
  ```
199
192
 
@@ -203,13 +196,9 @@ Container with header, content, and footer sections.
203
196
 
204
197
  Inline status indicators.
205
198
 
206
- ```erb
207
- <%= render "components/badge", variant: "success" do %>Active<% end %>
208
- <%= render "components/badge", variant: "warning" do %>Pending<% end %>
209
- ```
210
-
211
199
  ```html
212
200
  <span class="nano-badge nano-badge--success">Active</span>
201
+ <span class="nano-badge nano-badge--warning">Pending</span>
213
202
  <span class="nano-badge nano-badge--destructive">Failed</span>
214
203
  ```
215
204
 
@@ -219,12 +208,6 @@ Inline status indicators.
219
208
 
220
209
  Contextual feedback with icon, title, and description.
221
210
 
222
- ```erb
223
- <%= render "components/alert", variant: "success", title: "Saved!" do %>
224
- Your changes have been saved successfully.
225
- <% end %>
226
- ```
227
-
228
211
  ```html
229
212
  <div class="nano-alert nano-alert--success" role="alert">
230
213
  <div class="nano-alert__icon"><!-- SVG --></div>
@@ -243,11 +226,6 @@ Contextual feedback with icon, title, and description.
243
226
 
244
227
  #### Checkbox
245
228
 
246
- ```erb
247
- <%= render "components/checkbox", label: "Accept terms", name: "tos", error: true %>
248
- <%= render "components/checkbox", label: "Remember me", checked: true %>
249
- ```
250
-
251
229
  ```html
252
230
  <div class="nano-checkbox">
253
231
  <input type="checkbox" id="tos" class="nano-checkbox__input" name="tos">
@@ -255,29 +233,23 @@ Contextual feedback with icon, title, and description.
255
233
  </div>
256
234
  ```
257
235
 
258
- **Options:** `label`, `name`, `value`, `checked`, `disabled`, `error`, `id`
259
-
260
236
  #### Radio Group
261
237
 
262
- ```erb
263
- <%= render "components/radio_group",
264
- legend: "Plan",
265
- name: "plan",
266
- options: [
267
- { label: "Free", value: "free", checked: true },
268
- { label: "Pro", value: "pro" },
269
- { label: "Enterprise", value: "enterprise" }
270
- ] %>
271
- ```
272
-
273
238
  ```html
274
239
  <fieldset class="nano-radio-group">
275
240
  <legend class="nano-radio-group__legend">Plan</legend>
276
241
  <div class="nano-radio">
277
- <input type="radio" id="plan-0" name="plan" value="free" class="nano-radio__input" checked>
278
- <label for="plan-0" class="nano-radio__label">Free</label>
242
+ <input type="radio" id="plan-free" name="plan" value="free" class="nano-radio__input" checked>
243
+ <label for="plan-free" class="nano-radio__label">Free</label>
244
+ </div>
245
+ <div class="nano-radio">
246
+ <input type="radio" id="plan-pro" name="plan" value="pro" class="nano-radio__input">
247
+ <label for="plan-pro" class="nano-radio__label">Pro</label>
248
+ </div>
249
+ <div class="nano-radio">
250
+ <input type="radio" id="plan-ent" name="plan" value="enterprise" class="nano-radio__input">
251
+ <label for="plan-ent" class="nano-radio__label">Enterprise</label>
279
252
  </div>
280
- <!-- ... -->
281
253
  </fieldset>
282
254
  ```
283
255
 
@@ -285,10 +257,6 @@ Contextual feedback with icon, title, and description.
285
257
 
286
258
  Toggle switch with Stimulus controller.
287
259
 
288
- ```erb
289
- <%= render "components/switch", label: "Enable notifications", checked: true %>
290
- ```
291
-
292
260
  ```html
293
261
  <button type="button" role="switch" aria-checked="true" class="nano-switch"
294
262
  data-controller="nanoui-switch" data-action="nanoui-switch#toggle">
@@ -299,24 +267,19 @@ Toggle switch with Stimulus controller.
299
267
 
300
268
  #### Select
301
269
 
302
- Native `<select>` with custom styling.
303
-
304
- ```erb
305
- <%= render "components/select",
306
- label: "Country",
307
- placeholder: "Select a country",
308
- options: ["United States", "Canada", "United Kingdom"] %>
309
-
310
- <%= render "components/select",
311
- label: "Role",
312
- required: true,
313
- options: [
314
- { label: "Admin", value: "admin" },
315
- { label: "Editor", value: "editor" }
316
- ] %>
317
- ```
270
+ Native `<select>` with custom styling. Bare `<select>` gets styled automatically.
318
271
 
319
- **Options:** `label`, `placeholder`, `options`, `required`, `error`, `disabled`, `name`, `id`
272
+ ```html
273
+ <div class="nano-field">
274
+ <label for="country">Country</label>
275
+ <select id="country">
276
+ <option value="">Select a country</option>
277
+ <option>United States</option>
278
+ <option>Canada</option>
279
+ <option>United Kingdom</option>
280
+ </select>
281
+ </div>
282
+ ```
320
283
 
321
284
  ---
322
285
 
@@ -326,19 +289,6 @@ Native `<select>` with custom styling.
326
289
 
327
290
  Native `<dialog>` with `showModal()` — free focus trap, Escape close, and `::backdrop`.
328
291
 
329
- ```erb
330
- <%= render "components/dialog",
331
- title: "Edit Profile",
332
- description: "Update your info.",
333
- trigger: render("components/button") { "Open" },
334
- footer: safe_join([
335
- render("components/button", variant: "outline", html: { data: { action: "nanoui-dialog#close" } }) { "Cancel" },
336
- render("components/button", variant: "primary") { "Save" }
337
- ]) do %>
338
- <p>Dialog body content.</p>
339
- <% end %>
340
- ```
341
-
342
292
  ```html
343
293
  <div data-controller="nanoui-dialog">
344
294
  <button data-action="nanoui-dialog#open">Open</button>
@@ -347,12 +297,18 @@ Native `<dialog>` with `showModal()` — free focus trap, Escape close, and `::b
347
297
  aria-labelledby="dialog-title">
348
298
  <div class="nano-dialog__content">
349
299
  <header class="nano-dialog__header">
350
- <h2 id="dialog-title" class="nano-dialog__title">Title</h2>
300
+ <h2 id="dialog-title" class="nano-dialog__title">Edit Profile</h2>
301
+ <p class="nano-dialog__description">Update your info.</p>
351
302
  </header>
352
- <div class="nano-dialog__body">...</div>
353
- <footer class="nano-dialog__footer">...</footer>
303
+ <div class="nano-dialog__body">
304
+ <p>Dialog body content.</p>
305
+ </div>
306
+ <footer class="nano-dialog__footer">
307
+ <button class="nano-btn--outline" data-action="nanoui-dialog#close">Cancel</button>
308
+ <button>Save</button>
309
+ </footer>
354
310
  <button class="nano-dialog__close" data-action="nanoui-dialog#close"
355
- aria-label="Close dialog">...</button>
311
+ aria-label="Close dialog">&times;</button>
356
312
  </div>
357
313
  </dialog>
358
314
  </div>
@@ -364,14 +320,16 @@ Native `<dialog>` with `showModal()` — free focus trap, Escape close, and `::b
364
320
 
365
321
  Click-activated menu with keyboard navigation.
366
322
 
367
- ```erb
368
- <%= render "components/dropdown",
369
- trigger: render("components/button", variant: "outline") { "Options" } do %>
370
- <button class="nano-dropdown__item">Profile</button>
371
- <button class="nano-dropdown__item">Settings</button>
372
- <div class="nano-dropdown__separator"></div>
373
- <button class="nano-dropdown__item">Log out</button>
374
- <% end %>
323
+ ```html
324
+ <div data-controller="nanoui-dropdown">
325
+ <button class="nano-btn--outline" data-action="nanoui-dropdown#toggle">Options</button>
326
+ <div class="nano-dropdown__menu" data-nanoui-dropdown-target="menu">
327
+ <button class="nano-dropdown__item">Profile</button>
328
+ <button class="nano-dropdown__item">Settings</button>
329
+ <div class="nano-dropdown__separator"></div>
330
+ <button class="nano-dropdown__item">Log out</button>
331
+ </div>
332
+ </div>
375
333
  ```
376
334
 
377
335
  Keyboard: Arrow Up/Down navigates items, Escape closes, click outside closes.
@@ -380,12 +338,12 @@ Keyboard: Arrow Up/Down navigates items, Escape closes, click outside closes.
380
338
 
381
339
  Hover/focus tooltip with configurable delay.
382
340
 
383
- ```erb
384
- <%= render "components/tooltip", text: "Add to favorites", position: "top" do %>
385
- <%= render "components/button", variant: "primary", size: "icon" do %>
341
+ ```html
342
+ <div data-controller="nanoui-tooltip" data-nanoui-tooltip-text-value="Add to favorites">
343
+ <button class="nano-btn--primary nano-btn--icon">
386
344
  <!-- heart icon SVG -->
387
- <% end %>
388
- <% end %>
345
+ </button>
346
+ </div>
389
347
  ```
390
348
 
391
349
  **Positions:** top (default), bottom, left, right. **Delay:** 200ms default.
@@ -396,16 +354,19 @@ Auto-dismissing notifications stacked bottom-right.
396
354
 
397
355
  Place the container once in your layout:
398
356
 
399
- ```erb
400
- <%# app/views/layouts/application.html.erb %>
401
- <%= render "components/toast_container" %>
357
+ ```html
358
+ <div class="nano-toast-container" data-controller="nanoui-toast"></div>
402
359
  ```
403
360
 
404
- Add toasts dynamically (via Turbo Stream or JS):
361
+ Add toasts dynamically:
405
362
 
406
- ```erb
407
- <%= render "components/toast", variant: "success",
408
- title: "Saved!", description: "Changes applied." %>
363
+ ```html
364
+ <div class="nano-toast nano-toast--success" role="alert">
365
+ <div class="nano-toast__content">
366
+ <p class="nano-toast__title">Saved!</p>
367
+ <p class="nano-toast__description">Changes applied.</p>
368
+ </div>
369
+ </div>
409
370
  ```
410
371
 
411
372
  **Variants:** default, success, destructive, warning. **Auto-dismiss:** 5000ms default.
@@ -418,57 +379,85 @@ Add toasts dynamically (via Turbo Stream or JS):
418
379
 
419
380
  Semantic table with responsive scroll wrapper.
420
381
 
421
- ```erb
422
- <%= render "components/table",
423
- headers: ["Name", "Email", "Status"],
424
- striped: true, hoverable: true do %>
425
- <tr class="nano-table__row">
426
- <td class="nano-table__cell">Jane Doe</td>
427
- <td class="nano-table__cell">jane@example.com</td>
428
- <td class="nano-table__cell">
429
- <%= render "components/badge", variant: "success" do %>Active<% end %>
430
- </td>
431
- </tr>
432
- <% end %>
382
+ ```html
383
+ <div class="nano-table-wrapper">
384
+ <table class="nano-table nano-table--striped nano-table--hoverable">
385
+ <thead>
386
+ <tr>
387
+ <th class="nano-table__head">Name</th>
388
+ <th class="nano-table__head">Email</th>
389
+ <th class="nano-table__head">Status</th>
390
+ </tr>
391
+ </thead>
392
+ <tbody>
393
+ <tr class="nano-table__row">
394
+ <td class="nano-table__cell">Jane Doe</td>
395
+ <td class="nano-table__cell">jane@example.com</td>
396
+ <td class="nano-table__cell">
397
+ <span class="nano-badge nano-badge--success">Active</span>
398
+ </td>
399
+ </tr>
400
+ </tbody>
401
+ </table>
402
+ </div>
433
403
  ```
434
404
 
435
- **Options:** `headers`, `striped`, `hoverable`, `class`
436
-
437
405
  #### Tabs
438
406
 
439
407
  WAI-ARIA tabs pattern with arrow key navigation.
440
408
 
441
- ```erb
442
- <%= render "components/tabs", label: "Settings", tabs: [
443
- { id: "general", label: "General", content: tag.p("General settings..."), active: true },
444
- { id: "security", label: "Security", content: tag.p("Security settings...") },
445
- { id: "billing", label: "Billing", content: tag.p("Billing info...") }
446
- ] %>
409
+ ```html
410
+ <div data-controller="nanoui-tabs" class="nano-tabs">
411
+ <div role="tablist" aria-label="Settings" class="nano-tabs__list">
412
+ <button role="tab" aria-selected="true" aria-controls="general"
413
+ class="nano-tabs__trigger" data-action="nanoui-tabs#select">General</button>
414
+ <button role="tab" aria-selected="false" aria-controls="security"
415
+ class="nano-tabs__trigger" data-action="nanoui-tabs#select">Security</button>
416
+ <button role="tab" aria-selected="false" aria-controls="billing"
417
+ class="nano-tabs__trigger" data-action="nanoui-tabs#select">Billing</button>
418
+ </div>
419
+ <div role="tabpanel" id="general" class="nano-tabs__content">General settings...</div>
420
+ <div role="tabpanel" id="security" class="nano-tabs__content" hidden>Security settings...</div>
421
+ <div role="tabpanel" id="billing" class="nano-tabs__content" hidden>Billing info...</div>
422
+ </div>
447
423
  ```
448
424
 
449
- **Options:** `tabs` (array), `label` (aria-label), `hash` (URL hash sync)
450
-
451
425
  #### Accordion
452
426
 
453
427
  Native `<details>`/`<summary>` with optional single-open mode.
454
428
 
455
- ```erb
456
- <%= render "components/accordion", single: true, items: [
457
- { title: "Is it free?", content: "Yes, MIT licensed.", open: true },
458
- { title: "Build step?", content: "No, vanilla CSS." },
459
- { title: "Dark mode?", content: "Add .dark class to <html>." }
460
- ] %>
429
+ ```html
430
+ <div class="nano-accordion" data-controller="nanoui-accordion"
431
+ data-nanoui-accordion-single-value="true">
432
+ <details class="nano-accordion__item" open>
433
+ <summary class="nano-accordion__trigger">Is it free?</summary>
434
+ <div class="nano-accordion__content">Yes, MIT licensed.</div>
435
+ </details>
436
+ <details class="nano-accordion__item">
437
+ <summary class="nano-accordion__trigger">Build step?</summary>
438
+ <div class="nano-accordion__content">No, vanilla CSS.</div>
439
+ </details>
440
+ <details class="nano-accordion__item">
441
+ <summary class="nano-accordion__trigger">Dark mode?</summary>
442
+ <div class="nano-accordion__content">Add .dark class to &lt;html&gt;.</div>
443
+ </details>
444
+ </div>
461
445
  ```
462
446
 
463
- **Options:** `items` (array), `single` (single-open mode), `class`
464
-
465
447
  #### Progress
466
448
 
467
449
  Native `<progress>` element with custom styling.
468
450
 
469
- ```erb
470
- <%= render "components/progress", value: 65, label: "65%", aria_label: "Upload progress" %>
471
- <%= render "components/progress", value: 100, variant: "success", label: "Complete" %>
451
+ ```html
452
+ <div class="nano-progress">
453
+ <progress class="nano-progress__bar" value="65" max="100"></progress>
454
+ <span class="nano-progress__label">65%</span>
455
+ </div>
456
+
457
+ <div class="nano-progress nano-progress--success">
458
+ <progress class="nano-progress__bar" value="100" max="100"></progress>
459
+ <span class="nano-progress__label">Complete</span>
460
+ </div>
472
461
  ```
473
462
 
474
463
  **Variants:** default (primary), success, warning, destructive
@@ -523,6 +512,7 @@ rails generate nanoui:component --all
523
512
  ## Philosophy
524
513
 
525
514
  - **Semantic HTML first** — `<dialog>`, `<details>`, `<progress>`, `<fieldset>`, `<output>`
515
+ - **Native element styling** — Bare HTML elements look good without classes
526
516
  - **Accessibility is not optional** — ARIA attributes, keyboard navigation, focus management, screen reader support
527
517
  - **No build step** — No Tailwind, no PostCSS, no webpack. Vanilla CSS with native nesting
528
518
  - **You own the code** — Generator copies files into your app. Edit freely, no runtime dependency