Public profile
Shown on your comments and shared documents.
A labelled settings navigation beside one neutral content region with a Nitro-owned narrow stack.
app/components/nitro_kit/settings_layout.rbNitroKit::SettingsLayout.new { |layout| layout.navigation(label:) { layout.item(text, href:, icon:, current:) }; layout.content { ... } }One labelled navigation list of typed destinations and one neutral content region form the complete contract.
Shown on your comments and shared documents.
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.header do
card.title("Public profile", level: 4)
card.description("Shown on your comments and shared documents.")
end
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.divider
card.footer do
card.actions do
render NitroKit::Button.new(
"Save profile",
id: "gallery-settings-layout-profile-save",
variant: :primary,
size: :sm
)
end
end
end
end
endThe regions remain explicit with one destination, a single surface, and dense caller-owned content.
Policy section 1
Policy section 2
Policy section 3
Policy section 4
Policy section 5
Policy section 6
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}", size: :sm) do |card|
card.description("Policy section #{index + 1}")
end
end
end
end
end
endNitro owns the single 48rem collapse while long labels and product copy remain application data.
The two regions stack automatically at the shared max-width: 48rem conditionThese settings apply to every administrator, production environment, customer-visible status notification, security event, and invoice issued by this unusually long-named workspace.
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.description(
"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
endSettingsLayout owns only the two regions; Toolbar and PaginationBar retain their own placement contracts.
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
endAll twenty Gallery::Hostile navigation labels, unbroken, RTL, CJK, and emoji, beside content titled with an unbroken word and filled with four paragraphs, a webhook URL, and every hostile action.
This description keeps going well past the point where a sensible product would have stopped, because real customers paste release notes, legal disclaimers, and entire support tickets into fields that were designed for a single line of text. This description keeps going well past the point where a sensible product would have stopped, because real customers paste release notes, legal disclaimers, and entire support tickets into fields that were designed for a single line of text. This description keeps going well past the point where a sensible product would have stopped, because real customers paste release notes, legal disclaimers, and entire support tickets into fields that were designed for a single line of text. This description keeps going well past the point where a sensible product would have stopped, because real customers paste release notes, legal disclaimers, and entire support tickets into fields that were designed for a single line of text.
test/dummy/app/components/gallery/components/settings_layout_page.rbrender NitroKit::SettingsLayout.new(id: "gallery-settings-layout-hostile") do |layout|
layout.navigation(label: Gallery::Hostile::LONG_LABEL) do
Gallery::Hostile::NAVIGATION_LABELS.each_with_index do |label, index|
layout.item(
label,
href: "#hostile-destination-#{index + 1}",
icon: :settings,
current: index == 3
)
end
end
layout.content do
render NitroKit::Card.new(id: "gallery-settings-layout-hostile-card") do |card|
card.title(Gallery::Hostile::LONG_WORD, level: 4)
card.body do
p { Gallery::Hostile::LONG_PARAGRAPH }
p { a(href: Gallery::Hostile::LONG_URL) { Gallery::Hostile::LONG_URL } }
end
card.footer do
render NitroKit::ButtonGroup.new(id: "gallery-settings-layout-hostile-actions", label: "Every hostile setting action") do |group|
Gallery::Hostile::ACTION_LABELS.each_with_index do |label, index|
group.button(label, id: "gallery-settings-layout-hostile-action-#{index + 1}", size: :sm)
end
end
end
end
end
endConstructor options, rendered root, closed vocabularies, and compound boundary for this component, exactly as shipped.
docs/component_contracts.md · NitroKit::SettingsLayout
id: nildiv[data-nk=settings-layout]navigation(label:) and one content region, both block-only and declared inside the render block. The navigation requires at least one item(text, href:, icon: nil, current: false, html:, aria:, data:, desperately_need_a_class:) and renders nav > ul > li > a, where icon: names a Lucide icon and the item attribute bags land on the link; a current item uses aria-current="page" and at most one item is current. Routes remain caller-owned.Application conventions this component belongs to. Each summary is the leading section of its pattern document.
docs/patterns/application_foundation.md
User, Team, and Membership; roles belong to memberships, and tenant-owned records load through Current.team.AppShell for the authenticated product and one application-owned content gutter inside shell.main.Toolbar; keep destinations in AppNavigation.Toast::FlashMessages region for server feedback.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
render NitroKit::Button.new("Save", variant: :primary).ArgumentError; this component's accepted options are in the contract above.html: for HTML, aria: for ARIA, and data: for application data. class and style are forbidden, including inside html:.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 through data:. data-action, data-controller are additive and compose with Nitro behavior.desperately_need_a_class:. It requires a non-blank String and marks the exception with data-nk-escape="class".data-nk; owned parts emit component-qualified data-slot values such as field-control or card-title. Select on those attributes, never on classes.--nk-* custom properties in an application stylesheet. Variables beginning with --_nk-* are private component mechanics.NitroKit::Flex and NitroKit::Grid for layout. Parents own external placement and available width; components own their intrinsic geometry.data-state when styling or behavior also needs it.