nitro_kit 2.0.0.alpha.2 → 2.0.0.alpha.3
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 +50 -1
- data/README.md +23 -4
- data/STYLE_GUIDE.md +37 -5
- data/app/assets/stylesheets/nitro_kit.css +1714 -1358
- data/app/components/nitro_kit/accordion.rb +1 -7
- data/app/components/nitro_kit/app_navigation.rb +1 -2
- data/app/components/nitro_kit/app_shell.rb +1 -7
- data/app/components/nitro_kit/appearance_picker.rb +1 -11
- data/app/components/nitro_kit/avatar.rb +6 -1
- data/app/components/nitro_kit/avatar_stack.rb +3 -0
- 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 +3 -16
- data/app/components/nitro_kit/command_palette.rb +41 -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 +3 -14
- 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 +0 -1
- 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 +3 -3
- 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 +1 -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 +21 -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 +8 -1
- data/app/javascript/controllers/nk/tooltip_controller.js +9 -0
- data/config/locales/en.yml +4 -1
- data/docs/agent_guide.md +31 -16
- data/docs/agent_native_spec.md +27 -10
- data/docs/browser_support.md +193 -0
- data/docs/component_contracts.md +88 -82
- data/docs/customization.md +27 -15
- data/docs/hotwire.md +15 -3
- data/docs/initialization_prompt.md +31 -6
- data/docs/migration_1_to_2.md +61 -19
- data/docs/patterns/application_foundation.md +6 -4
- data/docs/patterns/crud_resource.md +6 -3
- data/docs/patterns/destructive_action.md +25 -0
- data/docs/patterns/resource_form.md +1 -1
- data/docs/rails_conventions.md +4 -1
- data/docs/rails_integration.md +39 -11
- data/lib/generators/nitro_kit/upgrade_smoke_tests_generator.rb +12 -0
- data/lib/nitro_kit/installation.rb +9 -4
- data/lib/nitro_kit/migration_inventory.rb +268 -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 +6 -1
- data/plugins/nitro-kit/skills/nitro-kit-ui/SKILL.md +18 -8
- data/src/stylesheets/nitro_kit/components/accordion.css +40 -40
- data/src/stylesheets/nitro_kit/components/alert.css +9 -9
- data/src/stylesheets/nitro_kit/components/app_navigation.css +19 -19
- data/src/stylesheets/nitro_kit/components/app_shell.css +196 -193
- data/src/stylesheets/nitro_kit/components/appearance_picker.css +41 -31
- data/src/stylesheets/nitro_kit/components/avatar.css +3 -3
- data/src/stylesheets/nitro_kit/components/avatar_stack.css +36 -19
- data/src/stylesheets/nitro_kit/components/button.css +83 -58
- data/src/stylesheets/nitro_kit/components/button_group.css +19 -14
- data/src/stylesheets/nitro_kit/components/card.css +11 -5
- data/src/stylesheets/nitro_kit/components/checkbox.css +40 -36
- data/src/stylesheets/nitro_kit/components/checkbox_group.css +13 -13
- data/src/stylesheets/nitro_kit/components/combobox.css +33 -23
- data/src/stylesheets/nitro_kit/components/command_palette.css +48 -47
- data/src/stylesheets/nitro_kit/components/control_group.css +52 -58
- data/src/stylesheets/nitro_kit/components/danger_zone.css +3 -3
- data/src/stylesheets/nitro_kit/components/data_section.css +1 -1
- data/src/stylesheets/nitro_kit/components/details_table.css +13 -13
- data/src/stylesheets/nitro_kit/components/dialog.css +6 -3
- data/src/stylesheets/nitro_kit/components/dropdown.css +29 -23
- data/src/stylesheets/nitro_kit/components/dropzone.css +71 -28
- data/src/stylesheets/nitro_kit/components/empty_state.css +6 -6
- data/src/stylesheets/nitro_kit/components/field.css +30 -26
- data/src/stylesheets/nitro_kit/components/field_group.css +13 -0
- data/src/stylesheets/nitro_kit/components/fieldset.css +6 -0
- data/src/stylesheets/nitro_kit/components/input.css +9 -2
- data/src/stylesheets/nitro_kit/components/layout.css +165 -165
- data/src/stylesheets/nitro_kit/components/page_header.css +3 -3
- data/src/stylesheets/nitro_kit/components/pagination.css +55 -46
- data/src/stylesheets/nitro_kit/components/pagination_bar.css +7 -7
- data/src/stylesheets/nitro_kit/components/palette.css +20 -20
- data/src/stylesheets/nitro_kit/components/progressive_image.css +44 -54
- data/src/stylesheets/nitro_kit/components/radio_button.css +36 -29
- data/src/stylesheets/nitro_kit/components/radio_button_group.css +43 -43
- data/src/stylesheets/nitro_kit/components/rich_text_area.css +15 -0
- data/src/stylesheets/nitro_kit/components/select.css +2 -9
- data/src/stylesheets/nitro_kit/components/settings_layout.css +21 -18
- data/src/stylesheets/nitro_kit/components/settings_section.css +84 -0
- data/src/stylesheets/nitro_kit/components/sheet.css +23 -3
- data/src/stylesheets/nitro_kit/components/stat_grid.css +10 -9
- data/src/stylesheets/nitro_kit/components/switch.css +37 -33
- data/src/stylesheets/nitro_kit/components/table.css +35 -31
- data/src/stylesheets/nitro_kit/components/tabs.css +43 -39
- data/src/stylesheets/nitro_kit/components/toast.css +20 -9
- data/src/stylesheets/nitro_kit/components/toolbar.css +14 -39
- data/src/stylesheets/nitro_kit/components/tooltip.css +34 -29
- data/src/stylesheets/nitro_kit/components/typeset.css +147 -1
- data/src/stylesheets/nitro_kit/reset.css +9 -9
- data/src/stylesheets/nitro_kit/tokens.css +8 -9
- metadata +4 -3
- data/src/stylesheets/nitro_kit/components/form_section.css +0 -71
|
@@ -7,8 +7,13 @@ import {
|
|
|
7
7
|
export default class extends Controller {
|
|
8
8
|
static targets = ["trigger", "content", "item"];
|
|
9
9
|
|
|
10
|
+
connect() {
|
|
11
|
+
this.outsidePointerDown = this.outsidePointerDown.bind(this);
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
disconnect() {
|
|
11
15
|
this.stopPositioning?.();
|
|
16
|
+
this.stopOutsidePointerFallback();
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
openFromKeyboard(event) {
|
|
@@ -36,10 +41,12 @@ export default class extends Controller {
|
|
|
36
41
|
focusOpened(event) {
|
|
37
42
|
if (event.newState === "open") {
|
|
38
43
|
this.startPositioning();
|
|
44
|
+
this.startOutsidePointerFallback();
|
|
39
45
|
this.focusInitialItem();
|
|
40
46
|
} else {
|
|
41
47
|
this.stopPositioning?.();
|
|
42
48
|
this.stopPositioning = null;
|
|
49
|
+
this.stopOutsidePointerFallback();
|
|
43
50
|
this.focusLast = false;
|
|
44
51
|
this.restoreFocus();
|
|
45
52
|
}
|
|
@@ -65,6 +72,37 @@ export default class extends Controller {
|
|
|
65
72
|
this.stopPositioning = observeOverlayPosition(update);
|
|
66
73
|
}
|
|
67
74
|
|
|
75
|
+
startOutsidePointerFallback() {
|
|
76
|
+
if (!this.supportsPopover || this.stopOutsidePointerFallbackListener)
|
|
77
|
+
return;
|
|
78
|
+
|
|
79
|
+
document.addEventListener("pointerdown", this.outsidePointerDown, true);
|
|
80
|
+
this.stopOutsidePointerFallbackListener = () => {
|
|
81
|
+
document.removeEventListener(
|
|
82
|
+
"pointerdown",
|
|
83
|
+
this.outsidePointerDown,
|
|
84
|
+
true,
|
|
85
|
+
);
|
|
86
|
+
this.stopOutsidePointerFallbackListener = null;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
stopOutsidePointerFallback() {
|
|
91
|
+
this.stopOutsidePointerFallbackListener?.();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
outsidePointerDown(event) {
|
|
95
|
+
if (!this.contentTarget.matches(":popover-open")) return;
|
|
96
|
+
|
|
97
|
+
const path = event.composedPath?.();
|
|
98
|
+
const insideMenu = path
|
|
99
|
+
? path.includes(this.contentTarget) || path.includes(this.triggerTarget)
|
|
100
|
+
: this.contentTarget.contains(event.target) ||
|
|
101
|
+
this.triggerTarget.contains(event.target);
|
|
102
|
+
|
|
103
|
+
if (!insideMenu) this.hide();
|
|
104
|
+
}
|
|
105
|
+
|
|
68
106
|
navigate(event) {
|
|
69
107
|
switch (event.key) {
|
|
70
108
|
case "Escape":
|
|
@@ -123,4 +161,11 @@ export default class extends Controller {
|
|
|
123
161
|
(item) => !item.disabled && item.getAttribute("aria-disabled") !== "true",
|
|
124
162
|
);
|
|
125
163
|
}
|
|
164
|
+
|
|
165
|
+
get supportsPopover() {
|
|
166
|
+
return (
|
|
167
|
+
typeof this.contentTarget.showPopover === "function" &&
|
|
168
|
+
typeof this.contentTarget.hidePopover === "function"
|
|
169
|
+
);
|
|
170
|
+
}
|
|
126
171
|
}
|
|
@@ -24,6 +24,10 @@ export default class extends Controller {
|
|
|
24
24
|
if (this.hasFallbackTarget) this.fallbackTarget.hidden = true;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
prepareForCache() {
|
|
28
|
+
delete this.element.dataset.enhanced;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
imageTargetConnected(image) {
|
|
28
32
|
if (this.connected) this.bindImage(image);
|
|
29
33
|
}
|
|
@@ -47,11 +51,14 @@ export default class extends Controller {
|
|
|
47
51
|
this.releaseImage();
|
|
48
52
|
this.boundImage = image;
|
|
49
53
|
this.revision = (this.revision || 0) + 1;
|
|
50
|
-
this.setState("loading");
|
|
51
54
|
image.addEventListener("load", this.onLoad);
|
|
52
55
|
image.addEventListener("error", this.onError);
|
|
53
56
|
|
|
54
|
-
if (image.complete)
|
|
57
|
+
if (image.complete) {
|
|
58
|
+
this.reflectComplete(image);
|
|
59
|
+
} else {
|
|
60
|
+
this.setState("loading");
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
releaseImage() {
|
|
@@ -98,11 +105,8 @@ export default class extends Controller {
|
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
reflectComplete(image) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
} else {
|
|
104
|
-
this.failed();
|
|
105
|
-
}
|
|
108
|
+
this.revision = (this.revision || 0) + 1;
|
|
109
|
+
this.setState(image.naturalWidth > 0 ? "loaded" : "error");
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
setState(state) {
|
|
@@ -43,9 +43,12 @@ export default class extends Controller {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
navigate(event) {
|
|
46
|
+
const rightToLeft = getComputedStyle(this.element).direction === "rtl";
|
|
46
47
|
const directionKeys =
|
|
47
48
|
this.orientationValue === "vertical"
|
|
48
49
|
? ["ArrowUp", "ArrowDown"]
|
|
50
|
+
: rightToLeft
|
|
51
|
+
? ["ArrowRight", "ArrowLeft"]
|
|
49
52
|
: ["ArrowLeft", "ArrowRight"];
|
|
50
53
|
|
|
51
54
|
if (![...directionKeys, "Home", "End"].includes(event.key)) return;
|
|
@@ -142,7 +145,11 @@ export default class extends Controller {
|
|
|
142
145
|
const active = panel.dataset.key === value;
|
|
143
146
|
|
|
144
147
|
panel.hidden = !active;
|
|
145
|
-
|
|
148
|
+
if (active) {
|
|
149
|
+
panel.setAttribute("aria-hidden", "false");
|
|
150
|
+
} else {
|
|
151
|
+
panel.removeAttribute("aria-hidden");
|
|
152
|
+
}
|
|
146
153
|
panel.dataset.state = active ? "active" : "inactive";
|
|
147
154
|
panel.tabIndex = active ? 0 : -1;
|
|
148
155
|
});
|
|
@@ -6,6 +6,8 @@ export default class extends Controller {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
dismiss(event) {
|
|
9
|
+
if (!this.shown) return;
|
|
10
|
+
|
|
9
11
|
event.preventDefault();
|
|
10
12
|
this.element.dataset.dismissed = "";
|
|
11
13
|
}
|
|
@@ -21,4 +23,11 @@ export default class extends Controller {
|
|
|
21
23
|
reset() {
|
|
22
24
|
delete this.element.dataset.dismissed;
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
get shown() {
|
|
28
|
+
return (
|
|
29
|
+
!("dismissed" in this.element.dataset) &&
|
|
30
|
+
this.element.matches(":hover, :focus-within")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
24
33
|
}
|
data/config/locales/en.yml
CHANGED
|
@@ -12,7 +12,9 @@ en:
|
|
|
12
12
|
dark: "Dark"
|
|
13
13
|
system: "System"
|
|
14
14
|
avatar_stack:
|
|
15
|
-
overflow:
|
|
15
|
+
overflow:
|
|
16
|
+
one: "%{count} more avatar"
|
|
17
|
+
other: "%{count} more avatars"
|
|
16
18
|
combobox:
|
|
17
19
|
invalid_selection: "Choose an option."
|
|
18
20
|
no_results: "No options found."
|
|
@@ -37,6 +39,7 @@ en:
|
|
|
37
39
|
dropzone:
|
|
38
40
|
label: "Upload files"
|
|
39
41
|
prompt: "Drop files here or choose them from your device."
|
|
42
|
+
compact_prompt: "Drop or choose files."
|
|
40
43
|
preview_list: "Selected files"
|
|
41
44
|
progress: "Upload progress"
|
|
42
45
|
progress_for: "Upload progress for %{name}"
|
data/docs/agent_guide.md
CHANGED
|
@@ -20,8 +20,9 @@ Read this guide from that directory, then consult only the reference needed for
|
|
|
20
20
|
| Choose or compose UI | `docs/component_contracts.md` |
|
|
21
21
|
| Rails assets, forms, and application shells | `docs/rails_integration.md` |
|
|
22
22
|
| Turbo, Frames, Streams, morphs, or Stimulus | `docs/hotwire.md` |
|
|
23
|
+
| Browser support or progressive fallback | `docs/browser_support.md` |
|
|
23
24
|
| Theme tokens and application composition | `docs/customization.md` |
|
|
24
|
-
| Upgrade an existing Nitro Kit 1.x app
|
|
25
|
+
| Upgrade an existing Nitro Kit 1.x app | `docs/migration_1_to_2.md` |
|
|
25
26
|
| Query, sort, filter, or paginate | `docs/patterns/queryable_collection.md` |
|
|
26
27
|
| Create, update, and show validation | `docs/patterns/resource_form.md` |
|
|
27
28
|
| Delete, revoke, archive, or confirm | `docs/patterns/destructive_action.md` |
|
|
@@ -34,21 +35,21 @@ The installed component source is the final authority for constructor and compou
|
|
|
34
35
|
|
|
35
36
|
Prefer the component that owns the whole region, then compose smaller components inside it.
|
|
36
37
|
|
|
37
|
-
| Product need | Begin with
|
|
38
|
-
| -------------------------------- |
|
|
39
|
-
| Application chrome | `AppShell`, `AppNavigation`, `CommandPalette`
|
|
40
|
-
| Authentication page | `AuthShell`
|
|
41
|
-
| Settings navigation | `SettingsLayout`
|
|
42
|
-
| Team or account administration | `AppShell`, `Toolbar`, `Table`, `
|
|
43
|
-
| App page title and basic actions | `AppShell`, then `Toolbar`
|
|
44
|
-
| Content-led page introduction | `PageHeader`
|
|
45
|
-
| Data region | `DataSection`, then `Table` or `EmptyState`
|
|
46
|
-
| Queryable tabular data | `Table`, `Toolbar`, `PaginationBar`
|
|
47
|
-
| Model-backed form | `
|
|
48
|
-
| Destructive settings | `DangerZone`, optionally `Dialog`
|
|
49
|
-
| Transient server feedback | `Toast::FlashMessages`
|
|
50
|
-
| Rendered Markdown or rich text | `Container`, then `Typeset`
|
|
51
|
-
| General grouping | `Card`, `Flex`, `Grid`, `Container`
|
|
38
|
+
| Product need | Begin with |
|
|
39
|
+
| -------------------------------- | ------------------------------------------------------------- |
|
|
40
|
+
| Application chrome | `AppShell`, `AppNavigation`, `CommandPalette` |
|
|
41
|
+
| Authentication page | `AuthShell` |
|
|
42
|
+
| Settings navigation | `SettingsLayout` |
|
|
43
|
+
| Team or account administration | `AppShell`, `Toolbar`, `Table`, `SettingsSection` |
|
|
44
|
+
| App page title and basic actions | `AppShell`, then `Toolbar` |
|
|
45
|
+
| Content-led page introduction | `PageHeader` |
|
|
46
|
+
| Data region | `DataSection`, then `Table` or `EmptyState` |
|
|
47
|
+
| Queryable tabular data | `Table`, `Toolbar`, `PaginationBar` |
|
|
48
|
+
| Model-backed form | `SettingsSection`, Rails `form_with`, `NitroKit::FormBuilder` |
|
|
49
|
+
| Destructive settings | `DangerZone`, optionally `Dialog` |
|
|
50
|
+
| Transient server feedback | `Toast::FlashMessages` |
|
|
51
|
+
| Rendered Markdown or rich text | `Container`, then `Typeset` |
|
|
52
|
+
| General grouping | `Card`, `Flex`, `Grid`, `Container` |
|
|
52
53
|
|
|
53
54
|
Application-specific product UI belongs under the application's namespace and composes Nitro components. Nitro owns component markup, styles, accessibility structure, and narrowly scoped progressive behavior. The application owns product policy, records, routes, authorization, queries, DOM IDs, and server responses.
|
|
54
55
|
|
|
@@ -70,6 +71,8 @@ After establishing that inventory, generate the host integration smoke tests
|
|
|
70
71
|
from the currently bundled gem with `bin/rails generate
|
|
71
72
|
nitro_kit:upgrade_smoke_tests`. The generator preserves existing files and
|
|
72
73
|
skips missing Rails Minitest or system-test infrastructure with setup guidance.
|
|
74
|
+
Keep the generated explanatory header in each test: these are host-integration
|
|
75
|
+
regression checks, not substitutes for application-specific acceptance tests.
|
|
73
76
|
Run supported generated tests before and after the application conversion,
|
|
74
77
|
while retaining application-specific acceptance coverage for every inventoried
|
|
75
78
|
flow. Customize each generated `prepare_nitro_kit_upgrade_smoke_test` method
|
|
@@ -89,6 +92,15 @@ Do not retain copied Nitro Kit 1.x source as the fallback. Remove the legacy
|
|
|
89
92
|
component and preserve the unsupported behavior in clearly application-owned
|
|
90
93
|
code. Report the missing equivalent as a Nitro Kit coverage gap.
|
|
91
94
|
|
|
95
|
+
At the end of a migration, search the whole application for
|
|
96
|
+
`desperately_need_a_class:` and review every result. Aim for zero rather than
|
|
97
|
+
translating old utility classes mechanically. Move application layout and
|
|
98
|
+
visual treatment to wrappers, use semantic component options or native
|
|
99
|
+
attributes, accept incidental Nitro defaults, and keep unmatched product UI
|
|
100
|
+
application-owned. Remove generic class forwarding from shared builders.
|
|
101
|
+
Retain an escape only for a named external integration that requires a class
|
|
102
|
+
hook, and record that reason in the migration review.
|
|
103
|
+
|
|
92
104
|
## Use one interaction grammar
|
|
93
105
|
|
|
94
106
|
Choose the smallest primitive that completes the interaction:
|
|
@@ -119,6 +131,9 @@ Successful non-GET HTML submissions redirect with `303 See Other`. Invalid form
|
|
|
119
131
|
inline or multi-column.
|
|
120
132
|
- Use component options, compound declarations, layouts, and documented `--nk-*` theme properties.
|
|
121
133
|
- Keep an HTML fallback for every Turbo form flow.
|
|
134
|
+
- Treat that HTML response as a Turbo transport fallback, not automatically a
|
|
135
|
+
JavaScript-free interaction; use `docs/browser_support.md` for the canonical
|
|
136
|
+
component classifications.
|
|
122
137
|
- Test semantic output and stable owned attributes.
|
|
123
138
|
|
|
124
139
|
Do not copy component source, add `nk_*` helpers, invent a general ERB bridge, mutate Nitro-owned Stimulus controllers, or pass `class:` and `style:`. The intentionally loud `desperately_need_a_class:` escape exists only for external integrations that require a class hook.
|
data/docs/agent_native_spec.md
CHANGED
|
@@ -66,6 +66,20 @@ Rails remains first-class where Rails owns meaningful semantics:
|
|
|
66
66
|
|
|
67
67
|
Applications include the `Phlex::Rails::Helpers::*` adapters they actually use. Nitro Kit does not recreate Rails helpers under an `nk_*` namespace.
|
|
68
68
|
|
|
69
|
+
### Browser support
|
|
70
|
+
|
|
71
|
+
Nitro Kit uses modern web standards while keeping core content and actions
|
|
72
|
+
usable for the overwhelming majority of people on maintained browsers. The
|
|
73
|
+
practical target is current stable and popular evergreen Chrome, Edge, Firefox,
|
|
74
|
+
macOS Safari, and iOS Safari releases from roughly the two years before each
|
|
75
|
+
Nitro release. Mobile Safari is a first-class functional target. Nitro
|
|
76
|
+
feature-detects newer capabilities and owns the smallest fallback needed to
|
|
77
|
+
keep essential actions operable without holding components to the oldest
|
|
78
|
+
browser's feature set. Visual polish may degrade when the underlying content,
|
|
79
|
+
request path, semantics, and keyboard behavior remain intact. The versioned
|
|
80
|
+
public contract and no-JavaScript policy are in
|
|
81
|
+
[`browser_support.md`](browser_support.md).
|
|
82
|
+
|
|
69
83
|
## Consistency boundary
|
|
70
84
|
|
|
71
85
|
The initial correctness layer is explicit Ruby APIs and immediate component validation, not a generalized page linter.
|
|
@@ -149,15 +163,18 @@ The stable release contains 40 atoms and components:
|
|
|
149
163
|
|
|
150
164
|
The non-visual `AppearanceBootstrap` installs the shared document appearance runtime, and `NitroKit::Choice` is the typed option value shared by the choice controls.
|
|
151
165
|
|
|
152
|
-
There is no Datepicker component and no date controller. `Input`'s
|
|
166
|
+
There is no Datepicker component and no date controller. `Input`'s native date-family types are progressive enhancement; `Field` and `FormBuilder` preserve them, and `type: :date` inherits the Safari date-editor alignment fix. Month and week may render as text controls without picker, normalization, or constraint enforcement, so applications validate their ISO formats and ranges on the server and compose an application-owned `Select` when an exact bounded choice is required. `Combobox::Option` is likewise gone in favor of `Choice`.
|
|
153
167
|
|
|
154
168
|
### Native interaction authority
|
|
155
169
|
|
|
156
|
-
Nitro uses
|
|
170
|
+
Nitro uses standards-based HTML primitives as the preferred path before adding
|
|
171
|
+
JavaScript. Native is not proof of compatibility: the primitive must cover the
|
|
172
|
+
browser-support window or Nitro must supply a focused fallback for essential
|
|
173
|
+
behavior.
|
|
157
174
|
|
|
158
|
-
- Accordion items are native `details`/`summary` disclosures. Single mode uses one shared `name
|
|
159
|
-
- Dialog declarations produce exactly one native panel through the required `panel(title:, description: nil, nonmodal: false)` declaration, and Nitro renders close button, title, description, then application content inside it. `command="show-modal"` and `command="close"` controls target the panel through `commandfor`; `nonmodal: true` is the only server-rendered open mode and cannot be combined with a trigger. `nk--dialog`
|
|
160
|
-
- Dropdown visibility and invoker state belong to `popover="auto"`. `trigger` forwards `icon:`, `icon_end:`, and `label:` to Button, and `item` accepts its own `icon:`. Its small controller supplies menu focus, arrow/Home/End navigation,
|
|
175
|
+
- Accordion items are native `details`/`summary` disclosures. Single mode uses one shared `name` as its exclusive-group authority; where named-details grouping is unavailable, disclosure remains fully usable but the single-mode baseline is reduced to independently open items. Nitro deliberately adds no controller or browser detection for that short compatibility gap.
|
|
176
|
+
- Dialog declarations produce exactly one native panel through the required `panel(title:, description: nil, nonmodal: false)` declaration, and Nitro renders close button, title, description, then application content inside it. `command="show-modal"` and `command="close"` controls target the panel through `commandfor`; `nonmodal: true` is the only server-rendered open mode and cannot be combined with a trigger. `nk--dialog` handles backdrop light dismissal and, for `dismissible: false`, Escape suppression. Because Invoker Commands do not cover the full browser-support window, that controller also checks the reflected invoker relationship and falls back to `showModal()` or `close()` when the native command cannot run. The canonical no-JavaScript classifications remain in [`browser_support.md`](browser_support.md).
|
|
177
|
+
- Dropdown visibility and invoker state belong to `popover="auto"`. `trigger` forwards `icon:`, `icon_end:`, and `label:` to Button, and `item` accepts its own `icon:`. Its small controller supplies menu focus, arrow/Home/End navigation, focus restoration, collision-aware positioning, and a capability-checked outside-pointer dismissal fallback for WebKit Popover engines that miss light dismissal. The fallback requests the native close transition only while the popover is open, ignores composed-path interactions inside the menu or its trigger, and releases its listener on close or disconnect. Without Nitro JavaScript, native Popover still owns visibility; its bounded top-left placement is reduced from trigger-relative collision-aware placement.
|
|
161
178
|
- CommandPalette uses one native dialog, a declarative search-shaped trigger, and native destination links. Its controller adds the optional Command-K/Control-K shortcut, local filtering, result announcements, and Turbo cleanup without replacing link navigation or retaining hidden application policy. With `search_url:`, the same input submits debounced GET requests into the owned Turbo Frame; the endpoint returns `CommandPalette::Results` HTML and remains responsible for authorization.
|
|
162
179
|
- Tooltip visibility belongs to CSS hover and focus selectors, including a hoverable bridge across the visual gap. Button triggers cover ordinary buttons and links; `as: :custom` forwards owned HTML, ARIA, and data to an existing focusable mutation or compound trigger. Its controller only implements Escape dismissal and reset.
|
|
163
180
|
|
|
@@ -211,7 +228,7 @@ Eleven blocks and shells were extracted:
|
|
|
211
228
|
- `PageHeader`
|
|
212
229
|
- `StatGrid`
|
|
213
230
|
- `DataSection`
|
|
214
|
-
- `
|
|
231
|
+
- `SettingsSection`
|
|
215
232
|
- `DangerZone`
|
|
216
233
|
- `EmptyState`
|
|
217
234
|
|
|
@@ -294,7 +311,7 @@ The audited former-Pro catalog maps completely to the new architecture:
|
|
|
294
311
|
|
|
295
312
|
| Former surface | 2.0 disposition |
|
|
296
313
|
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
297
|
-
| Sidebar and Top Navigation layouts | Rebuilt as `AppShell` for whole-application navigation and `Sheet` for contextual narrow panels
|
|
314
|
+
| Sidebar and Top Navigation layouts | Rebuilt as `AppShell` for whole-application navigation and `Sheet` for contextual narrow panels |
|
|
298
315
|
| Details Table | Rebuilt as `DetailsTable` |
|
|
299
316
|
| Dropzone | Rebuilt as native `Dropzone`; Dropzone.js is removed |
|
|
300
317
|
| Progressive Image | Rebuilt as `ProgressiveImage` |
|
|
@@ -338,7 +355,7 @@ nitro-kit.compound
|
|
|
338
355
|
|
|
339
356
|
Selectors use `:where()` for zero authored specificity. Nitro ships its own global, Preflight-style reset in the `nitro-kit.reset` layer and never depends on Tailwind Preflight. It normalizes the whole page, so arbitrary content inside a component gets the same baseline as Nitro's own markup, and because it is layered, unlayered application CSS always wins. `min-width: 0` and list-marker removal stay scoped to Nitro-owned elements so prose lists keep real markers.
|
|
340
357
|
|
|
341
|
-
Public `--nk-*` variables cover semantic colors, the component-specific raised default-button treatment, typography, spacing, radii, border and focus geometry, shadows, motion, control heights, and content widths. Private `--_nk-*` variables coordinate component mechanics and are not theme API.
|
|
358
|
+
Public `--nk-*` variables cover semantic colors, the component-specific raised default-button treatment and optional button-only radius, typography, spacing, shared radii, border and focus geometry, shadows, motion, control heights, and content widths. Private `--_nk-*` variables coordinate component mechanics and are not theme API.
|
|
342
359
|
|
|
343
360
|
The exact browser order is optional third-party base CSS such as Lexxy, optional `nitro_kit-tailwind-v4.css`, generated `nitro_kit.css`, compiled Tailwind CSS when present, then unlayered application styles containing token overrides. `AppearanceBootstrap` precedes every stylesheet. Applications never edit the generated asset. [`customization.md`](customization.md) is the complete public token inventory and usage guide.
|
|
344
361
|
|
|
@@ -360,9 +377,9 @@ Turbo Frames and Turbo Streams remain Rails helpers used directly from Phlex. In
|
|
|
360
377
|
|
|
361
378
|
Server-rendered feedback is the Rails flash. `Toast` renders `section[data-nk="toast"][role="region"]` wrapping `ol[data-slot="toast-list"]`, whose id is the toast id plus `-list`, so the default region is addressable as `nk-toast-list` and a Turbo Stream can append `NitroKit::Toast::Item` to it directly. Items carry `role="status"`, or `role="alert"` for the error variant, so a server-rendered item announces without waiting for a DOM mutation, and every item is `data-turbo-temporary` so a cached page never replays stale feedback while the region and list survive. `Toast::FlashMessages` maps an enumerable Rails flash onto the same items. Nitro does not add a client-side notification store.
|
|
362
379
|
|
|
363
|
-
The engine ships CSS assets and Nitro-owned Stimulus controllers for enhancements that native HTML and CSS do not cover. When importmap is present it adds its pins automatically; the host still owns Stimulus and its normal controller loader. The engine boots without importmap
|
|
380
|
+
The engine ships CSS assets and Nitro-owned Stimulus controllers for enhancements and compatibility bridges that native HTML and CSS do not cover throughout the support window. When importmap is present it adds its pins automatically; the host still owns Stimulus and its normal controller loader. The engine boots without importmap, but only the component-specific no-JavaScript baselines remain. Accordion keeps its full native disclosure baseline without a controller; its single mode is reduced to independently open items only where named-details grouping is unavailable. Dialog opens without its controller only where declarative `command`/`commandfor` is supported. Nitro Kit 2.0 does not define a JavaScript-package entrypoint for automatic bundler registration.
|
|
364
381
|
|
|
365
|
-
Date inputs, Switch, and ordinary checked state deliberately use native inputs rather than custom controllers. The one exception is `indeterminate:`, which HTML cannot express as an attribute: `Checkbox` mounts `nk--checkable` only in that case, and the controller's whole job is to apply the native DOM property and own the matching `data-state="indeterminate"`. No third-party JavaScript runtime is vendored.
|
|
382
|
+
Date-family inputs, Switch, and ordinary checked state deliberately use native inputs rather than custom controllers. Month and week retain their native types as progressive enhancement even where a browser exposes text entry; Nitro does not promise a picker or client-side normalization. The one exception is `indeterminate:`, which HTML cannot express as an attribute: `Checkbox` mounts `nk--checkable` only in that case, and the controller's whole job is to apply the native DOM property and own the matching `data-state="indeterminate"`. No third-party JavaScript runtime is vendored.
|
|
366
383
|
|
|
367
384
|
## Examples and verification
|
|
368
385
|
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Browser support
|
|
2
|
+
|
|
3
|
+
Nitro Kit uses modern web standards while keeping core content and actions
|
|
4
|
+
usable for the overwhelming majority of people on maintained browsers.
|
|
5
|
+
|
|
6
|
+
The practical target is current stable and popular evergreen releases from
|
|
7
|
+
roughly the two years before each Nitro Kit release. This includes Chrome and
|
|
8
|
+
Edge on desktop, Chrome on Android, Firefox, Safari on macOS, and Safari on iOS.
|
|
9
|
+
It is a coverage target, not a promise of identical results in every version or
|
|
10
|
+
a reason to hold components to the oldest browser's feature set.
|
|
11
|
+
|
|
12
|
+
Each release's dated test matrix sets the concrete floor using release date and
|
|
13
|
+
real-world usage rather than an equal number of major versions. Mobile Safari
|
|
14
|
+
is a first-class target, not a reduced mobile tier. Important usage,
|
|
15
|
+
accessibility, or platform constraints may justify coverage outside the usual
|
|
16
|
+
window.
|
|
17
|
+
|
|
18
|
+
## Functional baseline
|
|
19
|
+
|
|
20
|
+
In browsers covered by a release's matrix, Nitro components must keep their
|
|
21
|
+
essential content and actions usable. Forms must submit, links must navigate,
|
|
22
|
+
disclosures and overlays must open and close, destructive actions must retain a
|
|
23
|
+
usable request path, and controls must preserve their accessible names and
|
|
24
|
+
keyboard behavior.
|
|
25
|
+
|
|
26
|
+
Visual and convenience enhancements may degrade when a browser lacks a newer
|
|
27
|
+
CSS or platform feature. Exact animation, preferred overlay placement, advanced
|
|
28
|
+
text wrapping, and similar polish are not required when the simpler result
|
|
29
|
+
remains understandable and operable. Support means usable and accessible, not
|
|
30
|
+
pixel-identical presentation or identical convenience behavior.
|
|
31
|
+
|
|
32
|
+
Nitro prefers approved web standards and native browser behavior, but native is
|
|
33
|
+
not synonymous with supported. A newer standard can be adopted before it covers
|
|
34
|
+
the practical browser target when Nitro can preserve essential behavior:
|
|
35
|
+
|
|
36
|
+
1. Keep the standard markup or API as the preferred path.
|
|
37
|
+
2. Detect support by capability rather than user agent.
|
|
38
|
+
3. Add the smallest Nitro-owned fallback needed to preserve functionality.
|
|
39
|
+
4. Use a focused polyfill only when a local fallback cannot provide the
|
|
40
|
+
required semantics.
|
|
41
|
+
|
|
42
|
+
Typeset is a CSS-only exception to the usual progressive-enhancement note:
|
|
43
|
+
its readable source uses `@scope`, while the stylesheet also contains a
|
|
44
|
+
low-specificity fallback for engines that do not parse `@scope` (including
|
|
45
|
+
Firefox through 145). The fallback provides root typography and explicitly
|
|
46
|
+
anchored direct-child rules for headings, flow elements, lists, code/pre, and
|
|
47
|
+
tables, plus links within those supported semantic elements and their focus
|
|
48
|
+
state. It excludes direct nested
|
|
49
|
+
`[data-nk]` and `data-typeset="off"` boundaries and intentionally does not
|
|
50
|
+
promise the full descendant styling of the scoped path.
|
|
51
|
+
|
|
52
|
+
Applications should not need to copy Nitro controllers or install a general
|
|
53
|
+
polyfill bundle for a Nitro-owned component. Browsers outside a release's matrix
|
|
54
|
+
may still work, but Nitro does not promise fixes for them.
|
|
55
|
+
|
|
56
|
+
### Month and week inputs
|
|
57
|
+
|
|
58
|
+
`Input(type: :month)` and `Input(type: :week)`, including the matching `Field`
|
|
59
|
+
and `FormBuilder` paths, retain the standard native input types as progressive
|
|
60
|
+
enhancement. Desktop Safari and Firefox can expose these controls as text
|
|
61
|
+
inputs, and native week selection arrived later on iOS Safari. In those cases a
|
|
62
|
+
picker, browser normalization, and enforcement of `min`, `max`, and `step` may
|
|
63
|
+
be absent.
|
|
64
|
+
|
|
65
|
+
Applications must accept and validate the submitted ISO shapes on the server:
|
|
66
|
+
`YYYY-MM` for month and `YYYY-Www` for ISO week. They must also validate allowed
|
|
67
|
+
ranges and increments as domain rules; the native constraint attributes are
|
|
68
|
+
client-side hints, not an authorization or validation boundary. When choosing
|
|
69
|
+
only from an exact, bounded set is essential, compose an application-owned
|
|
70
|
+
`Select` with explicit month or week options instead of relying on a generic
|
|
71
|
+
Nitro datepicker. Ordinary `date`, `time`, and `datetime-local` controls keep
|
|
72
|
+
their existing contracts.
|
|
73
|
+
|
|
74
|
+
## JavaScript and progressive enhancement
|
|
75
|
+
|
|
76
|
+
Normal support assumes the documented Nitro JavaScript is installed. Nitro is
|
|
77
|
+
server-rendered and progressively enhanced, not JavaScript-free. Static content,
|
|
78
|
+
links, ordinary forms, and only the component-specific baselines documented in
|
|
79
|
+
the component contracts are guaranteed without JavaScript. Turbo transport,
|
|
80
|
+
Stimulus enhancements, and compatibility bridges are then unavailable.
|
|
81
|
+
|
|
82
|
+
The classifications below describe the rendered page with Nitro JavaScript,
|
|
83
|
+
Stimulus, and Turbo absent:
|
|
84
|
+
|
|
85
|
+
- **Full** means the component's essential content and action remain available;
|
|
86
|
+
documented convenience behavior may still be absent.
|
|
87
|
+
- **Reduced** means useful content or a simpler native interaction remains, but
|
|
88
|
+
part of the component's defining interaction is absent.
|
|
89
|
+
- **Unavailable** means the defining interaction cannot be completed. Critical
|
|
90
|
+
actions need an ordinary server-rendered route outside that component.
|
|
91
|
+
|
|
92
|
+
A server-rendered HTML response is not automatically a JavaScript-free
|
|
93
|
+
interaction. It can still place content in a closed dialog or rely on Turbo to
|
|
94
|
+
submit, replace, or confirm. Likewise, an HTML response branch is the fallback
|
|
95
|
+
for Turbo transport, not proof that the surrounding control works without
|
|
96
|
+
JavaScript.
|
|
97
|
+
|
|
98
|
+
### No-JavaScript component matrix
|
|
99
|
+
|
|
100
|
+
| Component or family | Classification | Concise baseline without JavaScript |
|
|
101
|
+
| ---------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
102
|
+
| Links, Button, ButtonTo, and ordinary forms | Full | Native navigation and submission work. Submission indicators and Turbo transport or confirmation do not. |
|
|
103
|
+
| Input, Select, Textarea, Switch, RadioButton, and Checkbox | Full; reduced for indeterminate Checkbox | Native controls edit and submit. `indeterminate: true` cannot be applied because it is a DOM property; month/week have the separate native-control limitations below. |
|
|
104
|
+
| Accordion | Full; reduced single grouping where named `details` is unsupported | Every `details` opens and closes. Exclusive grouping depends on native shared-`name` support. |
|
|
105
|
+
| AppShell | Full | Navigation stays visible in document flow at narrow widths; the enhanced drawer interaction is absent. |
|
|
106
|
+
| Appearance | Reduced | CSS follows the system preference and server-rendered picker state remains visible, but a picker cannot apply or persist a new choice without the bootstrap runtime and controller. |
|
|
107
|
+
| Avatar | Reduced | The native image and rendered fallback remain, but a failed image is not swapped to its fallback. |
|
|
108
|
+
| Combobox | Full | The labelled native Select remains the named control and submission source; searchable listbox behavior is absent. |
|
|
109
|
+
| Dialog and Sheet | Reduced with Invoker Commands; unavailable without them | Declarative trigger and close controls work only with native Invoker Commands. A server-open nonmodal Dialog remains visible; controller dismissal policy and the compatibility bridge are absent. |
|
|
110
|
+
| CommandPalette | Reduced with Invoker Commands; unavailable without them | Native commands can open the dialog and its destination links navigate, but search, shortcut, announcements, and Turbo results are absent. Without Invoker Commands the closed content is unreachable. |
|
|
111
|
+
| Dropdown | Reduced | Native Popover opens, closes, and light-dismisses. Menu keyboard conventions, focus restoration, collision-aware placement, and Nitro's outside-pointer compatibility fallback are absent; CSS supplies bounded top-left placement. |
|
|
112
|
+
| Dropzone | Full | Its labelled native file input and ordinary form submission work; drag preview, policy feedback, progress, and direct upload are absent. |
|
|
113
|
+
| RichTextArea | Unavailable unless the host editor provides its own fallback | Nitro wraps trusted host-editor markup and does not manufacture a plain-text control. The host editor owns its JavaScript and fallback contract. |
|
|
114
|
+
| ProgressiveImage | Reduced | The native image loads and retains its accessible alternative; decode/load state and the visible error fallback are not enhanced. |
|
|
115
|
+
| Tabs | Reduced | Every panel and its heading control remain visible and reachable; single-panel selection and APG keyboard behavior are absent. |
|
|
116
|
+
| Toast | Reduced | Server-rendered flash content and live-region semantics remain; timed and manual dismissal are absent. |
|
|
117
|
+
| Tooltip | Reduced | CSS hover and focus disclosure remain; Escape dismissal is absent. |
|
|
118
|
+
|
|
119
|
+
Typeset is not interactive, but its no-JavaScript and no-`@scope` CSS path is
|
|
120
|
+
also reduced: the fallback covers the documented semantic subset rather than
|
|
121
|
+
full descendant parity.
|
|
122
|
+
|
|
123
|
+
### Compatibility details
|
|
124
|
+
|
|
125
|
+
#### Accordion
|
|
126
|
+
|
|
127
|
+
Accordion always keeps native `details` and `summary` as the disclosure
|
|
128
|
+
authority. Its no-JavaScript baseline is full for opening and closing each
|
|
129
|
+
item. In `single` mode, a shared `name` is the browser-native exclusive-group
|
|
130
|
+
mechanism: browsers that support named details provide the full one-open-item
|
|
131
|
+
behavior without a Nitro controller.
|
|
132
|
+
|
|
133
|
+
The short Firefox gap before Firefox 130, including Firefox 128 ESR, has a
|
|
134
|
+
reduced `single` baseline: each disclosure still opens and closes normally,
|
|
135
|
+
but more than one item can remain open. Nitro does not detect the browser or
|
|
136
|
+
install JavaScript merely to duplicate that native grouping. Applications for
|
|
137
|
+
which strict one-open-at-a-time behavior is essential in that gap should use a
|
|
138
|
+
different interaction with an application-owned server or JavaScript policy.
|
|
139
|
+
|
|
140
|
+
Browser compatibility is verified against current stable browsers and
|
|
141
|
+
representative older releases near the edge of the support window. Compatibility
|
|
142
|
+
work prioritizes real Safari and iOS Safari coverage in addition to Chromium;
|
|
143
|
+
tests that merely remove an attribute in current Chrome are useful branch tests,
|
|
144
|
+
but are not a substitute for the affected browser engine.
|
|
145
|
+
|
|
146
|
+
Dialog, Sheet, and CommandPalette keep declarative `command`/`commandfor`
|
|
147
|
+
controls in their server markup. With Nitro JavaScript installed, their shared
|
|
148
|
+
controller uses the reflected invoker relationship when available and falls
|
|
149
|
+
back to `HTMLDialogElement.showModal()` or `close()` only when that relationship
|
|
150
|
+
cannot run. Without JavaScript, opening and closing these overlays is available
|
|
151
|
+
only in browsers with Invoker Commands; destination links and ordinary forms
|
|
152
|
+
remain server-rendered, but content inside a closed dialog is not reachable.
|
|
153
|
+
|
|
154
|
+
Each Nitro release records a dated tested-browser matrix. The rolling policy is
|
|
155
|
+
the durable contract; the matrix records the concrete versions used to verify a
|
|
156
|
+
particular release.
|
|
157
|
+
|
|
158
|
+
## Nitro Kit 2.0 release matrix (2026-08-05)
|
|
159
|
+
|
|
160
|
+
This is the release checklist for the next Nitro 2.0 release. “Automated” means
|
|
161
|
+
the Rails/Selenium system suite runs in CI; it does not claim that Selenium
|
|
162
|
+
reproduces every device or browser UI detail. Version numbers are the stable
|
|
163
|
+
targets recorded on this date, and should be refreshed if the release slips.
|
|
164
|
+
|
|
165
|
+
| Target | Near-floor reference | Current release verification | Method |
|
|
166
|
+
| --------------- | -------------------- | ---------------------------- | ----------------------------------------------------- |
|
|
167
|
+
| Chrome desktop | Chrome 128 | Chrome 151 | Automated Ubuntu full-system-suite lane |
|
|
168
|
+
| Edge desktop | Edge 128 | Edge 151 | Manual Chromium release check (same engine as Chrome) |
|
|
169
|
+
| Chrome Android | Chrome 128 | Chrome 151 on Android | Manual real-device check |
|
|
170
|
+
| Firefox desktop | Firefox 128 ESR | Firefox 153 | Automated Ubuntu priority-smoke lane |
|
|
171
|
+
| Safari macOS | Safari 18 | Safari 26.5 | Automated `macos-latest` priority-smoke lane |
|
|
172
|
+
| Safari iOS | Safari 18 / iOS 18 | Safari 26.5 / iOS 26.5 | Manual real-device release check |
|
|
173
|
+
|
|
174
|
+
The automated matrix intentionally tests current Chrome, Firefox, and macOS
|
|
175
|
+
Safari rather than downloading historical binaries. Chrome runs the full
|
|
176
|
+
system suite. Firefox and Safari run `bin/browser-smoke`, the single maintained
|
|
177
|
+
priority lane for Dialog, Sheet, CommandPalette, Dropdown/AppearancePicker,
|
|
178
|
+
Accordion, Hotwire lifecycle, Typeset contracts, and date-family input
|
|
179
|
+
behavior. Chrome DevTools emulation remains Chrome-only coverage and is not
|
|
180
|
+
presented as cross-engine verification. Priority smoke lanes run with one
|
|
181
|
+
worker; Chrome retains the full suite's normal parallelism.
|
|
182
|
+
|
|
183
|
+
Before release, manually repeat those flows on one current Android Chrome and
|
|
184
|
+
one current iPhone Safari, including narrow layout, VoiceOver/TalkBack where
|
|
185
|
+
available, real form submission, and month/week ISO values. Record device OS,
|
|
186
|
+
browser build, date, and any reduced-baseline behavior in the release notes.
|
|
187
|
+
Do not describe the near-floor or iOS rows as automated coverage.
|
|
188
|
+
|
|
189
|
+
The Firefox 128 ESR and older Safari behavior that cannot be installed
|
|
190
|
+
reliably on GitHub-hosted runners is represented by capability-focused tests:
|
|
191
|
+
stripping Invoker Commands and exercising Nitro fallbacks, plus the documented
|
|
192
|
+
native month/week degradation. These branch simulations supplement, but do not
|
|
193
|
+
replace, the dated real-browser checks.
|