nitro_kit 2.0.0.alpha.2 → 2.0.0.alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +229 -1
- data/README.md +25 -10
- data/STYLE_GUIDE.md +47 -5
- data/app/assets/stylesheets/nitro_kit-tailwind-v4.css +3 -3
- data/app/assets/stylesheets/nitro_kit.css +2820 -1816
- data/app/components/nitro_kit/accordion.rb +2 -26
- data/app/components/nitro_kit/alert.rb +1 -11
- data/app/components/nitro_kit/app_navigation.rb +45 -11
- data/app/components/nitro_kit/app_shell.rb +1 -7
- data/app/components/nitro_kit/appearance_picker.rb +11 -12
- data/app/components/nitro_kit/avatar.rb +6 -1
- data/app/components/nitro_kit/avatar_stack.rb +3 -0
- data/app/components/nitro_kit/badge.rb +13 -4
- data/app/components/nitro_kit/button_group.rb +2 -2
- data/app/components/nitro_kit/checkbox.rb +7 -4
- data/app/components/nitro_kit/checkbox_group.rb +0 -25
- data/app/components/nitro_kit/combobox.rb +4 -33
- data/app/components/nitro_kit/command_palette.rb +42 -59
- data/app/components/nitro_kit/component.rb +57 -5
- data/app/components/nitro_kit/danger_zone.rb +5 -1
- data/app/components/nitro_kit/data_section.rb +28 -4
- data/app/components/nitro_kit/details_table.rb +19 -8
- data/app/components/nitro_kit/dialog.rb +23 -19
- data/app/components/nitro_kit/dropdown.rb +1 -6
- data/app/components/nitro_kit/dropzone.rb +4 -15
- data/app/components/nitro_kit/empty_state.rb +21 -2
- data/app/components/nitro_kit/field.rb +35 -11
- data/app/components/nitro_kit/form_builder.rb +1 -2
- data/app/components/nitro_kit/progressive_image.rb +2 -2
- data/app/components/nitro_kit/radio_button.rb +7 -4
- data/app/components/nitro_kit/radio_button_group.rb +0 -25
- data/app/components/nitro_kit/select.rb +4 -17
- data/app/components/nitro_kit/settings_layout.rb +17 -4
- data/app/components/nitro_kit/{form_section.rb → settings_section.rb} +18 -11
- data/app/components/nitro_kit/sheet.rb +29 -20
- data/app/components/nitro_kit/stat_grid.rb +18 -2
- data/app/components/nitro_kit/table.rb +22 -3
- data/app/components/nitro_kit/tabs.rb +2 -13
- data/app/components/nitro_kit/toast.rb +4 -2
- data/app/components/nitro_kit/toolbar.rb +2 -0
- data/app/components/nitro_kit/tooltip.rb +7 -10
- data/app/javascript/controllers/nk/app_shell_controller.js +3 -3
- data/app/javascript/controllers/nk/appearance_controller.js +6 -0
- data/app/javascript/controllers/nk/combobox_controller.js +51 -1
- data/app/javascript/controllers/nk/command_palette_controller.js +25 -11
- data/app/javascript/controllers/nk/dialog_controller.js +54 -0
- data/app/javascript/controllers/nk/dropdown_controller.js +45 -0
- data/app/javascript/controllers/nk/dropzone_controller.js +2 -2
- data/app/javascript/controllers/nk/progressive_image_controller.js +11 -7
- data/app/javascript/controllers/nk/tabs_controller.js +21 -2
- data/app/javascript/controllers/nk/tooltip_controller.js +9 -0
- data/config/locales/en.yml +4 -1
- data/docs/agent_guide.md +63 -123
- data/docs/agent_native_spec.md +72 -383
- data/docs/browser_support.md +82 -0
- data/docs/component_contracts.md +108 -82
- data/docs/customization.md +689 -91
- data/docs/hotwire.md +50 -76
- data/docs/initialization_prompt.md +26 -31
- data/docs/migration_1_to_2.md +88 -30
- data/docs/patterns/application_foundation.md +45 -119
- data/docs/patterns/crud_resource.md +44 -126
- data/docs/patterns/destructive_action.md +45 -77
- data/docs/patterns/flash_and_toast.md +18 -36
- data/docs/patterns/inline_edit.md +21 -44
- data/docs/patterns/queryable_collection.md +65 -161
- data/docs/patterns/resource_form.md +33 -81
- data/docs/rails_conventions.md +38 -75
- data/docs/rails_integration.md +96 -446
- data/lib/generators/nitro_kit/upgrade_smoke_tests_generator.rb +12 -0
- data/lib/nitro_kit/installation.rb +20 -7
- data/lib/nitro_kit/migration_inventory.rb +275 -8
- data/lib/nitro_kit/upgrade_smoke_test.rb +10 -12
- data/lib/nitro_kit/version.rb +1 -1
- data/lib/rails/commands/nitro_kit/nitro_kit_command.rb +10 -3
- data/plugins/nitro-kit/skills/nitro-kit-hotwire/SKILL.md +4 -0
- data/plugins/nitro-kit/skills/nitro-kit-rails/SKILL.md +13 -1
- data/plugins/nitro-kit/skills/nitro-kit-ui/SKILL.md +32 -15
- data/src/stylesheets/nitro_kit/components/accordion.css +44 -44
- data/src/stylesheets/nitro_kit/components/alert.css +27 -36
- data/src/stylesheets/nitro_kit/components/app_navigation.css +92 -20
- data/src/stylesheets/nitro_kit/components/app_shell.css +199 -196
- data/src/stylesheets/nitro_kit/components/appearance_picker.css +61 -39
- data/src/stylesheets/nitro_kit/components/auth_shell.css +2 -2
- data/src/stylesheets/nitro_kit/components/avatar.css +15 -11
- data/src/stylesheets/nitro_kit/components/avatar_stack.css +48 -29
- data/src/stylesheets/nitro_kit/components/badge.css +13 -9
- data/src/stylesheets/nitro_kit/components/button.css +121 -79
- data/src/stylesheets/nitro_kit/components/button_group.css +19 -14
- data/src/stylesheets/nitro_kit/components/card.css +12 -6
- data/src/stylesheets/nitro_kit/components/checkbox.css +54 -67
- data/src/stylesheets/nitro_kit/components/checkbox_group.css +16 -16
- data/src/stylesheets/nitro_kit/components/combobox.css +39 -29
- data/src/stylesheets/nitro_kit/components/command_palette.css +110 -51
- data/src/stylesheets/nitro_kit/components/container.css +6 -6
- data/src/stylesheets/nitro_kit/components/control_group.css +61 -67
- data/src/stylesheets/nitro_kit/components/danger_zone.css +8 -8
- data/src/stylesheets/nitro_kit/components/data_section.css +4 -4
- data/src/stylesheets/nitro_kit/components/details_table.css +17 -15
- data/src/stylesheets/nitro_kit/components/dialog.css +8 -5
- data/src/stylesheets/nitro_kit/components/dropdown.css +36 -30
- data/src/stylesheets/nitro_kit/components/dropzone.css +78 -35
- data/src/stylesheets/nitro_kit/components/empty_state.css +7 -7
- data/src/stylesheets/nitro_kit/components/field.css +32 -28
- data/src/stylesheets/nitro_kit/components/field_group.css +13 -0
- data/src/stylesheets/nitro_kit/components/fieldset.css +8 -2
- data/src/stylesheets/nitro_kit/components/flex.css +1 -1
- data/src/stylesheets/nitro_kit/components/grid.css +1 -1
- data/src/stylesheets/nitro_kit/components/icon.css +5 -5
- data/src/stylesheets/nitro_kit/components/input.css +42 -8
- data/src/stylesheets/nitro_kit/components/layout.css +165 -165
- data/src/stylesheets/nitro_kit/components/page_header.css +7 -7
- data/src/stylesheets/nitro_kit/components/pagination.css +58 -49
- data/src/stylesheets/nitro_kit/components/pagination_bar.css +9 -9
- data/src/stylesheets/nitro_kit/components/palette.css +145 -77
- data/src/stylesheets/nitro_kit/components/progressive_image.css +45 -55
- data/src/stylesheets/nitro_kit/components/radio_button.css +48 -46
- data/src/stylesheets/nitro_kit/components/radio_button_group.css +49 -49
- data/src/stylesheets/nitro_kit/components/rich_text_area.css +16 -1
- data/src/stylesheets/nitro_kit/components/select.css +24 -15
- data/src/stylesheets/nitro_kit/components/settings_layout.css +26 -23
- data/src/stylesheets/nitro_kit/components/settings_section.css +84 -0
- data/src/stylesheets/nitro_kit/components/sheet.css +30 -10
- data/src/stylesheets/nitro_kit/components/stat_grid.css +11 -10
- data/src/stylesheets/nitro_kit/components/switch.css +60 -62
- data/src/stylesheets/nitro_kit/components/table.css +43 -35
- data/src/stylesheets/nitro_kit/components/tabs.css +48 -40
- data/src/stylesheets/nitro_kit/components/textarea.css +20 -5
- data/src/stylesheets/nitro_kit/components/toast.css +11 -47
- data/src/stylesheets/nitro_kit/components/toolbar.css +16 -41
- data/src/stylesheets/nitro_kit/components/tooltip.css +38 -29
- data/src/stylesheets/nitro_kit/components/typeset.css +150 -4
- data/src/stylesheets/nitro_kit/reset.css +15 -15
- data/src/stylesheets/nitro_kit/tokens.css +501 -79
- metadata +4 -4
- data/docs/new_app_strategy.md +0 -22
- data/src/stylesheets/nitro_kit/components/form_section.css +0 -71
data/docs/hotwire.md
CHANGED
|
@@ -1,94 +1,68 @@
|
|
|
1
1
|
# Hotwire with Nitro Kit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
responses. Hotwire transports server-rendered HTML between those boundaries.
|
|
3
|
+
**Audience:** Application developers and coding agents implementing Hotwire
|
|
4
|
+
interactions with Nitro Kit.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
2. Use a Turbo Frame for one independently navigable or replaceable region.
|
|
11
|
-
3. Return request-scoped Turbo Streams when one action changes multiple
|
|
12
|
-
regions.
|
|
13
|
-
4. Broadcast only when another session needs the update.
|
|
14
|
-
5. Add application Stimulus only for browser-owned behavior the preceding
|
|
15
|
-
layers cannot express.
|
|
16
|
-
|
|
17
|
-
Do not copy Nitro Kit controllers into the application. Do not add files under
|
|
18
|
-
`app/javascript/controllers/nk`; consume the `nk--*` controllers packaged by
|
|
19
|
-
the installed gem.
|
|
20
|
-
|
|
21
|
-
## Response matrix
|
|
6
|
+
Rails owns records, routes, authorization, DOM identity, and responses. Nitro
|
|
7
|
+
Kit owns component markup, CSS, and its focused controllers. Hotwire transports
|
|
8
|
+
server-rendered HTML between them.
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
| -------------------- | --------------------------------------------- | ------------------------------------------------------------------- |
|
|
25
|
-
| HTML form mutation | Redirect with `303 See Other` | Render HTML with `422` |
|
|
26
|
-
| Frame form mutation | Redirect or render the same frame ID | Render the same frame ID with `422` |
|
|
27
|
-
| Stream form mutation | Return only when multiple targets must change | Render the invalid form target with `422` and keep an HTML fallback |
|
|
28
|
-
| GET query | Render from URL parameters | Render a useful empty or error state |
|
|
29
|
-
|
|
30
|
-
Use `dom_id` or one named constant for a frame. The show, edit, invalid,
|
|
31
|
-
success, and cancel responses must preserve that identifier.
|
|
32
|
-
|
|
33
|
-
## Forms and application Stimulus
|
|
10
|
+
## Choose the smallest interaction
|
|
34
11
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
dialog must still submit a real Rails form.
|
|
12
|
+
1. Ordinary links and forms under Turbo Drive.
|
|
13
|
+
2. One Turbo Frame for one independently navigable region.
|
|
14
|
+
3. A request-scoped Turbo Stream when one action changes multiple regions.
|
|
15
|
+
4. A broadcast only when another session needs the update.
|
|
16
|
+
5. Application Stimulus only for browser-owned behavior the previous layers
|
|
17
|
+
cannot express.
|
|
42
18
|
|
|
43
|
-
|
|
44
|
-
|
|
19
|
+
Do not copy Nitro controllers or add files under
|
|
20
|
+
`app/javascript/controllers/nk`. Follow the canonical
|
|
21
|
+
[browser support policy](browser_support.md) for fallback behavior.
|
|
45
22
|
|
|
46
|
-
|
|
47
|
-
import { Controller } from "@hotwired/stimulus";
|
|
23
|
+
## Response contract
|
|
48
24
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
25
|
+
| Request | Success | Validation failure |
|
|
26
|
+
| --------------- | ----------------------------------------- | --------------------------------------------------------------- |
|
|
27
|
+
| HTML mutation | Redirect with `303 See Other` | Render the invalid form with `422` |
|
|
28
|
+
| Frame mutation | Redirect to, or render, the same frame ID | Render the same frame ID with `422` |
|
|
29
|
+
| Stream mutation | Return a stream only for multiple targets | Replace the invalid form target with `422`; keep an HTML branch |
|
|
30
|
+
| GET query | Render from URL parameters | Render a useful empty or error state |
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
the HTML fallback. Use `data-action` instead of manually registering DOM listeners. If a
|
|
59
|
-
controller owns a listener, observer, timer, object URL, or third-party
|
|
60
|
-
instance, release it in `disconnect`.
|
|
32
|
+
Authentication and authorization failures are separate policy decisions; do
|
|
33
|
+
not return `422` for them.
|
|
61
34
|
|
|
62
|
-
|
|
35
|
+
Use `dom_id` or one named constant for each frame. Show, edit, invalid, success,
|
|
36
|
+
and cancel responses must preserve that identifier. An HTML branch preserves
|
|
37
|
+
the request path without Turbo; it does not make a closed or JavaScript-owned
|
|
38
|
+
interaction available.
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
a genuinely stateful island, always with a stable unique `id`. Update content
|
|
66
|
-
inside a permanent element deliberately instead of making broad page regions
|
|
67
|
-
permanent.
|
|
40
|
+
## Stimulus and lifecycle
|
|
68
41
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
42
|
+
Let Turbo submit real Rails forms. Use `data-turbo-submits-with` for submission
|
|
43
|
+
feedback and `data-turbo-confirm` only for compact confirmation. A reviewed
|
|
44
|
+
destructive flow still submits a real Rails form; use the
|
|
45
|
+
[destructive action pattern](patterns/destructive_action.md).
|
|
72
46
|
|
|
73
|
-
|
|
47
|
+
Keep application controllers declarative. Prefer `data-action` over manually
|
|
48
|
+
registered listeners. Release listeners, observers, timers, object URLs, and
|
|
49
|
+
third-party instances in `disconnect`.
|
|
74
50
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
a full-page response, and handle `turbo:frame-missing` only when the
|
|
79
|
-
application has a deliberate recovery policy.
|
|
51
|
+
Keep server-rendered markup morph-safe. Enable refresh morphing deliberately,
|
|
52
|
+
and use `data-turbo-permanent` only for a stateful island with a stable unique
|
|
53
|
+
ID. Clean ephemeral UI before Turbo caches a page.
|
|
80
54
|
|
|
81
|
-
|
|
82
|
-
|
|
55
|
+
Use `_top` when navigation must leave a frame. Authentication redirects and
|
|
56
|
+
errors must not strand the user behind a missing-frame response. Give lazy and
|
|
57
|
+
failed frames useful content and a route to continue.
|
|
83
58
|
|
|
84
|
-
## Verify
|
|
59
|
+
## Verify
|
|
85
60
|
|
|
86
61
|
- Request-test `303`, `422`, HTML fallback, and stable frame IDs.
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Read the matching recipe under `docs/patterns/` for complete compositions.
|
|
62
|
+
- Scope mutation assertions to the matching frame.
|
|
63
|
+
- System-test focus, navigation, dialogs, and multi-target changes.
|
|
64
|
+
- Use Capybara waiting assertions; never use `sleep`.
|
|
65
|
+
- Test navigation, caching, morphing, and reconnection for duplicate state or
|
|
66
|
+
listeners.
|
|
67
|
+
|
|
68
|
+
Use the matching [interaction pattern](patterns/) for complete compositions.
|
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Verify Nitro Kit 2 setup in this Rails application
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
APIs or examples.
|
|
3
|
+
**Audience:** Coding agent running immediately after Nitro Kit installation.
|
|
5
4
|
|
|
6
|
-
1. Run `bundle show nitro_kit` and confirm the resolved version
|
|
5
|
+
1. Run `bundle show nitro_kit` and confirm the resolved version starts with
|
|
7
6
|
`2.`.
|
|
8
|
-
2.
|
|
9
|
-
|
|
10
|
-
3. Inspect the application before editing. Preserve established
|
|
11
|
-
|
|
12
|
-
4.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
7.
|
|
23
|
-
`NitroKit` components, `nk_*` helpers, or `controllers/nk` only when this
|
|
24
|
-
task is authorized to migrate the application.
|
|
25
|
-
8. Use ordinary Rails routes, models, forms, and server-rendered HTML. Follow
|
|
26
|
-
the installed Rails and Hotwire guidance for new work. During a migration,
|
|
27
|
-
replace an existing control only when Nitro Kit 2 has a genuine semantic
|
|
28
|
-
and behavioral equivalent. Otherwise preserve it as application-owned
|
|
29
|
-
Rails and semantic HTML; never downgrade specialized behavior or retain
|
|
30
|
-
copied Nitro Kit 1.x source as the fallback.
|
|
31
|
-
9. Run `bin/rails nitro_kit:doctor`, fix actionable failures, and run the
|
|
7
|
+
2. Choose the project-local Nitro Kit skill matching the task. It will resolve
|
|
8
|
+
and read the installed, version-matched `docs/agent_guide.md`.
|
|
9
|
+
3. Inspect the application before editing. Preserve established view, asset,
|
|
10
|
+
authentication, and testing conventions unless the task changes them.
|
|
11
|
+
4. For a greenfield application, run `bin/rails generate phlex:install` and use
|
|
12
|
+
Phlex for the application layout, route views, and reusable UI. In an
|
|
13
|
+
established application, introduce Phlex only at the requested boundary.
|
|
14
|
+
Do not perform an application-wide migration unless it is explicitly
|
|
15
|
+
authorized.
|
|
16
|
+
5. Verify that the application loads Nitro Kit CSS, the appearance bootstrap,
|
|
17
|
+
Turbo, Stimulus, and the normal Stimulus controller loader. Never copy Nitro
|
|
18
|
+
components or `nk--*` controllers into the application.
|
|
19
|
+
6. Verify one application base component includes `NitroKit`, and model-backed
|
|
20
|
+
forms select `NitroKit::FormBuilder` explicitly.
|
|
21
|
+
7. Run `bin/rails nitro_kit:doctor`, fix actionable failures, and run the
|
|
32
22
|
application's relevant tests.
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
If this is a Nitro Kit 1.x migration, stop and follow
|
|
25
|
+
`docs/migration_1_to_2.md` from the installed gem. Replace a control only when
|
|
26
|
+
2.x provides a genuine semantic and behavioral equivalent. Otherwise preserve
|
|
27
|
+
it as application-owned Rails and semantic HTML. Never retain copied Nitro Kit
|
|
28
|
+
1.x source as the fallback.
|
|
29
|
+
|
|
30
|
+
Report changes, preserved conventions, unsupported controls, and unresolved
|
|
31
|
+
decisions.
|
data/docs/migration_1_to_2.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# Migrating a Nitro Kit 1.x application
|
|
2
2
|
|
|
3
|
+
**Audience:** Coding agents and developers migrating an existing application
|
|
4
|
+
from Nitro Kit 1.x to 2.x.
|
|
5
|
+
|
|
3
6
|
Treat a 1.x migration as a product-flow review, not a helper rename. Nitro Kit
|
|
4
7
|
2 deliberately removed copied components, `nk_*` helpers, application-owned
|
|
5
8
|
`controllers/nk`, and unrestricted utility-class customization.
|
|
6
9
|
|
|
7
|
-
## Install the 2.0
|
|
10
|
+
## Install the 2.0 prerelease before migrating
|
|
8
11
|
|
|
9
|
-
Add the 2.0
|
|
12
|
+
Add the 2.0 prerelease to the application's Gemfile:
|
|
10
13
|
|
|
11
14
|
```ruby
|
|
12
|
-
gem "nitro_kit", "2.0.0.alpha.
|
|
15
|
+
gem "nitro_kit", "2.0.0.alpha.4"
|
|
13
16
|
```
|
|
14
17
|
|
|
15
18
|
Bundler records the exact released version in `Gemfile.lock`; commit `Gemfile`
|
|
@@ -22,14 +25,19 @@ rather than a moving Git branch.
|
|
|
22
25
|
## Inventory behavior before editing
|
|
23
26
|
|
|
24
27
|
1. List every `nk_*` helper, `NitroKit::*` component, copied Nitro source file,
|
|
25
|
-
|
|
28
|
+
`controllers/nk` controller, rendered native or Rails button helper, and
|
|
29
|
+
application-owned button class such as `.btn`.
|
|
26
30
|
2. Group usage by user flow: authentication, settings, collection browsing,
|
|
27
31
|
mobile navigation, editing, destructive actions, notifications, and
|
|
28
32
|
copy/share controls.
|
|
29
33
|
3. Record behavior that must survive: native element, submitted method and
|
|
30
34
|
parameters, Turbo target, accessible name and description, focus behavior,
|
|
31
35
|
narrow-screen presentation, empty/error state, and visual density.
|
|
32
|
-
4.
|
|
36
|
+
4. Inventory the existing semantic primary, focus, destructive, neutral, font,
|
|
37
|
+
density, and radius decisions. Translate those roles to public `--nk-*`
|
|
38
|
+
tokens rather than choosing similar raw palette values. Record separately
|
|
39
|
+
when buttons use a distinct shape from inputs and surfaces.
|
|
40
|
+
5. Capture representative wide and narrow screenshots before conversion.
|
|
33
41
|
|
|
34
42
|
If the Nitro Kit MCP catalog is connected, search it by workflow after this
|
|
35
43
|
inventory — for example `mobile transcript navigation`, `settings form`, or
|
|
@@ -46,11 +54,12 @@ Map each flow to the highest-level matching 2.x component first:
|
|
|
46
54
|
| Sign-in or recovery card | `AuthShell` |
|
|
47
55
|
| Application navigation | `AppShell`, `AppNavigation` |
|
|
48
56
|
| Mobile contextual navigation or details | `Sheet` |
|
|
49
|
-
| Settings screen | `SettingsLayout`, `
|
|
57
|
+
| Settings screen | `SettingsLayout`, `SettingsSection` |
|
|
50
58
|
| Empty collection card | `EmptyState` |
|
|
51
59
|
| Data collection | `DataSection`, `Table`, `PaginationBar` |
|
|
52
60
|
| Destructive settings | `DangerZone`, `Dialog`, `ButtonTo` |
|
|
53
61
|
| Joined copy or filter controls | `ControlGroup` |
|
|
62
|
+
| Joined action controls | `ButtonGroup` |
|
|
54
63
|
|
|
55
64
|
Only then replace remaining atoms. Common direct mappings include:
|
|
56
65
|
|
|
@@ -107,7 +116,7 @@ end
|
|
|
107
116
|
```
|
|
108
117
|
|
|
109
118
|
The executable minimal version is on the
|
|
110
|
-
[`AppNavigation` gallery page](/gallery/components/app-navigation#example-app-navigation-minimal).
|
|
119
|
+
[`AppNavigation` gallery page](https://gallery.nitrokit.dev/gallery/components/app-navigation#example-app-navigation-minimal).
|
|
111
120
|
|
|
112
121
|
### Dialog
|
|
113
122
|
|
|
@@ -158,7 +167,7 @@ end
|
|
|
158
167
|
```
|
|
159
168
|
|
|
160
169
|
The gallery runs this structure at narrow widths in
|
|
161
|
-
[`Narrow transcript actions`](/gallery/components/dialog#example-dialog-narrow-action-cluster).
|
|
170
|
+
[`Narrow transcript actions`](https://gallery.nitrokit.dev/gallery/components/dialog#example-dialog-narrow-action-cluster).
|
|
162
171
|
|
|
163
172
|
### Sheet
|
|
164
173
|
|
|
@@ -198,7 +207,7 @@ end
|
|
|
198
207
|
```
|
|
199
208
|
|
|
200
209
|
See the executable
|
|
201
|
-
[`Sheet` collection example](/gallery/components/sheet#example-sheet-constructions).
|
|
210
|
+
[`Sheet` collection example](https://gallery.nitrokit.dev/gallery/components/sheet#example-sheet-constructions).
|
|
202
211
|
|
|
203
212
|
### SettingsLayout
|
|
204
213
|
|
|
@@ -237,13 +246,22 @@ end
|
|
|
237
246
|
```
|
|
238
247
|
|
|
239
248
|
See the executable
|
|
240
|
-
[`SettingsLayout` minimal example](/gallery/components/settings-layout#example-settings-layout-cardinality-states).
|
|
249
|
+
[`SettingsLayout` minimal example](https://gallery.nitrokit.dev/gallery/components/settings-layout#example-settings-layout-cardinality-states).
|
|
241
250
|
|
|
242
251
|
## Preserve unsupported behavior honestly
|
|
243
252
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
253
|
+
Replace a form control only when the installed Nitro Kit catalog provides a
|
|
254
|
+
genuine semantic and behavioral equivalent. Preserve its parameter name, IDs,
|
|
255
|
+
values, errors, accessibility, uploads, and browser behavior. If no equivalent
|
|
256
|
+
exists, keep or re-express the control as application-owned Rails and semantic
|
|
257
|
+
HTML, optionally inside a custom `form.field` composition. Never downgrade an
|
|
258
|
+
editor, autocomplete, date range, upload, or other specialized input to the
|
|
259
|
+
nearest generic Nitro control merely for visual consistency.
|
|
260
|
+
|
|
261
|
+
Do not retain copied Nitro Kit 1.x source as the fallback. Remove the legacy
|
|
262
|
+
component, preserve the unsupported behavior in clearly application-owned
|
|
263
|
+
code, and report the missing equivalent as a Nitro Kit coverage gap. Do not
|
|
264
|
+
hide the gap behind a generic component.
|
|
247
265
|
|
|
248
266
|
Tooltip custom triggers are the explicit composition path for an existing
|
|
249
267
|
focusable control. Forward every yielded boundary to that actual control:
|
|
@@ -276,13 +294,19 @@ bin/rails test test/system/nitro_kit_upgrade_smoke_test.rb
|
|
|
276
294
|
|
|
277
295
|
The generator does not overwrite existing tests. It generates only files
|
|
278
296
|
supported by the host's Rails Minitest and system-test setup and prints setup
|
|
279
|
-
guidance for skipped files.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
and
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
297
|
+
guidance for skipped files. Keep the generated explanatory header in each
|
|
298
|
+
test: these are host-integration regression checks, not substitutes for
|
|
299
|
+
application-specific acceptance tests. Run every supported generated test
|
|
300
|
+
before and after the application conversion, while retaining
|
|
301
|
+
application-specific acceptance coverage for every inventoried flow.
|
|
302
|
+
|
|
303
|
+
The tests use the currently bundled gem and cover the shared upgrade boundary
|
|
304
|
+
— browser-submitted Turbo validation and mutation, Dialog and Sheet,
|
|
305
|
+
layout-owned Rails flash, Turbo Frame identity, redirects, and post-mutation
|
|
306
|
+
Phlex rendering. Their route is prepended only during each test, so host
|
|
307
|
+
catch-all routes remain compatible; an exact GET or PATCH route at the same
|
|
308
|
+
path is still rejected rather than masked. The route is restored afterward and
|
|
309
|
+
adds no production route or component source.
|
|
286
310
|
|
|
287
311
|
The endpoint deliberately inherits `ApplicationController` callbacks. If the
|
|
288
312
|
application requires authentication or current-account state, fill in the
|
|
@@ -291,20 +315,52 @@ and account-selection helpers used by ordinary integration and system tests.
|
|
|
291
315
|
Extend those application-owned classes rather than changing gem test support
|
|
292
316
|
or skipping host callbacks.
|
|
293
317
|
|
|
294
|
-
Run focused request and component tests, then compare the converted flows
|
|
295
|
-
|
|
318
|
+
Run focused request and component tests, then compare the converted flows
|
|
319
|
+
across the dated matrix in `docs/browser_support.md`, including Mobile Safari
|
|
320
|
+
where available, at wide and narrow widths. Exercise keyboard focus, dialogs and sheets,
|
|
296
321
|
Turbo submissions, errors, empty states, light/dark appearance, and dense
|
|
297
|
-
metadata.
|
|
322
|
+
metadata. Inspect computed styles for missing application classes, stacked
|
|
323
|
+
Button content, broken compound corners, double focus rings, clipping, and
|
|
324
|
+
theme drift. A green request suite does not prove that a tooltip, off-canvas
|
|
298
325
|
panel, interactive Card treatment, or responsive composition survived.
|
|
299
326
|
|
|
300
327
|
Finish by deleting copied components, helpers, and controllers; run
|
|
301
328
|
`bin/rails nitro_kit:doctor`; and record every remaining application-owned
|
|
302
|
-
fallback as either intentional product UI or a Nitro Kit coverage gap.
|
|
303
|
-
|
|
304
|
-
|
|
329
|
+
fallback as either intentional product UI or a Nitro Kit coverage gap. Use
|
|
330
|
+
`bin/rails nitro_kit:doctor --format=json` when migration automation needs
|
|
331
|
+
stable `status`, `label`, and `detail` fields.
|
|
332
|
+
|
|
333
|
+
Before declaring the migration complete, search for every
|
|
334
|
+
`desperately_need_a_class:` use and review each one. Aim for zero. Move layout,
|
|
335
|
+
spacing, width, responsive positioning, and application colors to an
|
|
336
|
+
application-owned wrapper; use documented component options and native
|
|
337
|
+
attributes for semantics and state; accept Nitro defaults where the difference
|
|
338
|
+
is incidental; and keep specialized controls or navigation application-owned
|
|
339
|
+
when Nitro is not the right abstraction. Remove generic class forwarding from
|
|
340
|
+
shared builders. A scoped wrapper rule may target ordinary descendant elements
|
|
341
|
+
when the application truly owns that layout behavior, such as fixed table
|
|
342
|
+
layout. Retain the escape only when a named external integration actually
|
|
343
|
+
requires a class hook, and document why.
|
|
344
|
+
|
|
345
|
+
For such a retained integration, `desperately_need_a_class:` accepts Rails-style
|
|
346
|
+
strings, symbols, nested arrays, or conditional hashes without manual
|
|
347
|
+
`compact.join(" ")` formatting. A Tailwind application may use its own
|
|
348
|
+
Tailwind-aware merger when that hook needs conflicting utility classes
|
|
349
|
+
resolved. Nitro does not require the dependency because its own component CSS
|
|
350
|
+
is static and classless.
|
|
351
|
+
|
|
352
|
+
Doctor inventories concrete Nitro Kit 1.x conventions: `nk_*` helpers,
|
|
305
353
|
generated files under `app/components/nitro_kit`, controllers under
|
|
306
|
-
`app/javascript/controllers/nk`, the old Floating UI and combobox packages
|
|
307
|
-
|
|
354
|
+
`app/javascript/controllers/nk`, and the old Floating UI and combobox packages.
|
|
355
|
+
When application CSS defines a `.btn` treatment, Doctor
|
|
356
|
+
also records rendered `btn` class usages as application-owned review work; it
|
|
357
|
+
does not assume every specialized control should become a Nitro Button. Doctor
|
|
358
|
+
also uses Ruby syntax trees to catch provable 2.0 runtime violations: direct
|
|
359
|
+
`id:` keywords on Table compound methods and statically icon-only
|
|
360
|
+
`NitroKit::Button`, Dropdown trigger, and Sheet trigger declarations without an
|
|
361
|
+
accessible name. Rendering remains
|
|
362
|
+
the final authority for dynamic wrappers and delegated component declarations.
|
|
363
|
+
Every finding includes a file and replacement or review instruction. Its
|
|
308
364
|
disposition is:
|
|
309
365
|
|
|
310
366
|
- `migrated` — no remaining occurrence in that category.
|
|
@@ -314,5 +370,7 @@ disposition is:
|
|
|
314
370
|
an application namespace, not as a Nitro shadow.
|
|
315
371
|
|
|
316
372
|
The inventory deliberately does not guess from generic component, JavaScript,
|
|
317
|
-
or dependency names
|
|
318
|
-
|
|
373
|
+
or dependency names beyond a button treatment the application itself defines.
|
|
374
|
+
Review application-owned product behavior separately and keep its migration
|
|
375
|
+
record with the application. Re-audit native buttons, Rails button helpers, and
|
|
376
|
+
application-owned button classes after Doctor is otherwise clean.
|
|
@@ -1,136 +1,62 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Application foundation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
smallest instance of the team model, not a separate personal mode that must be
|
|
6
|
-
replaced later.
|
|
3
|
+
**Audience:** Coding agents and developers starting an authenticated,
|
|
4
|
+
team-aware Nitro Kit application.
|
|
7
5
|
|
|
8
6
|
## Summary
|
|
9
7
|
|
|
10
|
-
- Model `User`, `Team`, and `Membership
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
compose with `SettingsLayout` and plain `FormSection` regions.
|
|
19
|
-
- When destination count warrants search, compose one `CommandPalette` in the
|
|
20
|
-
shell and render only routes the current membership may visit.
|
|
21
|
-
- Centralize cross-cutting feedback in one flash-driven toast region rendered
|
|
22
|
-
by the layout.
|
|
8
|
+
- Model `User`, `Team`, and `Membership`; roles belong to memberships, and
|
|
9
|
+
tenant-owned records load through `Current.team`.
|
|
10
|
+
- Use one `AppShell` for the authenticated product and one application-owned
|
|
11
|
+
content gutter inside `shell.main`.
|
|
12
|
+
- Put route titles and persistent actions in the shell `Toolbar`; keep
|
|
13
|
+
destinations in `AppNavigation`.
|
|
14
|
+
- Use links for settings destinations and one layout-level
|
|
15
|
+
`Toast::FlashMessages` region for server feedback.
|
|
23
16
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
Use `User`, `Team`, and `Membership` even when signup creates exactly one of
|
|
27
|
-
each. Put the role on `Membership`, not `User`, because authority belongs to a
|
|
28
|
-
person's relationship with a team. Create the first team and owner membership
|
|
29
|
-
in the same transaction as signup.
|
|
17
|
+
## Membership and current context
|
|
30
18
|
|
|
19
|
+
Create the first team and owner membership in the same transaction as signup.
|
|
31
20
|
Set `Current.user`, `Current.membership`, and `Current.team` from the session.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
product needs, usually owner, administrator, and member. Protect the last owner
|
|
35
|
-
in the domain model rather than only hiding a button.
|
|
36
|
-
|
|
37
|
-
Invitations should belong to a team, record the inviter and intended role,
|
|
38
|
-
expire, match the invited email, and be consumed when accepted. An existing
|
|
39
|
-
user accepts into a new membership; a new user completes signup and then uses
|
|
40
|
-
the same acceptance path.
|
|
41
|
-
|
|
42
|
-
## Compose one authenticated frame
|
|
43
|
-
|
|
44
|
-
Use one hybrid `AppShell` for the authenticated product. Keep the brand and
|
|
45
|
-
primary destinations in `AppNavigation`; place route titles and persistent
|
|
46
|
-
basic actions in the shell `Toolbar`. One wrapper immediately inside
|
|
47
|
-
`shell.main` owns responsive page padding for every route. The topbar and
|
|
48
|
-
sidebar header should use the shell's shared height and border tokens rather
|
|
49
|
-
than independent padding guesses.
|
|
50
|
-
|
|
51
|
-
Put infrequent account navigation after `navigation.spacer`, near the account
|
|
52
|
-
controls at the bottom of the sidebar:
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
shell.navigation do
|
|
56
|
-
AppNavigation(label: "Workspace navigation") do |navigation|
|
|
57
|
-
navigation.body do
|
|
58
|
-
navigation.item("Inventory", href: assets_path, icon: :archive)
|
|
59
|
-
navigation.item("Team", href: team_path, icon: :users)
|
|
60
|
-
navigation.spacer
|
|
61
|
-
navigation.item("Settings", href: settings_profile_path, icon: :settings)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Application code owns destinations and current-route policy. Nitro owns shell
|
|
68
|
-
layout, mobile disclosure, focus management, and navigation semantics.
|
|
69
|
-
For larger products, place one `CommandPalette` in the shell. Its native links
|
|
70
|
-
remain the navigation authority while Command-K or Control-K adds fast
|
|
71
|
-
filtering. Render the same authorized destination set the user can reach in
|
|
72
|
-
ordinary navigation; do not use the palette to bypass route policy.
|
|
73
|
-
When the destination set is too large or dynamic to render eagerly, pass
|
|
74
|
-
`search_url:` and return `CommandPalette::Results` from that endpoint. Keep the
|
|
75
|
-
same authorization scope on the initial links and every remote query.
|
|
76
|
-
Let the shell own viewport height and scrolling; do not add `min-height: 100vh`
|
|
77
|
-
to its main region or page wrapper. Put brand and destination icons through the
|
|
78
|
-
navigation slots so they share the same left alignment.
|
|
79
|
-
|
|
80
|
-
Team is an administration surface, not merely a roster. Include pending
|
|
81
|
-
invitations and the complete invite, role-change, removal, and revoke paths,
|
|
82
|
-
with last-owner protection in the model and authorization on every mutation.
|
|
83
|
-
|
|
84
|
-
Authentication is a standalone form surface. Inside `AuthShell`, use Rails
|
|
85
|
-
`form_with` with `NitroKit::FormBuilder` and put the visible fields, submit
|
|
86
|
-
control, and related recovery link in one `form.group`. The group owns their
|
|
87
|
-
vertical rhythm; `AuthShell` owns only the page container and spacing between
|
|
88
|
-
its major regions.
|
|
21
|
+
Use `Current.user` for authorship and audit fields; scope tenant data through
|
|
22
|
+
the team. Protect the last owner in the model.
|
|
89
23
|
|
|
90
|
-
|
|
24
|
+
Invitations belong to a team, record inviter and role, expire, and match the
|
|
25
|
+
invited email. Existing and new users should share one acceptance path.
|
|
91
26
|
|
|
92
|
-
|
|
93
|
-
stable subsections such as Profile, Notifications, Appearance, and Password;
|
|
94
|
-
its content renders the selected form. Use `FormSection` for genuinely distinct
|
|
95
|
-
form regions and ordinary whitespace or dividers between them.
|
|
27
|
+
## Authenticated shell
|
|
96
28
|
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
still works without JavaScript.
|
|
29
|
+
Use one `AppShell`, normally `layout: :hybrid`, for authenticated routes.
|
|
30
|
+
`AppNavigation` owns brand and destinations; a `Toolbar` in `shell.topbar`
|
|
31
|
+
owns the route's single `h1` and persistent actions. One wrapper inside
|
|
32
|
+
`shell.main` owns responsive page padding. Do not add another viewport-height
|
|
33
|
+
or outer-padding rule in child pages.
|
|
103
34
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
widths without JavaScript.
|
|
35
|
+
Application code owns destinations, authorization, and current-route policy.
|
|
36
|
+
Nitro owns responsive disclosure and focus behavior. Put infrequent account
|
|
37
|
+
destinations after `navigation.spacer`. Add one `CommandPalette` only when the
|
|
38
|
+
destination count warrants search, and render only authorized routes.
|
|
109
39
|
|
|
110
|
-
|
|
40
|
+
Use `AuthShell` with Rails `form_with` and `NitroKit::FormBuilder` for
|
|
41
|
+
authentication. Put visible fields, submit, and recovery link in one
|
|
42
|
+
`form.group`.
|
|
111
43
|
|
|
112
|
-
|
|
113
|
-
ordinary Rails flash and `303 See Other` redirects from controllers.
|
|
44
|
+
## Settings and feedback
|
|
114
45
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
browser's top layer keeps that inline dialog clear of ancestor clipping and
|
|
120
|
-
stacking contexts. Put record deletion on the edit route rather than adding a
|
|
121
|
-
danger surface to every operational show page.
|
|
46
|
+
Render `SettingsLayout` inside the normal shell. Settings destinations are
|
|
47
|
+
links with `aria-current="page"`, not Buttons. Use `SettingsSection` only for
|
|
48
|
+
distinct form regions; do not wrap every subsection in a Card or repeat the
|
|
49
|
+
route title.
|
|
122
50
|
|
|
123
|
-
|
|
51
|
+
Render `NitroKit::Toast::FlashMessages` once in the application layout. Keep
|
|
52
|
+
ordinary Rails flash and `303 See Other` redirects. Use the dedicated
|
|
53
|
+
[destructive action](destructive_action.md) and
|
|
54
|
+
[flash](flash_and_toast.md) patterns for those flows.
|
|
124
55
|
|
|
125
|
-
|
|
56
|
+
## Acceptance checklist
|
|
126
57
|
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
- successful mutations redirect with `303` and invalid forms render with
|
|
133
|
-
`422`;
|
|
134
|
-
- confirmation has both cancel and confirm coverage;
|
|
135
|
-
- the shell, headings, tables, and forms remain usable without custom request
|
|
136
|
-
JavaScript.
|
|
58
|
+
- Signup or sign-in selects a membership and team.
|
|
59
|
+
- Cross-team records cannot be loaded.
|
|
60
|
+
- Owner, administrator, and member policy differs where intended.
|
|
61
|
+
- Populated, empty, invalid, narrow, settings, and destructive states work.
|
|
62
|
+
- Successful mutations redirect with `303`; invalid forms render with `422`.
|