Flex
Responsive rows and columns with Tailwind-style breakpoint shorthand and closed layout values.
- Source
app/components/nitro_kit/flex.rb- API
NitroKit::Flex.new(dir: "col md:row", gap: "2 md:4", align:, justify:, wrap:)
Mobile-first shorthand
Each property accepts a base value followed by the fixed sm md lg xl 2xl breakpoint prefixes; omitted breakpoints inherit the previous value.
One layout, three arrangements
- API
dir: "col md:row", gap: "2 md:4 lg:6", align: "stretch md:center", justify: "start lg:between"
test/dummy/app/components/gallery/components/flex_page.rbrender NitroKit::Container.new(size: :lg) do
render NitroKit::Flex.new(
dir: "col md:row",
gap: "2 md:4 lg:6",
align: "stretch md:center",
justify: "start lg:between",
wrap: "nowrap md:wrap",
id: "gallery-flex-responsive"
) do
render NitroKit::Card.new(id: "gallery-flex-responsive-summary") do |card|
card.title("Production workspace", level: 4)
card.body("Stacks on small screens, then becomes a row without application CSS.")
end
render NitroKit::Badge.new(
"12 active members",
id: "gallery-flex-responsive-status",
color: :success
)
render NitroKit::Button.new(
"Open workspace",
id: "gallery-flex-responsive-action",
variant: :primary
)
end
endEvery breakpoint and property
- API
base + sm + md + lg + xl + 2xl
test/dummy/app/components/gallery/components/flex_page.rbrender NitroKit::Flex.new(
dir: "col sm:row md:row-reverse lg:col-reverse xl:row 2xl:col",
gap: "1 sm:2 md:4 lg:8 xl:12 2xl:16",
align: "start sm:center md:end lg:stretch xl:baseline 2xl:center",
justify: "start sm:center md:end lg:between xl:around 2xl:evenly",
wrap: "nowrap sm:wrap md:wrap-reverse lg:nowrap xl:wrap 2xl:wrap-reverse",
id: "gallery-flex-breakpoints"
) do
6.times do |index|
render NitroKit::Badge.new(
"Region #{index + 1}",
id: "gallery-flex-breakpoint-#{index + 1}",
variant: :outline
)
end
endDirection
Rows, columns, and their reverse forms share one primitive and the same responsive grammar.
Every direction
test/dummy/app/components/gallery/components/flex_page.rb%w[row col row-reverse col-reverse].each do |direction|
sample(direction.humanize, slug: direction) do
render NitroKit::Flex.new(
dir: direction,
gap: 2,
align: :start,
id: "gallery-flex-dir-#{direction}"
) do
3.times do |index|
render NitroKit::Badge.new(
"Item #{index + 1}",
id: "gallery-flex-dir-#{direction}-#{index + 1}",
variant: :outline
)
end
end
end
endGap and alignment
Numeric gaps follow Nitro's spacing unit. Alignment includes start, center, end, stretch, and baseline.
Responsive gap
test/dummy/app/components/gallery/components/flex_page.rbrender NitroKit::Flex.new(
dir: :row,
gap: "1 sm:2 md:4 lg:8 xl:12 2xl:16",
align: :center,
wrap: :wrap,
id: "gallery-flex-gap-responsive"
) do
6.times do |index|
render NitroKit::Badge.new(
"Space #{index + 1}",
id: "gallery-flex-gap-responsive-#{index + 1}",
color: :info
)
end
endEvery alignment
A second line makes cross-axis alignment visible.
A second line makes cross-axis alignment visible.
A second line makes cross-axis alignment visible.
A second line makes cross-axis alignment visible.
A second line makes cross-axis alignment visible.
test/dummy/app/components/gallery/components/flex_page.rb%w[start center end stretch baseline].each do |alignment|
sample(alignment.humanize, slug: alignment) do
render NitroKit::Flex.new(
dir: :row,
gap: 2,
align: alignment,
id: "gallery-flex-align-#{alignment}"
) do
render NitroKit::Card.new do |card|
card.body("Short")
end
render NitroKit::Card.new do |card|
card.body do
strong { "Taller surface" }
p { "A second line makes cross-axis alignment visible." }
end
end
end
end
endDistribution and wrapping
Justification and wrapping remain independent, responsive decisions.
Every justification
test/dummy/app/components/gallery/components/flex_page.rb%w[start center end between around evenly].each do |justification|
sample(justification.humanize, slug: justification) do
render NitroKit::Flex.new(
dir: :row,
gap: 2,
align: :center,
justify: justification,
id: "gallery-flex-justify-#{justification}"
) do
render NitroKit::Button.new("Previous", size: :sm)
render NitroKit::Button.new("Continue", size: :sm, variant: :primary)
end
end
endEvery wrap mode
test/dummy/app/components/gallery/components/flex_page.rb%w[nowrap wrap wrap-reverse].each do |wrap|
sample(wrap.humanize, slug: wrap) do
render NitroKit::Container.new(size: :sm) do
render NitroKit::Flex.new(
dir: :row,
gap: 2,
align: :center,
wrap:,
id: "gallery-flex-wrap-#{wrap}"
) do
8.times do |index|
render NitroKit::Button.new("Environment #{index + 1}", size: :sm)
end
end
end
end
endApplication composition
Responsive properties combine for familiar headers and action regions without utility classes.
Workspace heading
Analytical Engines
International Research, Production, and Reliability Engineering
test/dummy/app/components/gallery/components/flex_page.rbrender NitroKit::Container.new(size: :xl) do
render NitroKit::Flex.new(
dir: "col lg:row",
gap: "4 lg:6",
align: "stretch lg:center",
justify: "start lg:between",
id: "gallery-flex-composition"
) do
render NitroKit::Flex.new(dir: :col, gap: 1, align: :start) do
render NitroKit::Badge.new("Operational", color: :success)
h3 { "Analytical Engines" }
p { "International Research, Production, and Reliability Engineering" }
end
render NitroKit::Flex.new(
dir: :row,
gap: 2,
align: :center,
justify: :end,
wrap: :wrap,
id: "gallery-flex-composition-actions"
) do
render NitroKit::Button.new("View activity")
render NitroKit::Button.new("Create deployment", variant: :primary)
end
end
endComponent contract
Constructor options, rendered root, closed vocabularies, and compound boundary for this component, exactly as shipped.
docs/component_contracts.md · NitroKit::Flex
- Constructor
- required
dir:;gap: 4,align: :start,justify: :start,wrap: :nowrap,id: nil - Root
div[data-nk=flex]- Contract
- Direction, gap, alignment, justification, and wrapping accept scalar or responsive values. Direct content; empty content is valid.
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.