Spacing & sizing
The shared spacing unit, control heights, content widths, and responsive boundaries.
- Source
src/stylesheets/nitro_kit/tokens.css + app/components/nitro_kit/layout_options.rb- API
var(--nk-space), gap: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16
Spacing
The closed Flex and Grid gap scale multiplies the shared 0.25rem (4px) base unit.
Every gap
gap: 00rem · 0pxgap: 10.25rem · 4pxgap: 20.5rem · 8pxgap: 30.75rem · 12pxgap: 41rem · 16pxgap: 51.25rem · 20pxgap: 61.5rem · 24pxgap: 82rem · 32pxgap: 102.5rem · 40pxgap: 123rem · 48pxgap: 164rem · 64pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
SPACING_STEPS.each do |step|
rem = step / 4.0
measurement(
key: "space-#{step}",
label: "gap: #{step}",
value: "#{format_number(rem)}rem · #{step * 4}px",
css_value: "calc(var(--nk-space) * #{step})"
)
end
endControl heights
Five Nitro-specific tokens keep interactive controls aligned across component families, sharing one inline padding. On coarse pointers, buttons extend an invisible touch target to the large step, so taps meet the 44px minimum while the rendered size stays put.
Every control height
--nk-control-height-xs1.5rem · 24px--nk-control-height-sm2rem · 32px--nk-control-height-md2.25rem · 36px--nk-control-height-lg2.75rem · 44px--nk-control-height-xl3.5rem · 56px--nk-control-padding-inline0.75rem · 12pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
CONTROL_HEIGHTS.each do |size, (rem, pixels)|
measurement(
key: "control-#{size}",
label: "--nk-control-height-#{size}",
value: "#{rem} · #{pixels}",
css_value: "var(--nk-control-height-#{size})",
axis: :height
)
end
measurement(
key: "control-padding-inline",
label: "--nk-control-padding-inline",
value: "0.75rem · 12px",
css_value: "var(--nk-control-padding-inline)"
)
endChoice sizes
Checkboxes and radios have one comfortable size and one emphasized size; every box, glyph, and indent derives from these two tokens.
Every choice size
--nk-choice-size-md1.125rem · 18px--nk-choice-size-lg1.5rem · 24pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
CHOICE_SIZES.each do |size, (rem, pixels)|
measurement(
key: "choice-#{size}",
label: "--nk-choice-size-#{size}",
value: "#{rem} · #{pixels}",
css_value: "var(--nk-choice-size-#{size})",
axis: :height
)
end
endIcon sizes
The Icon ladder also sizes every owned glyph: Alert status icons and the Accordion chevron resolve through the same axis.
Every icon size
--nk-icon-size-xs0.75rem · 12px--nk-icon-size-sm1rem · 16px--nk-icon-size-md1.25rem · 20px--nk-icon-size-lg1.75rem · 28px--nk-icon-size-xl2.25rem · 36pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
ICON_SIZES.each do |size, (rem, pixels)|
measurement(
key: "icon-#{size}",
label: "--nk-icon-size-#{size}",
value: "#{rem} · #{pixels}",
css_value: "var(--nk-icon-size-#{size})",
axis: :height
)
end
endAvatar sizes
Avatar and AvatarStack share one size ladder, from a list row up to a profile header.
Every avatar size
--nk-avatar-size-xs1.5rem · 24px--nk-avatar-size-sm2rem · 32px--nk-avatar-size-md3rem · 48px--nk-avatar-size-lg4rem · 64pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
AVATAR_SIZES.each do |size, (rem, pixels)|
measurement(
key: "avatar-#{size}",
label: "--nk-avatar-size-#{size}",
value: "#{rem} · #{pixels}",
css_value: "var(--nk-avatar-size-#{size})",
axis: :height
)
end
endContent widths
Container uses four Nitro-specific readable maximum widths; these are not Tailwind container breakpoints. Bars are normalized to xl so their proportions remain visible.
Every content width
--nk-content-sm24rem · 384px--nk-content-md32rem · 512px--nk-content-lg48rem · 768px--nk-content-xl64rem · 1024pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
CONTENT_WIDTHS.each do |size, (rem, pixels)|
measurement(
key: "content-#{size}",
label: "--nk-content-#{size}",
value: "#{rem} · #{pixels}",
css_value: percentage(rem, maximum: CONTENT_WIDTHS.fetch(:xl).first)
)
end
endResponsive breakpoints
Responsive Flex and Grid values use Tailwind’s default mobile-first sm, md, lg, xl, and 2xl boundaries. Bars are normalized to 2xl so every boundary remains comparable.
Every breakpoint
sm40rem · 640pxmd48rem · 768pxlg64rem · 1024pxxl80rem · 1280px2xl96rem · 1536pxtest/dummy/app/components/gallery/foundations/spacing_sizing_page.rbdiv(role: "list", data: { gallery: "measure-list" }) do
BREAKPOINTS.each do |name, rem|
pixels = rem.delete_suffix("rem").to_f * 16
measurement(
key: "breakpoint-#{name}",
label: name,
value: "#{rem} · #{format_number(pixels)}px",
css_value: percentage(rem, maximum: BREAKPOINTS.fetch("2xl"))
)
end
endSystem 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.