Settings layout
A labelled settings navigation beside one neutral content region with a Nitro-owned narrow stack.
- Source
app/components/nitro_kit/settings_layout.rb- API
NitroKit::SettingsLayout.new { |layout| layout.navigation(label:) { layout.item(text, href:, icon:, current:) }; layout.content { ... } }
Required regions
One labelled navigation list of typed destinations and one neutral content region form the complete contract.
Workspace settings
test/dummy/app/components/gallery/components/settings_layout_page.rbrender NitroKit::SettingsLayout.new(id: "gallery-settings-layout-workspace") do |layout|
layout.navigation(label: "Workspace settings") do
DESTINATIONS.each do |destination, icon|
layout.item(
destination.to_s.humanize,
href: "##{destination}",
icon:,
current: destination == :profile
)
end
end
layout.content do
render NitroKit::Card.new(id: "gallery-settings-layout-profile-card") do |card|
card.title("Public profile", level: 4)
card.body do
render NitroKit::Field.new(
nil,
:name,
id: "gallery-settings-layout-profile-name",
name: "profile[name]",
value: "Ada Lovelace",
label: "Name"
)
end
card.footer do
render NitroKit::Button.new(
"Save profile",
id: "gallery-settings-layout-profile-save",
variant: :primary
)
end
end
end
endContent cardinality
The regions remain explicit with one destination, a single surface, and dense caller-owned content.
One and many
test/dummy/app/components/gallery/components/settings_layout_page.rbsample("One destination", slug: "one") do
destinations = [ [ "Profile", "#profile", :user_round ] ]
render NitroKit::SettingsLayout.new(id: "gallery-settings-layout-one") do |layout|
layout.navigation(label: "Account settings") do
destinations.each do |text, href, icon|
layout.item(text, href:, icon:, current: true)
end
end
layout.content do
render NitroKit::Alert.new(id: "gallery-settings-layout-one-alert") do |alert|
alert.title("Nothing needs attention")
alert.description("Your account settings are current.")
end
end
end
end
sample("Dense content", slug: "many") do
render NitroKit::SettingsLayout.new(id: "gallery-settings-layout-many") do |layout|
layout.navigation(label: "Operations settings") do
DESTINATIONS.each do |destination, icon|
layout.item(
destination.to_s.humanize,
href: "##{destination}",
icon:,
current: destination == :security
)
end
end
layout.content do
render NitroKit::Flex.new(dir: :col, gap: 2, align: :stretch) do
6.times do |index|
render NitroKit::Card.new(id: "gallery-settings-layout-many-#{index + 1}") do |card|
card.body("Policy section #{index + 1}")
end
end
end
end
end
endNarrow and long pressure
Nitro owns the single 48rem collapse while long labels and product copy remain application data.
Long organization settings
- API
The two regions stack automatically at the shared max-width: 48rem condition
Public organization identity and verified domains
test/dummy/app/components/gallery/components/settings_layout_page.rbrender NitroKit::SettingsLayout.new(id: "gallery-settings-layout-long") do |layout|
layout.navigation(label: "Analytical Engines — International Research and Production settings") do
layout.item(
"Public organization identity and verified domains",
href: "#identity",
icon: :building_2,
current: true
)
layout.item("Credential rotation and browser session policy", href: "#security", icon: :key_round)
layout.item("Deployment notification delivery preferences", href: "#notifications", icon: :bell)
end
layout.content do
render NitroKit::Card.new(id: "gallery-settings-layout-long-card") do |card|
card.title("Public organization identity and verified domains", level: 4)
card.body(
"These settings apply to every administrator, production environment, customer-visible status " \
"notification, security event, and invoice issued by this unusually long-named workspace."
)
end
end
endNested compositions
SettingsLayout owns only the two regions; Toolbar and PaginationBar retain their own placement contracts.
Audit settings
Audit retention
Showing audit records 1–25 of 240
test/dummy/app/components/gallery/components/settings_layout_page.rbrender NitroKit::SettingsLayout.new(id: "gallery-settings-layout-audit") do |layout|
layout.navigation(label: "Audit settings") do
DESTINATIONS.each do |destination, icon|
layout.item(
destination.to_s.humanize,
href: "##{destination}",
icon:,
current: destination == :activity
)
end
end
layout.content do
render NitroKit::Flex.new(dir: :col, gap: 4, align: :stretch) do
render NitroKit::Toolbar.new(id: "gallery-settings-layout-audit-toolbar") do |toolbar|
toolbar.leading { h3 { "Audit retention" } }
toolbar.trailing do
render NitroKit::Button.new(
"Export records",
id: "gallery-settings-layout-audit-export",
variant: :primary
)
end
end
render NitroKit::PaginationBar.new(id: "gallery-settings-layout-audit-pagination-bar") do |bar|
bar.summary("Showing audit records 1–25 of 240")
bar.pagination(NitroKit::Pagination.new(label: "Audit record pages")) do |pagination|
pagination.prev
pagination.page(1, current: true)
pagination.page(2, href: "?page=2")
pagination.next(href: "?page=2")
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::SettingsLayout
- Constructor
id: nil- Root
div[data-nk=settings-layout]- Compound contract
- Exactly one
navigation(label:)and onecontentregion, both block-only and declared inside the render block. The navigation requires at least oneitem(text, href:, icon: nil, current: false, html:, aria:, data:, desperately_need_a_class:)and rendersnav > ul > li > a, whereicon:names a Lucide icon and the item attribute bags land on the link; a current item usesaria-current="page"and at most one item is current. Routes remain caller-owned.
Relevant patterns
Application conventions this component belongs to. Each summary is the leading section of its pattern document.
docs/patterns/application_foundation.md
Application foundation
- Model
User,Team, andMembership; roles belong to memberships, and tenant-owned records load throughCurrent.team. - Use one
AppShellfor the authenticated product and one application-owned content gutter insideshell.main. - Put route titles and persistent actions in the shell
Toolbar; keep destinations inAppNavigation. - Use links for settings destinations and one layout-level
Toast::FlashMessagesregion for server feedback.
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.