nitro_kit 2.0.0.alpha.1 → 2.0.0.alpha.2
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 +21 -0
- data/README.md +4 -2
- data/app/assets/stylesheets/nitro_kit.css +28 -14
- data/app/components/nitro_kit/checkbox_group.rb +5 -2
- data/app/components/nitro_kit/field.rb +1 -1
- data/app/components/nitro_kit/radio_button_group.rb +5 -2
- data/docs/agent_native_spec.md +2 -2
- data/docs/component_contracts.md +4 -4
- data/docs/migration_1_to_2.md +1 -1
- data/docs/new_app_strategy.md +4 -6
- data/docs/rails_integration.md +1 -1
- data/lib/nitro_kit/upgrade_smoke_test.rb +2 -2
- data/lib/nitro_kit/version.rb +1 -1
- data/src/stylesheets/nitro_kit/components/alert.css +1 -1
- data/src/stylesheets/nitro_kit/components/dropzone.css +10 -1
- data/src/stylesheets/nitro_kit/components/page_header.css +5 -6
- data/src/stylesheets/nitro_kit/components/palette.css +1 -1
- data/src/stylesheets/nitro_kit/components/table.css +8 -2
- data/src/stylesheets/nitro_kit/tokens.css +3 -3
- metadata +1 -2
- data/docs/implementation_plan.md +0 -279
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2cfb9e904d8890216a6c778b5dd10655e22f77fa7508fd4d9eb9521453b61cd8
|
|
4
|
+
data.tar.gz: 3c9564fa27b68e1a10c1febe8b7104416251a5203b1bd25aa89b1617cab1e116
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de02c676e41ea2cb0d674de967efa9c73aa8d6f65195af0bfae0ce35e305dc9fb5e53979659e5feed738cf0fdc4e968902931be726a5d1bd67f22a400ce3c0b2
|
|
7
|
+
data.tar.gz: 2ffdd83fe0f4005c5146a3d9e4fa13e7ce8a6b6d84d0a7e881aace598a6ff938836b20c15f29bde5f9d72e9bed8ef2e4a6b8a083add6986a0acdacb653221a4e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.0.alpha.2
|
|
4
|
+
|
|
5
|
+
This alpha keeps the 2.0 API experimental while incorporating the first full
|
|
6
|
+
catalog-pattern and external-agent testing pass.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Add the setup-only installer, project-local agent guidance, diagnostics, and
|
|
11
|
+
initialization prompt used by new and existing Rails applications.
|
|
12
|
+
- Align desktop page-header actions with the title edge and improve nested
|
|
13
|
+
table and responsive dropzone composition.
|
|
14
|
+
- Exercise desktop pointer capabilities in browser CI with environment-matched
|
|
15
|
+
Chrome tooling.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Improve warning, destructive, form-error, and dark-mode contrast.
|
|
20
|
+
- Preserve native list and fieldset semantics in form errors and grouped
|
|
21
|
+
controls.
|
|
22
|
+
- Fix table-cell alignment leakage and cramped upload actions at narrow widths.
|
|
23
|
+
|
|
3
24
|
## 2.0.0.alpha.1
|
|
4
25
|
|
|
5
26
|
Nitro Kit 2.0 alpha is a ground-up, intentionally incompatible rebuild around gem-owned Phlex composition. It replaces the generated, helper-driven 1.x architecture with a versioned component system owned by the gem.
|
data/README.md
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
**Rails front-end for the agent era.**
|
|
8
8
|
|
|
9
|
-
Nitro Kit is a gem-owned, agent-native UI system for Ruby on Rails. The `2.0.0.alpha.
|
|
9
|
+
Nitro Kit is a gem-owned, agent-native UI system for Ruby on Rails. The `2.0.0.alpha.2` prerelease is a ground-up rebuild under active testing and must not be treated as stable. New documentation and the Nitro Kit Pro alpha catalog are coming next.
|
|
10
10
|
|
|
11
11
|
[](https://rubygems.org/gems/nitro_kit)
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
|
+
Install the alpha explicitly and keep it pinned while evaluating it.
|
|
16
|
+
|
|
15
17
|
```ruby
|
|
16
|
-
gem "nitro_kit", "2.0.0.alpha.
|
|
18
|
+
gem "nitro_kit", "2.0.0.alpha.2"
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
Use the released gem and commit `Gemfile` with `Gemfile.lock`. Before upgrading, review the changelog, run `bundle update nitro_kit`, rerun the installer, and test the application.
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
--nk-color-success-content: oklch(0.393 0.095 152.535);
|
|
106
106
|
--nk-color-warning: oklch(0.681 0.162 75.834);
|
|
107
107
|
--nk-color-warning-content: oklch(0.421 0.095 57.708);
|
|
108
|
-
--nk-color-danger: oklch(0.
|
|
108
|
+
--nk-color-danger: oklch(0.577 0.237 25.331);
|
|
109
109
|
--nk-color-danger-foreground: oklch(1 0 0);
|
|
110
110
|
--nk-color-danger-content: oklch(0.444 0.177 26.899);
|
|
111
111
|
--nk-color-overlay: oklch(0 0 0 / 0.5);
|
|
@@ -200,8 +200,8 @@
|
|
|
200
200
|
transparent 10%
|
|
201
201
|
);
|
|
202
202
|
--nk-color-danger-hover: light-dark(
|
|
203
|
-
oklch(0.
|
|
204
|
-
oklch(0.
|
|
203
|
+
oklch(0.54 0.237 25.331),
|
|
204
|
+
oklch(0.59 0.237 25.331)
|
|
205
205
|
);
|
|
206
206
|
--nk-app-shell-background: var(--nk-color-canvas);
|
|
207
207
|
--nk-app-shell-sidebar-background: var(--nk-color-surface);
|
|
@@ -578,7 +578,7 @@
|
|
|
578
578
|
|
|
579
579
|
:where([data-nk="alert"] > [data-slot="alert-description"]) {
|
|
580
580
|
max-width: var(--nk-content-lg);
|
|
581
|
-
color:
|
|
581
|
+
color: var(--_nk-alert-foreground);
|
|
582
582
|
text-wrap: pretty;
|
|
583
583
|
}
|
|
584
584
|
}
|
|
@@ -3485,7 +3485,9 @@
|
|
|
3485
3485
|
}
|
|
3486
3486
|
|
|
3487
3487
|
:where([data-nk="dropzone"] [data-slot="dropzone-remove-control"]) {
|
|
3488
|
-
|
|
3488
|
+
grid-column: -2 / -1;
|
|
3489
|
+
justify-self: end;
|
|
3490
|
+
min-block-size: var(--nk-control-height-md);
|
|
3489
3491
|
padding-inline: calc(var(--nk-space) * 2);
|
|
3490
3492
|
color: var(--nk-color-foreground);
|
|
3491
3493
|
background: var(--nk-color-surface);
|
|
@@ -3500,6 +3502,13 @@
|
|
|
3500
3502
|
border-color: var(--nk-color-primary);
|
|
3501
3503
|
}
|
|
3502
3504
|
|
|
3505
|
+
:where(
|
|
3506
|
+
[data-nk="dropzone"][data-presentation="minimal"][data-state="idle"]
|
|
3507
|
+
> [data-slot="dropzone-status"]
|
|
3508
|
+
) {
|
|
3509
|
+
text-align: center;
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3503
3512
|
:where([data-nk="dropzone"][data-state="uploading"]) {
|
|
3504
3513
|
border-color: var(--nk-color-info);
|
|
3505
3514
|
}
|
|
@@ -5178,11 +5187,9 @@
|
|
|
5178
5187
|
}
|
|
5179
5188
|
|
|
5180
5189
|
/* Every text slot owns the first column, so the eyebrow, title, and
|
|
5181
|
-
description each keep a row of their own.
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
what used to seat the actions beside the eyebrow and push the title down
|
|
5185
|
-
onto a line of its own. */
|
|
5190
|
+
description each keep a row of their own. Actions explicitly occupy
|
|
5191
|
+
the first row so their top edge aligns with the header instead of
|
|
5192
|
+
dropping beside the final line of supporting text. */
|
|
5186
5193
|
:where(
|
|
5187
5194
|
[data-nk="page-header"]:has(> [data-slot="page-header-actions"])
|
|
5188
5195
|
> :not([data-slot="page-header-actions"])
|
|
@@ -5192,8 +5199,9 @@
|
|
|
5192
5199
|
|
|
5193
5200
|
:where([data-nk="page-header"] > [data-slot="page-header-actions"]) {
|
|
5194
5201
|
grid-column: 2;
|
|
5202
|
+
grid-row: 1;
|
|
5195
5203
|
justify-self: end;
|
|
5196
|
-
align-self:
|
|
5204
|
+
align-self: start;
|
|
5197
5205
|
margin-block-start: 0;
|
|
5198
5206
|
}
|
|
5199
5207
|
}
|
|
@@ -5394,7 +5402,7 @@
|
|
|
5394
5402
|
[data-nk="badge"][data-color="warning"],
|
|
5395
5403
|
[data-nk="alert"][data-variant="warning"]
|
|
5396
5404
|
) {
|
|
5397
|
-
--_nk-palette-light: oklch(55
|
|
5405
|
+
--_nk-palette-light: oklch(55% 0.163 48.998);
|
|
5398
5406
|
--_nk-palette-dark: oklch(92.4% 0.12 95.746);
|
|
5399
5407
|
--_nk-palette-tint: oklch(82.8% 0.189 84.429);
|
|
5400
5408
|
}
|
|
@@ -6502,10 +6510,16 @@
|
|
|
6502
6510
|
}
|
|
6503
6511
|
|
|
6504
6512
|
@layer nitro-kit.compound {
|
|
6505
|
-
:where(
|
|
6513
|
+
:where(
|
|
6514
|
+
[data-nk="table"] [data-slot="table-header"][data-align="center"],
|
|
6515
|
+
[data-nk="table"] [data-slot="table-cell"][data-align="center"]
|
|
6516
|
+
) {
|
|
6506
6517
|
text-align: center;
|
|
6507
6518
|
}
|
|
6508
|
-
:where(
|
|
6519
|
+
:where(
|
|
6520
|
+
[data-nk="table"] [data-slot="table-header"][data-align="right"],
|
|
6521
|
+
[data-nk="table"] [data-slot="table-cell"][data-align="right"]
|
|
6522
|
+
) {
|
|
6509
6523
|
font-variant-numeric: tabular-nums;
|
|
6510
6524
|
text-align: right;
|
|
6511
6525
|
}
|
|
@@ -52,8 +52,11 @@ module NitroKit
|
|
|
52
52
|
attributes: {
|
|
53
53
|
id: @id,
|
|
54
54
|
disabled: @disabled,
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
data: {
|
|
56
|
+
orientation: @orientation,
|
|
57
|
+
presentation: @presentation,
|
|
58
|
+
required: @required ? "true" : nil
|
|
59
|
+
}.compact
|
|
57
60
|
}.compact,
|
|
58
61
|
html:,
|
|
59
62
|
aria:,
|
|
@@ -45,8 +45,11 @@ module NitroKit
|
|
|
45
45
|
attributes: {
|
|
46
46
|
id: @id,
|
|
47
47
|
disabled: @disabled,
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
data: {
|
|
49
|
+
orientation: @orientation,
|
|
50
|
+
presentation: @presentation,
|
|
51
|
+
required: @required ? "true" : nil
|
|
52
|
+
}.compact
|
|
50
53
|
}.compact,
|
|
51
54
|
html:,
|
|
52
55
|
aria:,
|
data/docs/agent_native_spec.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Nitro Kit 2.0 — agent-native Phlex UI system
|
|
2
2
|
|
|
3
|
-
This is the canonical architecture specification for the stable Nitro Kit 2.0 line.
|
|
3
|
+
This is the canonical architecture specification for the stable Nitro Kit 2.0 line. `tk` is the source of truth for live work status, and [`component_contracts.md`](component_contracts.md) records the shipped Ruby and integration contracts.
|
|
4
4
|
|
|
5
5
|
Nitro Kit is a gem-owned, versioned UI system for Rails. Developers and coding agents compose application interfaces in Ruby with Phlex. Nitro Kit owns component behavior, rendered structure, and default aesthetics. Applications own product code and documented theme overrides, not copies of Nitro Kit internals.
|
|
6
6
|
|
|
@@ -404,7 +404,7 @@ These ideas require new evidence and a deliberate future API decision. Their app
|
|
|
404
404
|
|
|
405
405
|
The pivot was delivered in evidence-seeking stages: establish the kernel and representative vertical slice, migrate atoms, build atom-only product flows, record repeated friction, extract only proven layouts and page sections, expand the gallery, remove 1.x, then consolidate behavior, packaging, and documentation.
|
|
406
406
|
|
|
407
|
-
The first evidence pass rejected more abstractions than it accepted. `Spacer`, `Split`, `Frame`, App/Marketing shells, AuthenticationPanel, and ProgressSteps stayed out because those flows did not establish a stable cross-domain responsibility. That pass initially extracted separate `VStack` and `HStack` components plus a fixed three-column Grid. The later responsive-layout consolidation superseded those APIs with unified `Flex` and responsive `Grid
|
|
407
|
+
The first evidence pass rejected more abstractions than it accepted. `Spacer`, `Split`, `Frame`, App/Marketing shells, AuthenticationPanel, and ProgressSteps stayed out because those flows did not establish a stable cross-domain responsibility. That pass initially extracted separate `VStack` and `HStack` components plus a fixed three-column Grid. The later responsive-layout consolidation superseded those APIs with unified `Flex` and responsive `Grid`. The application-layout mandate and the Sidebar and Top Navigation reference audit supplied enough repeated responsibility to approve `AppShell`; MarketingShell, AuthenticationPanel, and speculative layout primitives remain rejected.
|
|
408
408
|
|
|
409
409
|
## Definition of done
|
|
410
410
|
|
data/docs/component_contracts.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Nitro Kit 2.0 component contracts
|
|
2
2
|
|
|
3
|
-
This is the shipped public catalog for `2.0.0.alpha.
|
|
3
|
+
This is the shipped public catalog for `2.0.0.alpha.2`. It describes current Ruby construction, rendered roots, closed vocabularies, compound cardinalities, and integration boundaries. Other candidate abstractions and the historical delivery plan are not public API.
|
|
4
4
|
|
|
5
5
|
## Shared component boundary
|
|
6
6
|
|
|
@@ -44,17 +44,17 @@ Every default string a person can read or hear comes from the engine-loaded `nit
|
|
|
44
44
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
45
|
| `AppearancePicker` | required `id:`; `label:` defaulting to `I18n.t("nitro_kit.appearance_picker.label")`; `presentation: :segmented`; `preference: :system` | `fieldset`, `label`, or `div[data-nk=appearance-picker]`; presentations `segmented radios select dropdown`; preferences `light dark system` | Owns labelled native choices or an icon-only Dropdown trigger with icon-led preference buttons. `preference:` renders the server-persisted choice as the initial `data-state`, checked radio, selected option, and trigger icon, so a stored preference does not flash. It requests changes from and subscribes to the document runtime; zero or many picker instances do not duplicate document media/storage listeners. `id:` follows the AppShell identifier contract. Preference names come from `nitro_kit.appearance_picker.preferences.*`. |
|
|
46
46
|
| `Checkbox` | `label: nil`, `description: nil`, `id: nil`, `name: nil`, `value: "1"`, `unchecked_value: "0"`, `include_hidden: true`, `checked: false`, `indeterminate: false`, `disabled: false`, `required: false`, `invalid: false`, `size: :md`, `control_html: {}`, `control_aria: {}`, `control_data: {}` | `div[data-nk=checkbox]` containing a native checkbox input; sizes `md lg` | Requires label text, block content, or an accessible control name; a description requires a label or block and a non-blank String `id:` to bind `aria-describedby`. A named checkbox emits its unchecked hidden input by default, and `include_hidden: true` with a nil `unchecked_value` raises. `invalid: true` sets `aria-invalid` on the control. Checked state stays native; only `indeterminate: true` mounts the `nk--checkable` enhancer, which applies the native DOM property and owns `data-state="indeterminate"`. |
|
|
47
|
-
| `CheckboxGroup` | required `legend:`, `options:`, `name:`; `value: []`, `id: nil`, `description: nil`, `orientation: :vertical`, `presentation: :list`, `unchecked_value: ""`, `include_hidden: true`, `disabled: false`, `required: false`, `size: :md` | native `fieldset[data-nk=checkbox-group]`; orientations `vertical horizontal`; presentations `list cards`; sizes `md lg` | Options are non-empty `Choice` values with unique values and IDs. Names normalize to `[]`; one group-level unchecked input is emitted by default and its `unchecked_value` stays Rails' array sentinel `""`. A name with no id-safe characters raises instead of skipping IDs. `required: true` marks
|
|
47
|
+
| `CheckboxGroup` | required `legend:`, `options:`, `name:`; `value: []`, `id: nil`, `description: nil`, `orientation: :vertical`, `presentation: :list`, `unchecked_value: ""`, `include_hidden: true`, `disabled: false`, `required: false`, `size: :md` | native `fieldset[data-nk=checkbox-group]`; orientations `vertical horizontal`; presentations `list cards`; sizes `md lg` | Options are non-empty `Choice` values with unique values and IDs. Names normalize to `[]`; one group-level unchecked input is emitted by default and its `unchecked_value` stays Rails' array sentinel `""`. A name with no id-safe characters raises instead of skipping IDs. `required: true` marks application state as `data-required=true`; it does not emit invalid `aria-required` on the native fieldset or require every checkbox. The legend and description scope the whole group, so choices do not repeat the description through `aria-describedby`. |
|
|
48
48
|
| `ControlGroup` | `label: nil`, `id: nil` | `div[data-nk=control-group]`; optional `role=group` when labelled | Requires direct content. Joins direct Input, Select, and Button children without taking ownership of their values or behavior. `addon(text)` renders a textual prefix, suffix, or unit. The group owns shared borders and logical corner geometry for copy fields, URL builders, and compact filter submissions. |
|
|
49
49
|
| `Dropzone` | required `id:`, `name:`; `label:` defaulting to `I18n.t("nitro_kit.dropzone.label")`, `description: nil`, `presentation: :input` (`input minimal`), `direct_upload: true`, `multiple: false`, `accept: nil`, `max_files: 1`, `max_bytes: nil`, `disabled: false`, `required: false` | `div[data-nk=dropzone][data-presentation]`; states `idle drag uploading success error disabled` | Owns a labelled native input, description/error/live status, preview list, native progress, and remove controls. `label:` is the visible prompt heading and renders in the `dropzone-title` slot; it replaced the former `title:` keyword. Every other user-facing string comes from the `nitro_kit.dropzone.*` locale scope, and `CONTROLLER_MESSAGE_KEYS` hands the runtime strings to `nk--dropzone` as Stimulus values so no English lives in JavaScript. Limits are positive and consistent; `max_files` must be 1 unless `multiple: true`. Keyboard selection and `direct_upload: false` preserve ordinary form submission. `presentation: :minimal` hides the native input visually while keeping it focusable and named, so the drop target is the only visible affordance. |
|
|
50
|
-
| `Field` | optional Rails form builder and field name; see the full signature below | `div[data-nk=field][data-field-type=…]`; types listed below | Default rendering owns label, description, control, and errors. A render block replaces the default composition. `label`, `description`, `control`, and `errors` remain available to custom compositions. The error
|
|
50
|
+
| `Field` | optional Rails form builder and field name; see the full signature below | `div[data-nk=field][data-field-type=…]`; types listed below | Default rendering owns label, description, control, and errors. A render block replaces the default composition. `label`, `description`, `control`, and `errors` remain available to custom compositions. The error list keeps native list semantics and uses `aria-live=assertive`; controls reference it through `aria-describedby`. Derived labels use `human_attribute_name` when the form object supplies one. `as: :radio_group` requires a legend and rejects `label: false`, falling back to `I18n.t("nitro_kit.field.options_legend")` when neither an explicit nor a derived label exists. `as: :combobox` binds the field label to `#{id}-input` through `for` and `aria-labelledby` and wires description, error, and invalid state onto the combobox input. |
|
|
51
51
|
| `RichTextArea` | required captured editor content as an `ActiveSupport::SafeBuffer`; `id: nil` | `div[data-nk=rich-text-area]` wrapping `div[data-slot=rich-text-area-editor]` | `Field(as: :rich_text)` through `FormBuilder#field` is the expected path; construct `RichTextArea` directly only for a standalone editor. Wraps trusted output from the host application's rich-text helper. Nitro owns Field composition and theme variables; the editor owns inputs, attachments, and behavior. |
|
|
52
52
|
| `FieldGroup` | no component-specific keywords | `div[data-nk=field-group]` | Requires a content block. It is the default vertical rhythm boundary for a standalone form's visible fields, submit control, and related links. |
|
|
53
53
|
| `Fieldset` | `legend: nil`, `description: nil`, `disabled: false`, `name: nil` | native `fieldset[data-nk=fieldset]` | Requires a content block and a legend through the constructor or the matching `legend` compound method; `description` accepts either form too. Declarations may appear anywhere in the block and always render legend → description → fields. |
|
|
54
54
|
| `Input` | `type: :text`, `id: nil`, `name: nil`, `value: nil`, `placeholder: nil`, `disabled: false`, `readonly: false`, `required: false`, `autocomplete: nil`, `min: nil`, `max: nil`, `step: nil`, `minlength: nil`, `maxlength: nil`, `multiple: false`, `accept: nil`, `pattern: nil`, `inputmode: nil`, `checked: nil` | native `input[data-nk=input]`; types `button checkbox color date datetime-local email file hidden month number password radio range search tel text time url week` | Every owned attribute is a keyword; passing one through `html:` raises and names the keyword. `type: :file` with a `value:` raises. Boolean attributes and length constraints are validated; minimum length cannot exceed maximum. Read-only controls have their own muted treatment. `type: :date` is the only date control; its CSS normalizes Safari's internal date editor alignment, so `Field`/`FormBuilder` `as: :date` inherits the fix. |
|
|
55
55
|
| `Label` | optional text; `for: nil`, `id: nil` | native `label[data-nk=label]` | Requires non-blank text or a content block. |
|
|
56
56
|
| `RadioButton` | `label: nil`, `description: nil`, `id: nil`, `name: nil`, `value: "1"`, `checked: false`, `disabled: false`, `required: false`, `invalid: false`, `size: :md`, `control_html: {}`, `control_aria: {}`, `control_data: {}` | `div[data-nk=radio-button]` containing a native radio input; sizes `md lg` | Requires label text, block content, or an accessible control name; a description requires a label or block. `invalid: true` sets `aria-invalid` on the control. Selection is entirely native: no Stimulus controller and no mirrored `data-state`. |
|
|
57
|
-
| `RadioButtonGroup` | required `legend:`, `options:`, `name:`; `value: nil`, `id: nil`, `description: nil`, `orientation: :vertical`, `presentation: :list`, `disabled: false`, `required: false`, `size: :md` | native `fieldset[data-nk=radio-button-group]`; orientations `vertical horizontal`; presentations `list cards segmented`; sizes `md lg` | Options are non-empty `Choice` values with unique values and IDs. A non-nil `value:` is compared by string value; `value: nil` selects nothing, including a choice whose value is `""`. A name with no id-safe characters raises instead of skipping IDs. `required: true` marks
|
|
57
|
+
| `RadioButtonGroup` | required `legend:`, `options:`, `name:`; `value: nil`, `id: nil`, `description: nil`, `orientation: :vertical`, `presentation: :list`, `disabled: false`, `required: false`, `size: :md` | native `fieldset[data-nk=radio-button-group]`; orientations `vertical horizontal`; presentations `list cards segmented`; sizes `md lg` | Options are non-empty `Choice` values with unique values and IDs. A non-nil `value:` is compared by string value; `value: nil` selects nothing, including a choice whose value is `""`. A name with no id-safe characters raises instead of skipping IDs. `required: true` marks application state as `data-required=true` and every native radio `required`; it does not emit invalid `aria-required` on the fieldset. The legend and description scope the whole group, so choices do not repeat the description through `aria-describedby`. |
|
|
58
58
|
| `Select` | `options: []`, `option_tags: nil`, `id: nil`, `name: nil`, `value: nil`, `include_blank: nil`, `prompt: nil`, `disabled: false`, `required: false`, `multiple: false`, `autocomplete: nil`, `control_html: {}`, `control_aria: {}`, `control_data: {}` | `span[data-nk=select]` wrapping a native select | `options:` and captured `option_tags:` are mutually exclusive, as are `include_blank:` and `prompt:`. `id:` and the `control_*` bags address the inner select; `html:`, `aria:`, and `data:` address the root span. Multiple names normalize to `[]`, drop the toggle icon, and accept array values. |
|
|
59
59
|
| `Switch` | `label: nil`, `description: nil`, `id: nil`, `name: nil`, `value: "1"`, `unchecked_value: "0"`, `include_hidden: true`, `checked: false`, `disabled: false`, `required: false`, `invalid: false`, `size: :md`, `control_html: {}`, `control_aria: {}`, `control_data: {}` | `div[data-nk=switch]` containing `input[type=checkbox][role=switch]`; sizes `md lg` | Uses native checkbox submission with an unchecked hidden input by default; checked state stays native, with no Stimulus controller and no mirrored `data-state`. Requires label text, block content, or an accessible control name; a description requires a label or block and is nested in the content slot beside the label text and connected with `aria-describedby`. The ARIA-only form renders the bare control and track without a label wrapper. `invalid: true` sets `aria-invalid` on the control. `role` is owned by Switch: a `control_html: { role: ... }` value is replaced by `role="switch"`. |
|
|
60
60
|
| `Textarea` | `id: nil`, `name: nil`, `value: nil`, `placeholder: nil`, `disabled: false`, `readonly: false`, `required: false`, `autocomplete: nil`, `rows: nil`, `cols: nil`, `minlength: nil`, `maxlength: nil`, `wrap: nil` | native `textarea[data-nk=textarea]`; wraps `soft hard off` | Every owned attribute is a keyword; passing one through `html:` raises and names the keyword. Rows and columns must be positive; lengths must be non-negative; minimum length cannot exceed maximum. Read-only controls have their own muted treatment. |
|
data/docs/migration_1_to_2.md
CHANGED
|
@@ -9,7 +9,7 @@ Treat a 1.x migration as a product-flow review, not a helper rename. Nitro Kit
|
|
|
9
9
|
Add the 2.0 alpha prerelease to the application's Gemfile:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem "nitro_kit", "2.0.0.alpha.
|
|
12
|
+
gem "nitro_kit", "2.0.0.alpha.2"
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Bundler records the exact released version in `Gemfile.lock`; commit `Gemfile`
|
data/docs/new_app_strategy.md
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Recommend a Rails application template for new applications, not a return to Nitro Kit's old component-copying generator.
|
|
4
4
|
|
|
5
|
-
Rails application templates are designed to configure a new app during `rails new`, can add gems, and can run generators after Bundler finishes.
|
|
5
|
+
Rails application templates are designed to configure a new app during `rails new`, can add gems, and can run generators after Bundler finishes. Nitro Kit's starter therefore remains a thin one-command entry point:
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
rails new my_app -m https://nitrokit.dev/template.rb
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
The template
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
layout that renders the stylesheet, appearance bootstrap, and flash toasts. It
|
|
15
|
-
must not copy Nitro components or their controllers.
|
|
11
|
+
The template adds Nitro Kit and invokes `nitro_kit:install`. The generator owns
|
|
12
|
+
the project-local skills and `AGENTS.md`. The template does not copy Nitro
|
|
13
|
+
components, controllers, authentication, teams, billing, or product models.
|
|
16
14
|
|
|
17
15
|
Existing applications install the gem directly and run the setup generator.
|
|
18
16
|
Agent discovery, version-matched skill routing, diagnostics, and initialization
|
data/docs/rails_integration.md
CHANGED
|
@@ -7,7 +7,7 @@ Nitro Kit 2.0 uses Rails where Rails owns important application semantics: model
|
|
|
7
7
|
Add the 2.0 alpha prerelease to the application's Gemfile:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem "nitro_kit", "2.0.0.alpha.
|
|
10
|
+
gem "nitro_kit", "2.0.0.alpha.2"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Bundler records the exact released version in `Gemfile.lock`; commit `Gemfile`
|
|
@@ -244,7 +244,7 @@ module NitroKit
|
|
|
244
244
|
assert_response :unprocessable_entity
|
|
245
245
|
assert_select "turbo-frame##{UpgradeSmoke::FRAME_ID}", count: 1 do
|
|
246
246
|
assert_select "input#profile_name[value='x'][aria-invalid='true'][aria-describedby~='profile_name-errors']"
|
|
247
|
-
assert_select "ul#profile_name-errors[data-slot='field-error'][
|
|
247
|
+
assert_select "ul#profile_name-errors[data-slot='field-error'][aria-live='assertive']:not([role]) > li", count: 1
|
|
248
248
|
end
|
|
249
249
|
assert_select "section[data-nk='toast'] [data-nk='toast-item'][data-variant='error']"
|
|
250
250
|
end
|
|
@@ -316,7 +316,7 @@ module NitroKit
|
|
|
316
316
|
find("#nitro-kit-upgrade-smoke-form button[type='submit']").click
|
|
317
317
|
|
|
318
318
|
assert_selector "#profile_name[value='x'][aria-invalid='true'][aria-describedby~='profile_name-errors']"
|
|
319
|
-
assert_selector "ul#profile_name-errors[data-slot='field-error'][
|
|
319
|
+
assert_selector "ul#profile_name-errors[data-slot='field-error'][aria-live='assertive']:not([role]) > li", count: 1
|
|
320
320
|
assert_selector "body section[data-nk='toast'] [data-nk='toast-item'][data-variant='error']"
|
|
321
321
|
assert_stable_frame
|
|
322
322
|
|
data/lib/nitro_kit/version.rb
CHANGED
|
@@ -115,7 +115,9 @@
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
:where([data-nk="dropzone"] [data-slot="dropzone-remove-control"]) {
|
|
118
|
-
|
|
118
|
+
grid-column: -2 / -1;
|
|
119
|
+
justify-self: end;
|
|
120
|
+
min-block-size: var(--nk-control-height-md);
|
|
119
121
|
padding-inline: calc(var(--nk-space) * 2);
|
|
120
122
|
color: var(--nk-color-foreground);
|
|
121
123
|
background: var(--nk-color-surface);
|
|
@@ -130,6 +132,13 @@
|
|
|
130
132
|
border-color: var(--nk-color-primary);
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
:where(
|
|
136
|
+
[data-nk="dropzone"][data-presentation="minimal"][data-state="idle"]
|
|
137
|
+
> [data-slot="dropzone-status"]
|
|
138
|
+
) {
|
|
139
|
+
text-align: center;
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
:where([data-nk="dropzone"][data-state="uploading"]) {
|
|
134
143
|
border-color: var(--nk-color-info);
|
|
135
144
|
}
|
|
@@ -40,11 +40,9 @@
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/* Every text slot owns the first column, so the eyebrow, title, and
|
|
43
|
-
description each keep a row of their own.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
what used to seat the actions beside the eyebrow and push the title down
|
|
47
|
-
onto a line of its own. */
|
|
43
|
+
description each keep a row of their own. Actions explicitly occupy
|
|
44
|
+
the first row so their top edge aligns with the header instead of
|
|
45
|
+
dropping beside the final line of supporting text. */
|
|
48
46
|
:where(
|
|
49
47
|
[data-nk="page-header"]:has(> [data-slot="page-header-actions"])
|
|
50
48
|
> :not([data-slot="page-header-actions"])
|
|
@@ -54,8 +52,9 @@
|
|
|
54
52
|
|
|
55
53
|
:where([data-nk="page-header"] > [data-slot="page-header-actions"]) {
|
|
56
54
|
grid-column: 2;
|
|
55
|
+
grid-row: 1;
|
|
57
56
|
justify-self: end;
|
|
58
|
-
align-self:
|
|
57
|
+
align-self: start;
|
|
59
58
|
margin-block-start: 0;
|
|
60
59
|
}
|
|
61
60
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
[data-nk="badge"][data-color="warning"],
|
|
29
29
|
[data-nk="alert"][data-variant="warning"]
|
|
30
30
|
) {
|
|
31
|
-
--_nk-palette-light: oklch(55
|
|
31
|
+
--_nk-palette-light: oklch(55% 0.163 48.998);
|
|
32
32
|
--_nk-palette-dark: oklch(92.4% 0.12 95.746);
|
|
33
33
|
--_nk-palette-tint: oklch(82.8% 0.189 84.429);
|
|
34
34
|
}
|
|
@@ -100,10 +100,16 @@
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
@layer nitro-kit.compound {
|
|
103
|
-
:where(
|
|
103
|
+
:where(
|
|
104
|
+
[data-nk="table"] [data-slot="table-header"][data-align="center"],
|
|
105
|
+
[data-nk="table"] [data-slot="table-cell"][data-align="center"]
|
|
106
|
+
) {
|
|
104
107
|
text-align: center;
|
|
105
108
|
}
|
|
106
|
-
:where(
|
|
109
|
+
:where(
|
|
110
|
+
[data-nk="table"] [data-slot="table-header"][data-align="right"],
|
|
111
|
+
[data-nk="table"] [data-slot="table-cell"][data-align="right"]
|
|
112
|
+
) {
|
|
107
113
|
font-variant-numeric: tabular-nums;
|
|
108
114
|
text-align: right;
|
|
109
115
|
}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
--nk-color-success-content: oklch(0.393 0.095 152.535);
|
|
91
91
|
--nk-color-warning: oklch(0.681 0.162 75.834);
|
|
92
92
|
--nk-color-warning-content: oklch(0.421 0.095 57.708);
|
|
93
|
-
--nk-color-danger: oklch(0.
|
|
93
|
+
--nk-color-danger: oklch(0.577 0.237 25.331);
|
|
94
94
|
--nk-color-danger-foreground: oklch(1 0 0);
|
|
95
95
|
--nk-color-danger-content: oklch(0.444 0.177 26.899);
|
|
96
96
|
--nk-color-overlay: oklch(0 0 0 / 0.5);
|
|
@@ -185,8 +185,8 @@
|
|
|
185
185
|
transparent 10%
|
|
186
186
|
);
|
|
187
187
|
--nk-color-danger-hover: light-dark(
|
|
188
|
-
oklch(0.
|
|
189
|
-
oklch(0.
|
|
188
|
+
oklch(0.54 0.237 25.331),
|
|
189
|
+
oklch(0.59 0.237 25.331)
|
|
190
190
|
);
|
|
191
191
|
--nk-app-shell-background: var(--nk-color-canvas);
|
|
192
192
|
--nk-app-shell-sidebar-background: var(--nk-color-surface);
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nitro_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.0.alpha.
|
|
4
|
+
version: 2.0.0.alpha.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikkel Malmberg
|
|
@@ -179,7 +179,6 @@ files:
|
|
|
179
179
|
- docs/component_contracts.md
|
|
180
180
|
- docs/customization.md
|
|
181
181
|
- docs/hotwire.md
|
|
182
|
-
- docs/implementation_plan.md
|
|
183
182
|
- docs/initialization_prompt.md
|
|
184
183
|
- docs/migration_1_to_2.md
|
|
185
184
|
- docs/new_app_strategy.md
|
data/docs/implementation_plan.md
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
# Nitro Kit 2.0 implementation record
|
|
2
|
-
|
|
3
|
-
This document records the staged delivery program used for the agent-native pivot. `tk` is the source of truth for live status and dependency order; ticket IDs below connect durable design intent to executable work. Candidate lists in early stages are historical investigation prompts, not current public API. The settled contract lives in [`agent_native_spec.md`](agent_native_spec.md) and [`component_contracts.md`](component_contracts.md).
|
|
4
|
-
|
|
5
|
-
Nitro Kit 2.0 is allowed to break 1.x completely. Each stage should leave one simpler system behind rather than maintaining parallel APIs.
|
|
6
|
-
|
|
7
|
-
The original implementation program reached release-quality consolidation. Component migration, evidence-gathering flows, accepted layout/block extraction, the expanded gallery, 1.x removal, documentation, packaging, and browser verification are retained here as history; later contract revisions such as responsive Flex/Grid remain tracked in `tk` and the canonical contract documents.
|
|
8
|
-
|
|
9
|
-
## Definition of done
|
|
10
|
-
|
|
11
|
-
The 2.0 pivot is complete when:
|
|
12
|
-
|
|
13
|
-
- Every component is constructed directly from Phlex with explicit Ruby options.
|
|
14
|
-
- Nitro Kit owns the Ruby, markup contract, behavior, CSS, examples, and documentation.
|
|
15
|
-
- No Nitro-authored component, layout, block, flow, or gallery subject emits `class`, `style`, or an escape marker.
|
|
16
|
-
- Applications can theme the system through documented `--nk-*` variables and compose or subclass components.
|
|
17
|
-
- Rails forms, routes, assets, Turbo Frames, and Turbo Streams remain first-class.
|
|
18
|
-
- The gem does not expose `nk_*` view helpers, copied-component generators, a Tailwind runtime, or a template-buffer bridge.
|
|
19
|
-
- The complete catalog renders through explicit Rails routes in light/dark and narrow/wide states.
|
|
20
|
-
- Keyboard behavior and Turbo lifecycle behavior pass browser tests.
|
|
21
|
-
- The full Ruby, CSS, JavaScript formatting, package, and browser checks pass from a clean checkout.
|
|
22
|
-
|
|
23
|
-
## Delivery rules
|
|
24
|
-
|
|
25
|
-
Every migration ticket follows the same sequence:
|
|
26
|
-
|
|
27
|
-
1. Define the smallest explicit Ruby API and closed vocabularies.
|
|
28
|
-
2. Render native, self-describing HTML with `data-nk` and qualified `data-slot` identities.
|
|
29
|
-
3. Preserve Rails and accessibility semantics before visual styling.
|
|
30
|
-
4. Add static zero-specificity CSS driven by public theme tokens and private mechanics variables.
|
|
31
|
-
5. Add focused render, invalid-option, reserved-attribute, and integration tests.
|
|
32
|
-
6. Add representative and exhaustive gallery examples without classes or escape hatches.
|
|
33
|
-
7. Verify the family independently before allowing dependent flows to use it.
|
|
34
|
-
|
|
35
|
-
No compatibility layer should survive merely to keep a legacy example green. Replace the example and delete the obsolete API.
|
|
36
|
-
|
|
37
|
-
## Stage 0 — ownership and execution scaffold
|
|
38
|
-
|
|
39
|
-
Tickets: `nk-rzxf`, `nk-g3qu`
|
|
40
|
-
|
|
41
|
-
- Work on the dedicated `2.0-agent-native` branch.
|
|
42
|
-
- Keep the agent-native specification, component contracts, repository guidance, and this plan tracked with the gem.
|
|
43
|
-
- Track the entire program in the workspace-level `.tickets` database.
|
|
44
|
-
- Record implementation decisions and verification counts on each ticket before closing it.
|
|
45
|
-
|
|
46
|
-
Gate: an agent can explain who owns Ruby, markup, CSS, behavior, theming, composition, and the class escape hatch by reading the tracked docs.
|
|
47
|
-
|
|
48
|
-
## Stage 1 — kernel and distribution foundation
|
|
49
|
-
|
|
50
|
-
Tickets: `nk-6q5i`, `nk-fooc`, `nk-rx0j`
|
|
51
|
-
|
|
52
|
-
- Replace permissive attributes with explicit component options plus `html:`, `aria:`, and `data:` boundaries.
|
|
53
|
-
- Centralize identity, slot attachment, reserved attributes, validation, additive Stimulus data, and `desperately_need_a_class:`.
|
|
54
|
-
- Build deterministic layered CSS from split plain-CSS sources.
|
|
55
|
-
- Ship the built stylesheet, optional Tailwind adapter, importmap pins, and controllers from the gem without vendored third-party JavaScript.
|
|
56
|
-
- Prove the engine still boots when importmap-rails is absent.
|
|
57
|
-
|
|
58
|
-
Gate: a minimal Rails app can render and style a Nitro component without copying sources or configuring Tailwind or JavaScript pins.
|
|
59
|
-
|
|
60
|
-
## Stage 2 — representative vertical slice
|
|
61
|
-
|
|
62
|
-
Ticket: `nk-g3x1`
|
|
63
|
-
|
|
64
|
-
Migrate Button/Icon, Card, Input/Field/FormBuilder, Table, and Dialog all the way through Ruby, HTML, CSS, behavior, Rails integration, tests, and gallery routes.
|
|
65
|
-
|
|
66
|
-
Use this slice to settle:
|
|
67
|
-
|
|
68
|
-
- Direct compound-component methods and nested slot ownership.
|
|
69
|
-
- Native attribute boundaries and error messages.
|
|
70
|
-
- Intrinsic versus stretch sizing.
|
|
71
|
-
- Rails form names, IDs, values, errors, multipart behavior, and checkbox semantics.
|
|
72
|
-
- Native dialog behavior, stable ARIA relationships, and Stimulus cleanup.
|
|
73
|
-
- The explicit gallery catalog and route contract.
|
|
74
|
-
|
|
75
|
-
Gate: the slice is classless, invalid options fail immediately, focused tests pass, and its examples render through real Rails routes with Nitro CSS alone.
|
|
76
|
-
|
|
77
|
-
## Stage 3 — complete atom migration
|
|
78
|
-
|
|
79
|
-
Tickets: `nk-t17c`, `nk-b18t`, `nk-7lkw`, `nk-4r36`, `nk-i4xu`, `nk-19gd`
|
|
80
|
-
|
|
81
|
-
### Display
|
|
82
|
-
|
|
83
|
-
Migrate Alert, Avatar, AvatarStack, Badge, and Icon. Cover semantic intents, long content, image/fallback behavior, overflow, sizes, labeled/decorative icons, and nested combinations.
|
|
84
|
-
|
|
85
|
-
### Actions and navigation
|
|
86
|
-
|
|
87
|
-
Migrate ButtonGroup and Pagination. Preserve native links/buttons, current-page semantics, disabled previous/next controls, ellipses, compact ranges, and stable labels.
|
|
88
|
-
|
|
89
|
-
### Forms
|
|
90
|
-
|
|
91
|
-
Migrate Label, Textarea, Select, Checkbox, CheckboxGroup, RadioButton, RadioButtonGroup, Switch, FieldGroup, Fieldset, and all FormBuilder methods. Cover:
|
|
92
|
-
|
|
93
|
-
- Bound and unbound forms.
|
|
94
|
-
- Required, disabled, readonly, checked, indeterminate, invalid, prompt, and empty states.
|
|
95
|
-
- Array and nested parameter names.
|
|
96
|
-
- Active Model values and errors.
|
|
97
|
-
- Multipart uploads and native file-input constraints.
|
|
98
|
-
- Accessible descriptions, legends, labels, and error relationships.
|
|
99
|
-
|
|
100
|
-
### Structured content
|
|
101
|
-
|
|
102
|
-
Migrate Accordion and Tabs alongside the Card and Table slice. Require deterministic IDs and keys, direct compound APIs, semantic table structure, and keyboard-visible state.
|
|
103
|
-
|
|
104
|
-
### Interaction
|
|
105
|
-
|
|
106
|
-
Migrate Dropdown, Tooltip, Combobox, and Toast alongside Dialog. Require closed placement/state APIs, visible ARIA/data state, keyboard behavior, native controls where possible, and complete disconnect cleanup.
|
|
107
|
-
|
|
108
|
-
Gate: every shipped atom has a classless direct-Phlex API, static CSS, focused tests, exhaustive examples, and no dependency on a legacy helper.
|
|
109
|
-
|
|
110
|
-
## Stage 4 — Rails and Hotwire proof
|
|
111
|
-
|
|
112
|
-
Ticket: `nk-19gd`
|
|
113
|
-
|
|
114
|
-
- Exercise `form_with` directly from Phlex using `NitroKit::FormBuilder`.
|
|
115
|
-
- Prove route helpers, DOM ID helpers, CSRF behavior, model errors, submit paths, and multipart forms.
|
|
116
|
-
- Render Turbo Frames and Turbo Streams from direct-Phlex pages.
|
|
117
|
-
- Add one small validation submit path that demonstrates the real request/response loop.
|
|
118
|
-
- Document the supported Rails boundary and show examples without introducing Nitro view helpers.
|
|
119
|
-
|
|
120
|
-
Gate: Rails supplies framework semantics while Phlex remains the only general UI composition language.
|
|
121
|
-
|
|
122
|
-
## Stage 5 — gallery infrastructure and exhaustive combinations
|
|
123
|
-
|
|
124
|
-
Tickets: `nk-q4mj`, `nk-put2`, `nk-jp63`, `nk-5028`, `nk-t23x`, `nk-rtwj`, `nk-8mh8`, `nk-t66i`, `nk-vnwu`
|
|
125
|
-
|
|
126
|
-
- Keep one explicit catalog of component, block, and flow entries.
|
|
127
|
-
- Render the shell, pages, sections, examples, samples, and notes in Phlex.
|
|
128
|
-
- Pair every preview with highlighted, copyable Ruby extracted from the exact rendering block or concrete flow method.
|
|
129
|
-
- Generate route contract tests from the catalog.
|
|
130
|
-
- Assert every subject has stable Nitro identities and no `class`, `style`, or escape markers.
|
|
131
|
-
- Add meaningful Cartesian coverage without mechanically showing nonsense combinations.
|
|
132
|
-
|
|
133
|
-
Required pressure cases:
|
|
134
|
-
|
|
135
|
-
- All closed variants and sizes.
|
|
136
|
-
- Default, hover/focus-capable, disabled, readonly, invalid, empty, loading-like, open, and selected states where meaningful.
|
|
137
|
-
- Short, long, wrapped, numeric, missing-image, and high-item-count content.
|
|
138
|
-
- Nested atoms and compound slots.
|
|
139
|
-
- Light and dark themes.
|
|
140
|
-
- Narrow and wide viewports.
|
|
141
|
-
- Forms, tables, overlays, and navigation used together rather than only in isolation.
|
|
142
|
-
|
|
143
|
-
Gate: every catalog entry succeeds through an explicit route and the gallery itself requires no Tailwind or ERB templates.
|
|
144
|
-
|
|
145
|
-
## Stage 6 — atom-only product flows
|
|
146
|
-
|
|
147
|
-
Tickets: `nk-31ne`, `nk-7d05`, `nk-q37g`, `nk-tcig`, `nk-32vc`
|
|
148
|
-
|
|
149
|
-
Build realistic screens before inventing layout abstractions:
|
|
150
|
-
|
|
151
|
-
- Authentication, registration, password reset, and verification.
|
|
152
|
-
- Onboarding progress, validation, resume, and completion.
|
|
153
|
-
- Dashboard overview, statistics, recent records, and activity.
|
|
154
|
-
- Profile, preferences, account, and destructive settings.
|
|
155
|
-
- Billing plans, payment methods, invoices, and upgrade states.
|
|
156
|
-
- Users search, filters, pagination, empty state, and detail.
|
|
157
|
-
- Team members, invitations, roles, and removal confirmation.
|
|
158
|
-
- API credential empty, create, reveal-once, list, and revoke states.
|
|
159
|
-
|
|
160
|
-
Use deterministic PORO data and explicit state slugs. Record repeated layout and responsibility friction in `notes/block_candidates.md`; do not abstract a one-off screen.
|
|
161
|
-
|
|
162
|
-
Gate: all required product states exist using atoms and plain Phlex composition, and repeated structures are backed by concrete evidence.
|
|
163
|
-
|
|
164
|
-
## Stage 7 — evidence-driven layouts and blocks
|
|
165
|
-
|
|
166
|
-
Tickets: `nk-okls`, `nk-bjv5`, `nk-b8eg`
|
|
167
|
-
|
|
168
|
-
Extract the smallest vocabulary that removes proven repetition. The evidence pass accepted:
|
|
169
|
-
|
|
170
|
-
- Layouts: VStack, HStack, the three-column Grid, and Container.
|
|
171
|
-
- Shell: AuthShell.
|
|
172
|
-
- Sections and blocks: SettingsLayout, Toolbar, PaginationBar, PageHeader, StatGrid, DataSection, FormSection, DangerZone, and EmptyState.
|
|
173
|
-
|
|
174
|
-
This list records the first extraction decision. It has since been superseded: `VStack` and `HStack` were removed in favor of unified responsive `Flex`, and the fixed three-column Grid became a responsive 1–12-column `Grid`. See the canonical specification and component contracts for the current API.
|
|
175
|
-
|
|
176
|
-
At that stage, the evidence pass deferred Spacer, Split, Frame, AppShell, MarketingShell, AuthenticationPanel, and ProgressSteps because their responsibilities were not stable across enough domains. A later application-layout mandate supplied enough evidence to accept AppShell; the other deferred candidates remain outside 2.0. The settled contracts, rather than this historical stage record, live in [`agent_native_spec.md`](agent_native_spec.md) and [`component_contracts.md`](component_contracts.md).
|
|
177
|
-
|
|
178
|
-
Each abstraction must:
|
|
179
|
-
|
|
180
|
-
- Have one clear responsibility.
|
|
181
|
-
- Use closed layout options.
|
|
182
|
-
- Own only structure that actually repeats.
|
|
183
|
-
- Accept atoms/content through direct Ruby composition.
|
|
184
|
-
- Emit classless self-describing markup.
|
|
185
|
-
- Include examples showing reuse in at least two flows.
|
|
186
|
-
|
|
187
|
-
Then rewrite the atom-only flows with blocks and compare the result. Delete abstractions that do not make application code meaningfully clearer.
|
|
188
|
-
|
|
189
|
-
Gate: flow code describes product intent, while layouts and blocks remain a small coherent vocabulary rather than a second page framework.
|
|
190
|
-
|
|
191
|
-
## Stage 8 — expanded application gallery
|
|
192
|
-
|
|
193
|
-
Tickets: `nk-pmcn`, `nk-q1tc`, `nk-oi4y`, `nk-gn4s`, `nk-ky42`
|
|
194
|
-
|
|
195
|
-
Extend the block-based gallery across:
|
|
196
|
-
|
|
197
|
-
- Data detail, activity, audit, uploads, and integrations.
|
|
198
|
-
- Billing, authentication, onboarding, team, and API states.
|
|
199
|
-
- System error, permission, empty, loading-like, and marketing states.
|
|
200
|
-
- Changelog, support, and help experiences.
|
|
201
|
-
|
|
202
|
-
Prefer depth of state over disconnected decorative pages. Each flow should show success, empty/error, destructive/confirmation, and constrained-width behavior where applicable.
|
|
203
|
-
|
|
204
|
-
Gate: the gallery demonstrates that the vocabulary can build a credible Rails product, not only isolated controls.
|
|
205
|
-
|
|
206
|
-
## Stage 9 — remove the old system
|
|
207
|
-
|
|
208
|
-
Ticket: `nk-u534`
|
|
209
|
-
|
|
210
|
-
- Delete all `nk_*` view helpers and automatic variant aliases.
|
|
211
|
-
- Delete copied-component generators, schemas, manifests, and installation paths.
|
|
212
|
-
- Delete the Action View/template capture bridge and legacy builder path.
|
|
213
|
-
- Remove Tailwind Merge and consumer Tailwind dependencies.
|
|
214
|
-
- Remove legacy ERB test pages, Tailwind assets, and obsolete tests.
|
|
215
|
-
- Audit packaged gem files and runtime dependencies.
|
|
216
|
-
|
|
217
|
-
Gate: repository-wide searches find no public legacy invocation, copied-source promise, internal component class string, or required Tailwind runtime.
|
|
218
|
-
|
|
219
|
-
## Stage 10 — browser behavior and lifecycle verification
|
|
220
|
-
|
|
221
|
-
Tickets: `nk-352j`, `nk-6ivz`, `nk-gxwn`, `nk-lij5`, `nk-tkds`
|
|
222
|
-
|
|
223
|
-
- Enumerate every catalog URL and state in a real browser.
|
|
224
|
-
- Exercise keyboard order, arrows, Escape, Enter/Space, tab trapping/return, and focus visibility.
|
|
225
|
-
- Exercise open/close, select, filter, validation, submit, and destructive confirmation paths.
|
|
226
|
-
- Navigate through Turbo Drive, Frames, Streams, and morph refreshes; look for duplicate controllers and leaked listeners/positioners/timers.
|
|
227
|
-
- Review representative pages at narrow/wide sizes and light/dark themes.
|
|
228
|
-
- Check for horizontal overflow, clipped overlays, unstable layout, unreadable content, and broken focus state.
|
|
229
|
-
|
|
230
|
-
Gate: browser coverage proves both route completeness and interaction lifecycle behavior.
|
|
231
|
-
|
|
232
|
-
## Stage 11 — release-quality consolidation
|
|
233
|
-
|
|
234
|
-
Tickets: `nk-g3qu`, `nk-obb7`
|
|
235
|
-
|
|
236
|
-
- Reconcile actual component APIs with `docs/component_contracts.md` and `STYLE_GUIDE.md`.
|
|
237
|
-
- Document Rails integration, theming tokens, composition, subclass caveats, and the escape hatch.
|
|
238
|
-
- Run a data-structure review for duplicated collections, scattered state, and invalid intermediate states.
|
|
239
|
-
- Run a simplification pass and remove compatibility branches, duplicate validation, dead CSS, dead JavaScript, and unused dependencies.
|
|
240
|
-
- Audit all CSS selectors for zero specificity and owner scoping.
|
|
241
|
-
- Audit all controllers for disconnect cleanup and Turbo safety.
|
|
242
|
-
- Run the complete test, lint, format, CSS build/check, package, route, and browser suites.
|
|
243
|
-
|
|
244
|
-
Gate: every child of `nk-rzxf` is closed with evidence and the repository contains one coherent 2.0 architecture.
|
|
245
|
-
|
|
246
|
-
## Stage 12 — deterministic agent installation
|
|
247
|
-
|
|
248
|
-
Ticket: `2an-5ayl`
|
|
249
|
-
|
|
250
|
-
- Ship concise Nitro Kit Rails, Hotwire, and UI skills that resolve the
|
|
251
|
-
installed gem before acting.
|
|
252
|
-
- Add a setup-only generator that installs the skills for Codex and Claude and
|
|
253
|
-
maintains a bounded `AGENTS.md` section without copying component source.
|
|
254
|
-
- Add diagnostics for the resolved version, agent guidance, Hotwire wiring,
|
|
255
|
-
Phlex Kit inclusion, stylesheet loading, and Nitro Kit 1.x shadows.
|
|
256
|
-
- Print or copy an initialization prompt without launching an agent.
|
|
257
|
-
- Make guided and Pro eval profiles run the real installer and name Nitro Kit
|
|
258
|
-
2.x explicitly; retain cold profiles for diagnostic comparisons.
|
|
259
|
-
|
|
260
|
-
Gate: installation is idempotent, preserves application-owned instructions,
|
|
261
|
-
routes agents to version-matched doctrine, and is exercised by the eval
|
|
262
|
-
harness.
|
|
263
|
-
|
|
264
|
-
## Verification matrix
|
|
265
|
-
|
|
266
|
-
| Surface | Required verification |
|
|
267
|
-
| ------------- | ------------------------------------------------------------------------------------------------- |
|
|
268
|
-
| Ruby API | Focused construction/render tests; invalid vocabulary; missing content; reserved attributes |
|
|
269
|
-
| Markup | Native semantics; stable IDs; ARIA relationships; `data-nk`; qualified `data-slot`; visible state |
|
|
270
|
-
| CSS | Deterministic build/check; zero-specificity owner-scoped selectors; token audit; light/dark |
|
|
271
|
-
| Rails | Real `form_with`; Active Model; routes/DOM IDs; multipart; validation submit; Frame/Stream |
|
|
272
|
-
| JavaScript | Keyboard behavior; open/close/select; disconnect cleanup; Turbo navigation and morph |
|
|
273
|
-
| Gallery | Catalog routes; exact escaped source; copy behavior; no class/style/escape; narrow/wide |
|
|
274
|
-
| Packaging | Engine boot with optional integrations absent; gem contents and dependencies audited |
|
|
275
|
-
| Documentation | Specification, contracts, style guide, Rails boundary, theming, examples, repository commands |
|
|
276
|
-
|
|
277
|
-
## Execution and handoff
|
|
278
|
-
|
|
279
|
-
Use `tk ready` to select dependency-safe work. Preparatory parallel work is permitted only when the orchestrator explicitly assigns non-overlapping ownership. Every handoff names changed files, behavior decisions, focused test counts, and known failures. The orchestrator independently reviews and reproduces meaningful checks before closing a feature ticket.
|