nitro_kit 2.0.0.alpha.3 → 2.0.0.alpha.5
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 +194 -0
- data/README.md +34 -23
- data/STYLE_GUIDE.md +10 -0
- data/app/assets/stylesheets/nitro_kit-tailwind-v4.css +3 -3
- data/app/assets/stylesheets/nitro_kit.css +1220 -572
- data/app/components/nitro_kit/accordion.rb +1 -19
- data/app/components/nitro_kit/alert.rb +1 -11
- data/app/components/nitro_kit/app_navigation.rb +44 -9
- data/app/components/nitro_kit/appearance_picker.rb +10 -1
- data/app/components/nitro_kit/badge.rb +13 -4
- data/app/components/nitro_kit/combobox.rb +1 -17
- data/app/components/nitro_kit/command_palette.rb +1 -0
- data/app/components/nitro_kit/dropzone.rb +1 -1
- data/app/components/nitro_kit/form_builder.rb +1 -1
- data/app/components/nitro_kit/select.rb +1 -14
- data/app/components/nitro_kit/tabs.rb +1 -0
- data/app/javascript/controllers/nk/command_palette_controller.js +4 -0
- data/app/javascript/controllers/nk/tabs_controller.js +13 -1
- data/docs/agent_guide.md +85 -138
- data/docs/agent_native_spec.md +72 -400
- data/docs/browser_support.md +77 -188
- data/docs/component_contracts.md +56 -36
- data/docs/customization.md +675 -89
- data/docs/hotwire.md +61 -99
- data/docs/initialization_prompt.md +33 -57
- data/docs/migration_1_to_2.md +34 -18
- data/docs/patterns/application_foundation.md +45 -121
- data/docs/patterns/crud_resource.md +44 -129
- data/docs/patterns/destructive_action.md +45 -102
- data/docs/patterns/flash_and_toast.md +18 -36
- data/docs/patterns/inline_edit.md +21 -44
- data/docs/patterns/queryable_collection.md +65 -161
- data/docs/patterns/resource_form.md +33 -81
- data/docs/rails_conventions.md +38 -78
- data/docs/rails_integration.md +96 -474
- data/lib/nitro_kit/installation.rb +19 -3
- data/lib/nitro_kit/migration_inventory.rb +15 -8
- data/lib/nitro_kit/version.rb +1 -1
- data/plugins/nitro-kit/.codex-plugin/plugin.json +4 -4
- data/plugins/nitro-kit/skills/nitro-kit-hotwire/SKILL.md +12 -0
- data/plugins/nitro-kit/skills/nitro-kit-hotwire/agents/openai.yaml +1 -1
- data/plugins/nitro-kit/skills/nitro-kit-rails/SKILL.md +22 -0
- data/plugins/nitro-kit/skills/nitro-kit-rails/agents/openai.yaml +1 -1
- data/plugins/nitro-kit/skills/nitro-kit-ui/SKILL.md +33 -11
- data/plugins/nitro-kit/skills/nitro-kit-ui/agents/openai.yaml +1 -1
- data/src/stylesheets/nitro_kit/components/accordion.css +4 -4
- data/src/stylesheets/nitro_kit/components/alert.css +20 -29
- data/src/stylesheets/nitro_kit/components/app_navigation.css +74 -2
- data/src/stylesheets/nitro_kit/components/app_shell.css +3 -3
- data/src/stylesheets/nitro_kit/components/appearance_picker.css +20 -8
- data/src/stylesheets/nitro_kit/components/auth_shell.css +2 -2
- data/src/stylesheets/nitro_kit/components/avatar.css +12 -8
- data/src/stylesheets/nitro_kit/components/avatar_stack.css +12 -10
- data/src/stylesheets/nitro_kit/components/badge.css +13 -9
- data/src/stylesheets/nitro_kit/components/button.css +42 -25
- data/src/stylesheets/nitro_kit/components/card.css +2 -2
- data/src/stylesheets/nitro_kit/components/checkbox.css +29 -46
- data/src/stylesheets/nitro_kit/components/checkbox_group.css +3 -3
- data/src/stylesheets/nitro_kit/components/combobox.css +6 -6
- data/src/stylesheets/nitro_kit/components/command_palette.css +62 -4
- data/src/stylesheets/nitro_kit/components/container.css +6 -6
- data/src/stylesheets/nitro_kit/components/control_group.css +9 -9
- data/src/stylesheets/nitro_kit/components/danger_zone.css +6 -6
- data/src/stylesheets/nitro_kit/components/data_section.css +4 -4
- data/src/stylesheets/nitro_kit/components/details_table.css +7 -5
- data/src/stylesheets/nitro_kit/components/dialog.css +3 -3
- data/src/stylesheets/nitro_kit/components/dropdown.css +7 -7
- data/src/stylesheets/nitro_kit/components/dropzone.css +7 -7
- data/src/stylesheets/nitro_kit/components/empty_state.css +4 -4
- data/src/stylesheets/nitro_kit/components/field.css +2 -2
- data/src/stylesheets/nitro_kit/components/fieldset.css +2 -2
- data/src/stylesheets/nitro_kit/components/flex.css +1 -1
- data/src/stylesheets/nitro_kit/components/grid.css +1 -1
- data/src/stylesheets/nitro_kit/components/icon.css +5 -5
- data/src/stylesheets/nitro_kit/components/input.css +33 -6
- data/src/stylesheets/nitro_kit/components/page_header.css +4 -4
- data/src/stylesheets/nitro_kit/components/pagination.css +3 -3
- data/src/stylesheets/nitro_kit/components/pagination_bar.css +2 -2
- data/src/stylesheets/nitro_kit/components/palette.css +140 -72
- data/src/stylesheets/nitro_kit/components/progressive_image.css +1 -1
- data/src/stylesheets/nitro_kit/components/radio_button.css +28 -33
- data/src/stylesheets/nitro_kit/components/radio_button_group.css +6 -6
- data/src/stylesheets/nitro_kit/components/rich_text_area.css +2 -2
- data/src/stylesheets/nitro_kit/components/select.css +24 -8
- data/src/stylesheets/nitro_kit/components/settings_layout.css +5 -5
- data/src/stylesheets/nitro_kit/components/settings_section.css +2 -2
- data/src/stylesheets/nitro_kit/components/sheet.css +10 -10
- data/src/stylesheets/nitro_kit/components/stat_grid.css +1 -1
- data/src/stylesheets/nitro_kit/components/switch.css +34 -40
- data/src/stylesheets/nitro_kit/components/table.css +8 -4
- data/src/stylesheets/nitro_kit/components/tabs.css +5 -1
- data/src/stylesheets/nitro_kit/components/textarea.css +20 -5
- data/src/stylesheets/nitro_kit/components/toast.css +11 -58
- data/src/stylesheets/nitro_kit/components/toolbar.css +2 -2
- data/src/stylesheets/nitro_kit/components/tooltip.css +4 -0
- data/src/stylesheets/nitro_kit/components/typeset.css +4 -4
- data/src/stylesheets/nitro_kit/reset.css +6 -6
- data/src/stylesheets/nitro_kit/tokens.css +497 -74
- metadata +1 -2
- data/docs/new_app_strategy.md +0 -22
|
@@ -23,10 +23,26 @@ module NitroKit
|
|
|
23
23
|
Each skill resolves the installed gem with `bundle show nitro_kit` and reads
|
|
24
24
|
its version-matched documentation.
|
|
25
25
|
|
|
26
|
+
For greenfield planning or broad product work, check whether Nitro Kit
|
|
27
|
+
catalog or MCP tools are available. When they are, inventory and search the
|
|
28
|
+
catalog by product workflow, retrieve the relevant patterns, and state what
|
|
29
|
+
will be used, adapted, or deferred before implementation. When they are not,
|
|
30
|
+
continue with the bundled skills, documentation, and component contracts;
|
|
31
|
+
catalog access is optional and must never block the work. Nitro Kit Doctor
|
|
32
|
+
verifies integration health, not product completeness.
|
|
33
|
+
|
|
34
|
+
In a greenfield application, run `bin/rails generate phlex:install` and use
|
|
35
|
+
Phlex for the application layout, route views, and reusable UI. In an
|
|
36
|
+
established application, preserve its existing view architecture and
|
|
37
|
+
introduce Phlex and Nitro Kit only at the requested boundary unless an
|
|
38
|
+
application-wide migration is explicitly authorized.
|
|
39
|
+
|
|
26
40
|
Do not use Nitro Kit 1.x APIs, `nk_*` helpers, copied Nitro components, or
|
|
27
|
-
application-owned `controllers/nk`.
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
application-owned `controllers/nk`. Include `NitroKit` once in the base Phlex
|
|
42
|
+
component and prefer capitalized Kit methods such as `Button(...)` and
|
|
43
|
+
`Card(...)`; use `.new` only when another API requires a component object.
|
|
44
|
+
Keep routes, records, authorization, queries, DOM IDs, and server responses
|
|
45
|
+
in the application.
|
|
30
46
|
|
|
31
47
|
During migration, replace an existing form control only when Nitro Kit 2 has
|
|
32
48
|
a genuine semantic and behavioral equivalent. Otherwise preserve the control
|
|
@@ -141,13 +141,13 @@ module NitroKit
|
|
|
141
141
|
|
|
142
142
|
def inspect_table(node)
|
|
143
143
|
constructor = positional_arguments(node).first
|
|
144
|
-
return unless node.name == :render && table_constructor?(constructor)
|
|
144
|
+
return unless node.name == :render && table_constructor?(constructor)
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
return unless
|
|
148
|
-
return if
|
|
146
|
+
receiver_name = required_block_parameter_name(node)
|
|
147
|
+
return unless receiver_name
|
|
148
|
+
return if receiver_name == :table
|
|
149
149
|
|
|
150
|
-
visitor = TableCompoundVisitor.new(
|
|
150
|
+
visitor = TableCompoundVisitor.new(receiver_name)
|
|
151
151
|
visitor.visit(node.block.body) if node.block.body
|
|
152
152
|
issues.concat(visitor.issues)
|
|
153
153
|
end
|
|
@@ -166,14 +166,21 @@ module NitroKit
|
|
|
166
166
|
component = constructor.receiver.full_name.delete_prefix("NitroKit::")
|
|
167
167
|
return unless %w[Dropdown Sheet].include?(component)
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
return unless
|
|
169
|
+
receiver_name = required_block_parameter_name(node)
|
|
170
|
+
return unless receiver_name
|
|
171
171
|
|
|
172
|
-
visitor = IconTriggerVisitor.new(
|
|
172
|
+
visitor = IconTriggerVisitor.new(receiver_name, component)
|
|
173
173
|
visitor.visit(node.block.body) if node.block.body
|
|
174
174
|
issues.concat(visitor.issues)
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
+
def required_block_parameter_name(node)
|
|
178
|
+
return unless node.block.is_a?(Prism::BlockNode)
|
|
179
|
+
|
|
180
|
+
parameter = node.block.parameters&.parameters&.requireds&.first
|
|
181
|
+
parameter.name if parameter.is_a?(Prism::RequiredParameterNode)
|
|
182
|
+
end
|
|
183
|
+
|
|
177
184
|
def table_constructor?(node)
|
|
178
185
|
node.is_a?(Prism::CallNode) && node.name == :new &&
|
|
179
186
|
node.receiver&.respond_to?(:full_name) && node.receiver.full_name == "NitroKit::Table"
|
data/lib/nitro_kit/version.rb
CHANGED
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
"interface": {
|
|
15
15
|
"displayName": "Nitro Kit",
|
|
16
16
|
"shortDescription": "Build Rails UI with Nitro Kit and Hotwire",
|
|
17
|
-
"longDescription": "Model conventional Rails resources, compose Nitro Kit components,
|
|
17
|
+
"longDescription": "Model conventional Rails resources, compose Nitro Kit components, implement Hotwire interactions from the installed gem version, and use optional catalog guidance when available.",
|
|
18
18
|
"developerName": "Mikkel Malmberg",
|
|
19
19
|
"category": "Developer",
|
|
20
20
|
"capabilities": ["Read", "Write"],
|
|
21
21
|
"websiteURL": "https://nitrokit.dev",
|
|
22
22
|
"defaultPrompt": [
|
|
23
|
-
"Build this Rails feature on the Nitro Kit Rails path.",
|
|
24
|
-
"Build this Rails interface with Nitro Kit components.",
|
|
25
|
-
"Implement this interaction with Nitro Kit and
|
|
23
|
+
"Build this Rails feature on the Nitro Kit Rails path, using relevant catalog guidance when available.",
|
|
24
|
+
"Build this Rails interface with Nitro Kit components and any relevant catalog guidance available.",
|
|
25
|
+
"Implement this interaction with Nitro Kit, conventional Hotwire, and relevant catalog guidance when available."
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -25,6 +25,18 @@ Make the server response and stable DOM boundary the interaction API. Add Stimul
|
|
|
25
25
|
7. Read `NITRO_KIT_ROOT/docs/browser_support.md` for the canonical
|
|
26
26
|
full/reduced/unavailable no-JavaScript classification.
|
|
27
27
|
|
|
28
|
+
## Discover optional catalog guidance
|
|
29
|
+
|
|
30
|
+
For a broad product workflow, check whether Nitro Kit catalog or MCP tools are
|
|
31
|
+
available. When they are, inventory and search by workflow, retrieve relevant
|
|
32
|
+
patterns, and state what will be used, adapted, or deferred before
|
|
33
|
+
implementation. When they are not, continue with the installed Hotwire recipes,
|
|
34
|
+
component contracts, and source. Catalog access is optional and must never
|
|
35
|
+
block the work or be implied in the result.
|
|
36
|
+
|
|
37
|
+
For a focused interaction, use the catalog only when it is already available
|
|
38
|
+
and a higher-level pattern would materially help.
|
|
39
|
+
|
|
28
40
|
Do not proceed with a remembered Nitro Kit 1.x API. Do not copy or recreate
|
|
29
41
|
the installed gem's `nk--*` controllers under `app/javascript/controllers/nk`.
|
|
30
42
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Nitro Kit Hotwire"
|
|
3
3
|
short_description: "Build conventional Nitro Kit Hotwire flows"
|
|
4
|
-
default_prompt: "Use $nitro-kit-hotwire to build this interaction with Nitro Kit and
|
|
4
|
+
default_prompt: "Use $nitro-kit-hotwire to build this interaction with Nitro Kit, conventional Hotwire, and relevant catalog guidance when available."
|
|
@@ -24,11 +24,30 @@ established application outside the requested scope.
|
|
|
24
24
|
9. Read `NITRO_KIT_ROOT/docs/browser_support.md` before claiming an
|
|
25
25
|
interaction works without JavaScript.
|
|
26
26
|
|
|
27
|
+
## Discover optional catalog guidance
|
|
28
|
+
|
|
29
|
+
For greenfield planning or broad product work, check whether Nitro Kit catalog
|
|
30
|
+
or MCP tools are available. When they are, inventory and search by product
|
|
31
|
+
workflow, retrieve the relevant patterns, and state what will be used, adapted,
|
|
32
|
+
or deferred before implementation. When they are not, continue with the
|
|
33
|
+
installed skills, documentation, component contracts, and source. Catalog
|
|
34
|
+
access is optional and must never block the work or be implied in the result.
|
|
35
|
+
|
|
36
|
+
For a focused Rails change, use the catalog only when it is already available
|
|
37
|
+
and the task could benefit from a higher-level product pattern.
|
|
38
|
+
|
|
27
39
|
Never use a Nitro Kit 1.x helper, copied component, controller, or Tailwind
|
|
28
40
|
contract as a substitute for the installed API.
|
|
29
41
|
|
|
30
42
|
## Follow the application grammar
|
|
31
43
|
|
|
44
|
+
- In a greenfield application, run `bin/rails generate phlex:install` and use
|
|
45
|
+
Phlex for the application layout, route-level views, and reusable UI. Render
|
|
46
|
+
`Views::*` objects from controllers instead of creating ERB wrappers around
|
|
47
|
+
Phlex. In an established application, preserve its view architecture and
|
|
48
|
+
introduce Phlex only at the requested boundary unless an application-wide
|
|
49
|
+
migration is explicitly authorized. Judge this from existing view code, not
|
|
50
|
+
the Rails version or apparent age.
|
|
32
51
|
- Scope tenant data through `Current.team` or `Current.account`; record
|
|
33
52
|
`Current.user` as the actor.
|
|
34
53
|
- Put domain behavior on the model before introducing another abstraction.
|
|
@@ -63,3 +82,6 @@ Nitro Kit owns component contracts and focused progressive behavior.
|
|
|
63
82
|
Run focused model and request tests first. Add a system test only where browser
|
|
64
83
|
behavior is part of the contract. Assert status, visibility, authorization,
|
|
65
84
|
and stable DOM boundaries rather than implementation trivia.
|
|
85
|
+
|
|
86
|
+
Nitro Kit Doctor verifies integration and runtime contracts, not product
|
|
87
|
+
completeness. For broad work, verify the agreed workflow coverage separately.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Nitro Kit Rails"
|
|
3
3
|
short_description: "Build conventional Rails applications with Nitro Kit"
|
|
4
|
-
default_prompt: "Use $nitro-kit-rails to build this feature on the Nitro Kit Rails path."
|
|
4
|
+
default_prompt: "Use $nitro-kit-rails to build this feature on the Nitro Kit Rails path, using relevant catalog guidance when available."
|
|
@@ -18,32 +18,50 @@ Use the documentation shipped with the application's installed gem as the source
|
|
|
18
18
|
classifications in `NITRO_KIT_ROOT/docs/browser_support.md`.
|
|
19
19
|
7. Inspect the installed component source when constructor or compound-slot details remain unclear. Never guess a component API from memory.
|
|
20
20
|
|
|
21
|
+
## Discover optional catalog guidance
|
|
22
|
+
|
|
23
|
+
For greenfield planning or broad product UI work, check whether Nitro Kit
|
|
24
|
+
catalog or MCP tools are available. When they are, inventory and search by
|
|
25
|
+
product workflow, retrieve the relevant patterns, and state what will be used,
|
|
26
|
+
adapted, or deferred before implementation. When they are not, continue with
|
|
27
|
+
the installed skills, documentation, component contracts, and source. Catalog
|
|
28
|
+
access is optional and must never block the work or be implied in the result.
|
|
29
|
+
|
|
30
|
+
For a focused component change, use the catalog only when it is already
|
|
31
|
+
available and a higher-level composition would materially help.
|
|
32
|
+
|
|
21
33
|
For a Nitro Kit 1.x migration, read
|
|
22
34
|
`NITRO_KIT_ROOT/docs/migration_1_to_2.md` before editing. Inventory product
|
|
23
35
|
flows, behavior, application-owned button classes and Rails button helpers,
|
|
24
36
|
joined controls, and the existing semantic color, focus, radius, density, and
|
|
25
37
|
typography tokens first. Capture representative wide and narrow screenshots.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
atoms.
|
|
38
|
+
Apply the optional catalog process above, searching by workflow rather than old
|
|
39
|
+
component name and selecting high-level compositions before replacing atoms.
|
|
29
40
|
|
|
30
41
|
If the gem is not installed, say that the skill requires Nitro Kit and follow the application's requested installation scope. Do not substitute APIs from an older Nitro Kit release.
|
|
31
42
|
|
|
32
43
|
## Build the interface
|
|
33
44
|
|
|
34
|
-
1.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
1. In a greenfield application, run `bin/rails generate phlex:install` and use
|
|
46
|
+
Phlex for the application layout, route-level `Views::*`, and reusable UI.
|
|
47
|
+
Do not add ERB wrappers whose only purpose is to render Phlex. In an
|
|
48
|
+
established application with meaningful view conventions, preserve them
|
|
49
|
+
and introduce Phlex and Nitro Kit only at the requested boundary unless an
|
|
50
|
+
application-wide migration is explicitly authorized. Determine this from
|
|
51
|
+
the existing view architecture, not the Rails version or apparent age.
|
|
52
|
+
2. Reuse the highest-level Nitro block that matches the page region, then compose components inside it.
|
|
53
|
+
3. Include `NitroKit` once in the application's base Phlex component and use capitalized Kit methods such as `Button(...)` and `Card(...)`. Use `.new` only when another API needs a component object. Keep product-specific components under the application's namespace, commonly `UI::*`.
|
|
54
|
+
4. Use `NitroKit::FormBuilder` explicitly with Rails `form_with` for model-backed forms.
|
|
55
|
+
5. Keep routes, authorization, records, query policy, DOM IDs, Turbo boundaries, and response semantics in the application.
|
|
56
|
+
6. Translate the application's semantic theme into documented `--nk-*` properties instead of choosing similar raw palette values. Use `--nk-button-radius` when Button shape intentionally differs from inputs and surfaces.
|
|
57
|
+
7. Verify closed options and required compound declarations before rendering.
|
|
58
|
+
8. For authenticated CRUD, prefer a hybrid `AppShell` with a `Toolbar` that
|
|
41
59
|
owns the route's single `h1` and basic actions. The shell main region owns
|
|
42
60
|
one content gutter. Do not repeat that heading in `PageHeader`, or wrap each
|
|
43
61
|
table, form, and detail region in another Card. At narrow widths, preserve
|
|
44
62
|
the full title and persistent actions by stacking the trailing actions below
|
|
45
63
|
the title rather than clipping either region.
|
|
46
|
-
|
|
64
|
+
9. For team administration and account settings, read
|
|
47
65
|
`docs/patterns/application_foundation.md`. Put Settings after an
|
|
48
66
|
`AppNavigation` spacer and use `SettingsLayout` with plain `SettingsSection`
|
|
49
67
|
regions instead of a stack of Cards.
|
|
@@ -69,4 +87,8 @@ If the gem is not installed, say that the skill requires Nitro Kit and follow th
|
|
|
69
87
|
|
|
70
88
|
Run the smallest relevant application tests. For component rendering, assert semantic elements and owned `data-nk` or slot attributes rather than private implementation helpers. Exercise invalid and empty states when the UI accepts user input or collections.
|
|
71
89
|
|
|
90
|
+
Nitro Kit Doctor verifies integration and runtime contracts, not product
|
|
91
|
+
completeness. Do not use a green Doctor result as proof that every relevant
|
|
92
|
+
screen, state, or catalog workflow has been implemented.
|
|
93
|
+
|
|
72
94
|
For a migration, Doctor is an inventory, not visual proof. Run representative form and component rendering with `ActiveModel::Translation.raise_on_missing_translations` enabled when the application uses strict i18n. Compare the same representative flows in a browser at wide and narrow widths, exercise keyboard focus, and inspect computed styles for missing application classes, stacked Button content, broken compound corners, double focus rings, clipping, and theme drift. Re-audit rendered native buttons, Rails button helpers, and application-owned button classes before declaring the conversion complete. Search the whole application for `desperately_need_a_class:` and review every result, aiming for zero. Move layout and visual treatment to application-owned wrappers, remove generic class forwarding, accept incidental Nitro defaults, and keep unmatched product UI application-owned; retain only documented external-integration hooks.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Nitro Kit UI"
|
|
3
3
|
short_description: "Build Rails interfaces with Nitro Kit components"
|
|
4
|
-
default_prompt: "Use $nitro-kit-ui to build this Rails interface with the installed Nitro Kit components."
|
|
4
|
+
default_prompt: "Use $nitro-kit-ui to build this Rails interface with the installed Nitro Kit components and any relevant catalog guidance available."
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
> [data-slot="accordion-trigger"]
|
|
19
19
|
) {
|
|
20
20
|
display: flex;
|
|
21
|
-
|
|
22
|
-
min-
|
|
21
|
+
inline-size: 100%;
|
|
22
|
+
min-block-size: var(--nk-control-height-lg);
|
|
23
23
|
align-items: center;
|
|
24
24
|
justify-content: space-between;
|
|
25
25
|
gap: calc(var(--nk-space) * 3);
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
> [data-slot="accordion-icon"]
|
|
59
59
|
) {
|
|
60
60
|
flex: none;
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
inline-size: var(--nk-icon-size-sm);
|
|
62
|
+
block-size: var(--nk-icon-size-sm);
|
|
63
63
|
color: var(--nk-color-muted-foreground);
|
|
64
64
|
transition:
|
|
65
65
|
color var(--nk-duration-fast) var(--nk-ease),
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
--_nk-alert-background: var(--nk-color-surface);
|
|
4
4
|
--_nk-alert-border: var(--nk-color-border);
|
|
5
5
|
--_nk-alert-foreground: var(--nk-color-foreground);
|
|
6
|
-
--_nk-alert-tint-strength: 12%;
|
|
7
6
|
|
|
8
7
|
position: relative;
|
|
9
8
|
display: grid;
|
|
@@ -20,8 +19,8 @@
|
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
:where([data-nk="alert"] > [data-slot="alert-title"]) {
|
|
23
|
-
font-size: var(--nk-
|
|
24
|
-
font-weight: var(--nk-
|
|
22
|
+
font-size: var(--nk-title-compact-size);
|
|
23
|
+
font-weight: var(--nk-title-compact-weight);
|
|
25
24
|
line-height: var(--nk-leading-tight);
|
|
26
25
|
text-wrap: balance;
|
|
27
26
|
}
|
|
@@ -30,11 +29,20 @@
|
|
|
30
29
|
grid-template-columns: auto minmax(0, 1fr);
|
|
31
30
|
}
|
|
32
31
|
|
|
32
|
+
/* Centered on the title first line, whatever size the title role
|
|
33
|
+
resolves to, instead of a hand-tuned nudge for one size. */
|
|
33
34
|
:where([data-nk="alert"] > [data-slot="alert-icon"]) {
|
|
35
|
+
--_nk-alert-icon-size: var(--nk-icon-size-md);
|
|
36
|
+
|
|
34
37
|
grid-row: 1 / -1;
|
|
35
|
-
inline-size:
|
|
36
|
-
block-size:
|
|
37
|
-
margin-block-start:
|
|
38
|
+
inline-size: var(--_nk-alert-icon-size);
|
|
39
|
+
block-size: var(--_nk-alert-icon-size);
|
|
40
|
+
margin-block-start: calc(
|
|
41
|
+
(
|
|
42
|
+
var(--nk-title-compact-size) * var(--nk-leading-tight) -
|
|
43
|
+
var(--_nk-alert-icon-size)
|
|
44
|
+
) / 2
|
|
45
|
+
);
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
:where(
|
|
@@ -54,29 +62,12 @@
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
@layer nitro-kit.variant {
|
|
65
|
+
/* Colour, tint strength, and appearance switching all come from the shared
|
|
66
|
+
semantic palette, so an Alert and a Toast of the same variant render
|
|
67
|
+
identically. */
|
|
57
68
|
:where([data-nk="alert"][data-variant]) {
|
|
58
|
-
--_nk-alert-background:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
transparent
|
|
62
|
-
);
|
|
63
|
-
--_nk-alert-border: color-mix(
|
|
64
|
-
in oklab,
|
|
65
|
-
var(--_nk-palette-tint) 45%,
|
|
66
|
-
transparent
|
|
67
|
-
);
|
|
68
|
-
--_nk-alert-foreground: var(--_nk-palette-light);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
:where([data-theme="dark"] [data-nk="alert"][data-variant]) {
|
|
72
|
-
--_nk-alert-foreground: var(--_nk-palette-dark);
|
|
73
|
-
--_nk-alert-tint-strength: 20%;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
@media (prefers-color-scheme: dark) {
|
|
77
|
-
:where(:root:not([data-theme]) [data-nk="alert"][data-variant]) {
|
|
78
|
-
--_nk-alert-foreground: var(--_nk-palette-dark);
|
|
79
|
-
--_nk-alert-tint-strength: 20%;
|
|
80
|
-
}
|
|
69
|
+
--_nk-alert-background: var(--_nk-semantic-surface);
|
|
70
|
+
--_nk-alert-border: var(--_nk-semantic-border);
|
|
71
|
+
--_nk-alert-foreground: var(--_nk-semantic-content);
|
|
81
72
|
}
|
|
82
73
|
}
|
|
@@ -65,6 +65,58 @@
|
|
|
65
65
|
line-height: var(--nk-leading-tight);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/* A collapsible label is a row like the items around it: same height,
|
|
69
|
+
same inline padding, symmetric block margins, and the same hover
|
|
70
|
+
treatment, so the disclosure target is as easy to hit as a destination. */
|
|
71
|
+
:where(
|
|
72
|
+
[data-nk="app-navigation"]
|
|
73
|
+
summary[data-slot="app-navigation-section-label"]
|
|
74
|
+
) {
|
|
75
|
+
display: flex;
|
|
76
|
+
gap: var(--nk-space);
|
|
77
|
+
align-items: center;
|
|
78
|
+
min-block-size: var(--nk-control-height-md);
|
|
79
|
+
margin: var(--nk-space) 0;
|
|
80
|
+
padding: calc(var(--nk-space) * 2) calc(var(--nk-space) * 3);
|
|
81
|
+
list-style: none;
|
|
82
|
+
user-select: none;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
border-radius: var(--nk-radius-lg);
|
|
85
|
+
transition:
|
|
86
|
+
color var(--nk-duration-normal) var(--nk-ease),
|
|
87
|
+
background-color var(--nk-duration-normal) var(--nk-ease);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:where(
|
|
91
|
+
[data-nk="app-navigation"]
|
|
92
|
+
summary[data-slot="app-navigation-section-label"]::-webkit-details-marker
|
|
93
|
+
) {
|
|
94
|
+
display: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:where([data-nk="app-navigation"] [data-slot="app-navigation-section-icon"]) {
|
|
98
|
+
flex: none;
|
|
99
|
+
inline-size: var(--nk-icon-size-xs);
|
|
100
|
+
block-size: var(--nk-icon-size-xs);
|
|
101
|
+
transition: transform var(--nk-duration-fast) var(--nk-ease);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
:where(
|
|
105
|
+
[data-nk="app-navigation"]
|
|
106
|
+
[data-slot="app-navigation-section-disclosure"]:not([open])
|
|
107
|
+
[data-slot="app-navigation-section-icon"]
|
|
108
|
+
) {
|
|
109
|
+
transform: rotate(-90deg);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@media (prefers-reduced-motion: reduce) {
|
|
113
|
+
:where(
|
|
114
|
+
[data-nk="app-navigation"] [data-slot="app-navigation-section-icon"]
|
|
115
|
+
) {
|
|
116
|
+
transition-duration: 0.01ms;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
68
120
|
:where([data-nk="app-navigation"] [data-slot="app-navigation-item"]) {
|
|
69
121
|
flex: 0 0 auto;
|
|
70
122
|
min-inline-size: 0;
|
|
@@ -80,7 +132,7 @@
|
|
|
80
132
|
font-weight: var(--nk-font-weight-medium);
|
|
81
133
|
line-height: var(--nk-leading-tight);
|
|
82
134
|
text-decoration: none;
|
|
83
|
-
border-radius: var(--nk-radius-
|
|
135
|
+
border-radius: var(--nk-radius-lg);
|
|
84
136
|
transition:
|
|
85
137
|
color var(--nk-duration-normal) var(--nk-ease),
|
|
86
138
|
background-color var(--nk-duration-normal) var(--nk-ease);
|
|
@@ -96,6 +148,7 @@
|
|
|
96
148
|
|
|
97
149
|
:where(
|
|
98
150
|
[data-nk="app-navigation"] [data-slot="app-navigation-item-icon"],
|
|
151
|
+
[data-nk="app-navigation"] [data-slot="app-navigation-item-icon-end"],
|
|
99
152
|
[data-nk="app-navigation"] [data-slot="app-navigation-item-badge"]
|
|
100
153
|
) {
|
|
101
154
|
flex: 0 0 auto;
|
|
@@ -133,13 +186,32 @@
|
|
|
133
186
|
|
|
134
187
|
@media (hover: hover) {
|
|
135
188
|
:where(
|
|
136
|
-
[data-nk="app-navigation"] [data-slot="app-navigation-item-link"]:hover
|
|
189
|
+
[data-nk="app-navigation"] [data-slot="app-navigation-item-link"]:hover,
|
|
190
|
+
[data-nk="app-navigation"]
|
|
191
|
+
summary[data-slot="app-navigation-section-label"]:hover
|
|
137
192
|
) {
|
|
138
193
|
color: var(--nk-app-shell-sidebar-accent-foreground);
|
|
139
194
|
background: var(--nk-app-shell-sidebar-accent);
|
|
140
195
|
}
|
|
141
196
|
}
|
|
142
197
|
|
|
198
|
+
:where(
|
|
199
|
+
[data-nk="app-navigation"]
|
|
200
|
+
summary[data-slot="app-navigation-section-label"]:focus-visible
|
|
201
|
+
) {
|
|
202
|
+
outline: var(--nk-focus-width) solid var(--nk-color-focus);
|
|
203
|
+
outline-offset: calc(var(--nk-focus-offset) * -1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@media (prefers-reduced-motion: reduce) {
|
|
207
|
+
:where(
|
|
208
|
+
[data-nk="app-navigation"]
|
|
209
|
+
summary[data-slot="app-navigation-section-label"]
|
|
210
|
+
) {
|
|
211
|
+
transition-duration: 0.01ms;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
143
215
|
:where(
|
|
144
216
|
[data-nk="app-navigation"]
|
|
145
217
|
[data-slot="app-navigation-item-link"]:focus-visible
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
color: var(--nk-color-primary-foreground);
|
|
25
25
|
text-decoration: none;
|
|
26
26
|
background: var(--nk-color-primary);
|
|
27
|
-
border-radius: var(--nk-radius-
|
|
27
|
+
border-radius: var(--nk-radius-lg);
|
|
28
28
|
outline: var(--nk-focus-width) solid var(--nk-color-focus);
|
|
29
29
|
transform: translateY(calc(-100% - (var(--nk-space) * 4)));
|
|
30
30
|
transition: transform var(--nk-duration-fast) var(--nk-ease);
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
cursor: pointer;
|
|
80
80
|
background: transparent;
|
|
81
81
|
border: 0;
|
|
82
|
-
border-radius: var(--nk-radius-
|
|
82
|
+
border-radius: var(--nk-radius-lg);
|
|
83
83
|
transition:
|
|
84
84
|
background-color var(--nk-duration-fast) var(--nk-ease),
|
|
85
85
|
scale var(--nk-duration-fast) var(--nk-ease);
|
|
@@ -542,7 +542,7 @@
|
|
|
542
542
|
flex-direction: column;
|
|
543
543
|
inline-size: min(
|
|
544
544
|
var(--nk-app-shell-sidebar-width),
|
|
545
|
-
calc(
|
|
545
|
+
calc(100% - (var(--nk-space) * 12))
|
|
546
546
|
);
|
|
547
547
|
max-inline-size: 100%;
|
|
548
548
|
block-size: 100vh;
|
|
@@ -10,16 +10,24 @@
|
|
|
10
10
|
[data-nk="appearance-picker"] > [data-slot="appearance-picker-legend"]
|
|
11
11
|
) {
|
|
12
12
|
padding: 0;
|
|
13
|
-
font-size: var(--nk-
|
|
14
|
-
font-weight: var(--nk-
|
|
13
|
+
font-size: var(--nk-title-compact-size);
|
|
14
|
+
font-weight: var(--nk-title-compact-weight);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/* The gap below the label exists only when there is a label. */
|
|
17
18
|
:where(
|
|
18
19
|
[data-nk="appearance-picker"] > [data-slot="appearance-picker-options"]
|
|
19
20
|
) {
|
|
20
21
|
display: flex;
|
|
21
22
|
flex-wrap: wrap;
|
|
22
23
|
gap: calc(var(--nk-space) * 3);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:where(
|
|
27
|
+
[data-nk="appearance-picker"]
|
|
28
|
+
> [data-slot="appearance-picker-legend"]
|
|
29
|
+
+ [data-slot="appearance-picker-options"]
|
|
30
|
+
) {
|
|
23
31
|
margin-block-start: calc(var(--nk-space) * 2);
|
|
24
32
|
}
|
|
25
33
|
|
|
@@ -30,15 +38,17 @@
|
|
|
30
38
|
display: grid;
|
|
31
39
|
}
|
|
32
40
|
|
|
41
|
+
/* Like every other control, the segmented row fills its container and the
|
|
42
|
+
parent owns the available width; the segments share it equally. */
|
|
33
43
|
:where(
|
|
34
44
|
[data-nk="appearance-picker"][data-presentation="segmented"]
|
|
35
45
|
> [data-slot="appearance-picker-options"]
|
|
36
46
|
) {
|
|
37
|
-
display:
|
|
47
|
+
display: flex;
|
|
38
48
|
gap: 0;
|
|
39
49
|
padding: var(--nk-space);
|
|
40
50
|
background: var(--nk-color-muted);
|
|
41
|
-
border-radius: var(--nk-radius-
|
|
51
|
+
border-radius: var(--nk-radius-lg);
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
:where([data-nk="appearance-picker"] [data-slot="appearance-picker-option"]) {
|
|
@@ -53,8 +63,8 @@
|
|
|
53
63
|
:where(
|
|
54
64
|
[data-nk="appearance-picker"] [data-slot="appearance-picker-control"]
|
|
55
65
|
) {
|
|
56
|
-
inline-size:
|
|
57
|
-
block-size:
|
|
66
|
+
inline-size: var(--nk-choice-size-md);
|
|
67
|
+
block-size: var(--nk-choice-size-md);
|
|
58
68
|
margin: 0;
|
|
59
69
|
accent-color: var(--nk-color-primary);
|
|
60
70
|
cursor: pointer;
|
|
@@ -65,8 +75,10 @@
|
|
|
65
75
|
[data-slot="appearance-picker-option"]
|
|
66
76
|
) {
|
|
67
77
|
position: relative;
|
|
78
|
+
flex: 1 1 0;
|
|
79
|
+
justify-content: center;
|
|
68
80
|
padding-inline: calc(var(--nk-space) * 3);
|
|
69
|
-
border-radius: calc(var(--nk-radius-
|
|
81
|
+
border-radius: calc(var(--nk-radius-lg) - var(--nk-space));
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
:where(
|
|
@@ -90,7 +102,7 @@
|
|
|
90
102
|
font: inherit;
|
|
91
103
|
background: var(--nk-color-surface);
|
|
92
104
|
border: var(--nk-border-width) solid var(--nk-color-border);
|
|
93
|
-
border-radius: var(--nk-radius-
|
|
105
|
+
border-radius: var(--nk-radius-lg);
|
|
94
106
|
}
|
|
95
107
|
}
|
|
96
108
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
@layer nitro-kit.base {
|
|
2
2
|
:where([data-nk="avatar"]) {
|
|
3
3
|
position: relative;
|
|
4
|
+
/* The ring below carries a z-index, so without a stacking context it
|
|
5
|
+
would paint above overlapping siblings — in a stack, every ring showed
|
|
6
|
+
through the avatar covering it. Isolation keeps each avatar atomic. */
|
|
7
|
+
isolation: isolate;
|
|
4
8
|
display: inline-grid;
|
|
5
9
|
flex: none;
|
|
6
10
|
place-items: center;
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
inline-size: var(--_nk-avatar-size);
|
|
12
|
+
block-size: var(--_nk-avatar-size);
|
|
9
13
|
overflow: hidden;
|
|
10
14
|
font-size: var(--_nk-avatar-font-size);
|
|
11
15
|
font-weight: var(--nk-font-weight-medium);
|
|
@@ -32,8 +36,8 @@
|
|
|
32
36
|
[data-nk="avatar"] > [data-slot="avatar-image"]
|
|
33
37
|
) {
|
|
34
38
|
grid-area: 1 / 1;
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
inline-size: 100%;
|
|
40
|
+
block-size: 100%;
|
|
37
41
|
border-radius: inherit;
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -60,22 +64,22 @@
|
|
|
60
64
|
|
|
61
65
|
@layer nitro-kit.size {
|
|
62
66
|
:where([data-nk="avatar"][data-size="xs"]) {
|
|
63
|
-
--_nk-avatar-size:
|
|
67
|
+
--_nk-avatar-size: var(--nk-avatar-size-xs);
|
|
64
68
|
--_nk-avatar-font-size: calc(var(--nk-text-xs) * 0.875);
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
:where([data-nk="avatar"][data-size="sm"]) {
|
|
68
|
-
--_nk-avatar-size:
|
|
72
|
+
--_nk-avatar-size: var(--nk-avatar-size-sm);
|
|
69
73
|
--_nk-avatar-font-size: var(--nk-text-xs);
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
:where([data-nk="avatar"][data-size="md"]) {
|
|
73
|
-
--_nk-avatar-size:
|
|
77
|
+
--_nk-avatar-size: var(--nk-avatar-size-md);
|
|
74
78
|
--_nk-avatar-font-size: var(--nk-text-sm);
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
:where([data-nk="avatar"][data-size="lg"]) {
|
|
78
|
-
--_nk-avatar-size:
|
|
82
|
+
--_nk-avatar-size: var(--nk-avatar-size-lg);
|
|
79
83
|
--_nk-avatar-font-size: var(--nk-text-lg);
|
|
80
84
|
}
|
|
81
85
|
}
|