Personalization
Choose a fixed appearance or keep this browser in step with the operating system.
Native light, dark, and system preferences synchronized through one document runtime.
app/components/nitro_kit/appearance_picker.rbNitroKit::AppearancePicker.new(id:, label: "Appearance")Light, dark, and system remain labelled radio choices without JavaScript.
test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::AppearancePicker.new(id: "gallery-appearance-default")test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::AppearancePicker.new(
id: "gallery-appearance-product",
label: "Color appearance"
)test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::AppearancePicker.new(
id: "gallery-appearance-radios",
presentation: :radios
)test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::AppearancePicker.new(
id: "gallery-appearance-select",
presentation: :select
)test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::AppearancePicker.new(
id: "gallery-appearance-dropdown",
presentation: :dropdown
)preference: renders the server-saved choice without JavaScript; once JavaScript connects, every picker mirrors this document's one preference.
test/dummy/app/components/gallery/components/appearance_picker_page.rbNitroKit::AppearancePicker::PREFERENCES.each do |preference|
sample("Rendered with preference: :#{preference}", slug: preference.to_s) do
render NitroKit::AppearancePicker.new(
id: "gallery-appearance-preference-#{preference}",
label: "Saved appearance",
preference:
)
end
endEvery mounted picker reflects the one document-owned preference.
test/dummy/app/components/gallery/components/appearance_picker_page.rbsample("Navigation control", slug: "navigation-control") do
render NitroKit::AppearancePicker.new(
id: "gallery-appearance-navigation",
label: "Site appearance"
)
end
sample("Settings control", slug: "settings-control") do
render NitroKit::AppearancePicker.new(
id: "gallery-appearance-settings",
label: "Account appearance"
)
endAppearance is a document preference, even when the control lives in a settings surface.
Choose a fixed appearance or keep this browser in step with the operating system.
test/dummy/app/components/gallery/components/appearance_picker_page.rbrender NitroKit::Card.new(id: "gallery-appearance-card") do |card|
card.title("Personalization", level: 3)
card.body do
p { "Choose a fixed appearance or keep this browser in step with the operating system." }
render NitroKit::AppearancePicker.new(
id: "gallery-appearance-card-control",
label: "Interface appearance"
)
end
card.footer do
render NitroKit::Badge.new("Saved in this browser", variant: :outline, color: :neutral)
end
endConstructor options, rendered root, closed vocabularies, and compound boundary for this component, exactly as shipped.
docs/component_contracts.md · NitroKit::AppearancePicker
id:label: defaulting to I18n.t("nitro_kit.appearance_picker.label")label_visible: truepresentation: :segmentedpreference: :systemfieldset, label, or div[data-nk=appearance-picker]; presentations segmented radios select dropdown; preferences light dark systempreference: renders the server-persisted choice as the initial data-state, checked radio, selected option, and trigger icon, so a stored preference does not flash. It requests changes from and subscribes to the document runtime; zero or many picker instances do not duplicate document media/storage listeners. id: follows the shared fragment-safe identifier contract. Segmented selection styling keys off the native :checked radio, with data-state mirrored only by the controller; enhanced dropdown preference items expose role="menuitemradio" with aria-checked reflecting the document preference. Preference names come from nitro_kit.appearance_picker.preferences.*. label_visible: false requires the segmented presentation, renders no legend — browsers give legends special layout — and names the fieldset through aria-label; the segmented row fills its container and the segments share the width equally.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.