Toolbar
Neutral leading and trailing regions with owned distribution, wrapping, and narrow stacking.
- Source
app/components/nitro_kit/toolbar.rb- API
NitroKit::Toolbar.new { |toolbar| toolbar.leading { ... }; toolbar.trailing { ... } } # one or both regions
Region combinations
Leading and trailing are independently optional, may each appear once, and remain neutral content regions.
Every valid region shape
Workspace members
test/dummy/app/components/gallery/components/toolbar_page.rbsample("Leading only", slug: "leading-only") do
render NitroKit::Toolbar.new(id: "gallery-toolbar-leading-only") do |toolbar|
toolbar.leading do
render NitroKit::Badge.new("12 active members", id: "gallery-toolbar-leading-count", color: :success)
end
end
end
sample("Trailing only", slug: "trailing-only") do
render NitroKit::Toolbar.new(id: "gallery-toolbar-trailing-only") do |toolbar|
toolbar.trailing do
render NitroKit::Button.new(
"Save changes",
id: "gallery-toolbar-trailing-save",
variant: :primary
)
end
end
end
sample("Leading and trailing", slug: "split") do
render NitroKit::Toolbar.new(id: "gallery-toolbar-split") do |toolbar|
toolbar.leading { h3 { "Workspace members" } }
toolbar.trailing do
render NitroKit::ButtonGroup.new(label: "Member collection actions") do |group|
group.button("Export", id: "gallery-toolbar-split-export")
group.button("Invite", id: "gallery-toolbar-split-invite", variant: :primary)
end
end
end
endWrapping and narrow pressure
The root and both regions wrap at wide sizes, then stack at the shared 48rem narrow condition.
Long record controls
- API
No role=toolbar; ordinary links and buttons retain native tab order
Analytical Engines — International Research, Production, and Reliability Engineering
Credential rotation in progresstest/dummy/app/components/gallery/components/toolbar_page.rbrender NitroKit::Toolbar.new(id: "gallery-toolbar-long") do |toolbar|
toolbar.leading do
h3 { "Analytical Engines — International Research, Production, and Reliability Engineering" }
render NitroKit::Badge.new(
"Credential rotation in progress",
id: "gallery-toolbar-long-status",
color: :warning
)
end
toolbar.trailing do
render NitroKit::ButtonGroup.new(label: "Long workspace actions") do |group|
group.button("Review organization access policy", id: "gallery-toolbar-long-policy")
group.button("Invite an international research administrator", id: "gallery-toolbar-long-invite")
group.button("Open production security report", id: "gallery-toolbar-long-report", variant: :primary)
end
end
endDense actions
test/dummy/app/components/gallery/components/toolbar_page.rbrender NitroKit::Toolbar.new(id: "gallery-toolbar-dense") do |toolbar|
toolbar.leading do
8.times do |index|
render NitroKit::Badge.new(
"Filter #{index + 1}",
id: "gallery-toolbar-dense-filter-#{index + 1}",
size: :sm,
variant: :outline
)
end
end
toolbar.trailing do
5.times do |index|
render NitroKit::Button.new(
"Action #{index + 1}",
id: "gallery-toolbar-dense-action-#{index + 1}",
size: :sm
)
end
end
endReal compositions
Collection headings and form actions use the same placement boundary without arrays or action registries.
Collection heading
test/dummy/app/components/gallery/components/toolbar_page.rbrender NitroKit::Card.new(id: "gallery-toolbar-collection-card") do |card|
card.body do
render NitroKit::Toolbar.new(id: "gallery-toolbar-collection") do |toolbar|
toolbar.leading do
h3 { "API credentials" }
render NitroKit::Badge.new("4 active", id: "gallery-toolbar-collection-count", color: :info)
end
toolbar.trailing do
render NitroKit::Button.new(
"Create credential",
id: "gallery-toolbar-collection-create",
variant: :primary
)
end
end
end
endForm actions
Profile
test/dummy/app/components/gallery/components/toolbar_page.rbrender NitroKit::Container.new(size: :md) do
render NitroKit::Card.new(id: "gallery-toolbar-form-card") do |card|
card.title("Profile", level: 4)
card.body do
render NitroKit::Toolbar.new(id: "gallery-toolbar-form") do |toolbar|
toolbar.leading do
render NitroKit::Badge.new("Unsaved changes", id: "gallery-toolbar-form-status", color: :warning)
end
toolbar.trailing do
render NitroKit::Button.new("Discard", id: "gallery-toolbar-form-discard")
render NitroKit::Button.new("Save profile", id: "gallery-toolbar-form-save", variant: :primary)
end
end
end
end
endComponent contract
Constructor options, rendered root, closed vocabularies, and compound boundary for this component, exactly as shipped.
docs/component_contracts.md · NitroKit::Toolbar
- Constructor
id: nil- Root
div[data-nk=toolbar]- Compound contract
- At most one
leadingand onetrailing; at least one region total. Each region requires a content block. It deliberately has no toolbar role, sticky mode, or action registry.
Relevant patterns
Application conventions this component belongs to. Each summary is the leading section of its pattern document.
docs/patterns/queryable_collection.md · docs/patterns/crud_resource.md
Queryable collection
- One GET-driven Turbo Frame owns filters, sorting, results, and pagination; URL parameters are the state.
- An application query object owns allowlists, defaults, tenant scope, and page bounds;
NitroKit::Tableowns no query policy. - Pagination advances browser history; filters, reset, and sorting replace the current history entry.
- Every response contains the same frame, including empty results; links that leave the collection target
_top.
Complete product resource
- Define the resource, tenant boundary, actor, lifecycle, visibility, and states before writing views.
- Build index, form, detail, destructive action, and tests as one product surface.
- Use one shell toolbar title and one application-owned page gutter; do not repeat hierarchy across nested components.
- Scope every lookup through the current tenant and model meaningful lifecycle transitions as noun resources.
System rules for coding agents
Instructions for coding agents, repeated on every component page so one fetched page has enough context. Humans can usually skip this section.
test/dummy/app/components/gallery/agent_rules.rb
View agent instructions
- Nitro Kit 2.0 is a gem-owned, Phlex-only UI system for Rails. Render components directly:
render NitroKit::Button.new("Save", variant: :primary). - Compose Nitro components inside application-owned Phlex classes. Public initializers and declared slots are the component API; private methods are not.
- Public options are explicit keywords, and enumerated options are closed vocabularies. Invalid names or values raise
ArgumentError; this component's accepted options are in the contract above. - Pass native attributes through their typed boundary:
html:for HTML,aria:for ARIA, anddata:for application data.classandstyleare forbidden, including insidehtml:. - Nitro owns
NitroKit::Component::RESERVED_DATA_ATTRIBUTES:data-nk,data-slot,data-variant,data-size,data-nk-escape,data-enhanced,data-state,data-disabled,data-required,data-orientation,data-presentation,data-placement,data-layout,data-side,data-field-type,data-dir,data-gap,data-align,data-justify,data-wrap,data-cols,data-mode,data-key. Do not pass them throughdata:.data-action,data-controllerare additive and compose with Nitro behavior. - If an integration truly requires a class, use
desperately_need_a_class:. It requires a non-blank String and marks the exception withdata-nk-escape="class". - Every root emits
data-nk; owned parts emit component-qualifieddata-slotvalues such asfield-controlorcard-title. Select on those attributes, never on classes. - Customize components with documented
--nk-*custom properties in an application stylesheet. Variables beginning with--_nk-*are private component mechanics. - Use
NitroKit::FlexandNitroKit::Gridfor layout. Parents own external placement and available width; components own their intrinsic geometry. - Preserve native elements and accessibility semantics. State is exposed through native semantics and ARIA first, and through
data-statewhen styling or behavior also needs it.