Once UI Blog
Posts
Once UI
Breakpoints sit on the prop
Once UI 2.0: A system an AI can build with
What made Once UI 2.0 possible
Once UI 2.0 alpha: Measured, not assumed
Colour arrives in pairs
Once UI 1.8: Polish as a feature
Once UI 1.7: Form with Intent
From products to systems: a new service model
Magic Journal: A different medium for long-form writing
Supa Social: The next milestone of our journey
Once UI is open source. Here's how we plan to keep it that way.
Designing a timeless, AI-native brand
Once UI 1.5: Curiosity in code
Once UI 1.4 migration guide: The breakpoint object
Launching in the AI-native era: A guide for indie builders
The first design system for indie builders
Builder Stories
Side and Once UI: From café website to reservation system
SatisPro and Once UI: Scaling smarter review management
IQON and Once UI: Scaling with a design system
Dev: Blending science and code with Once UI
JExcellence and Once UI: Turning experiments into enterprise apps
OsmyReal: Launching a platform for mobile gaming with minimal coding
TrademarkTrademark
Ctrl k
Search…
Sign up
Once UI Blog
Posts
Once UI
Breakpoints sit on the prop
Once UI 2.0: A system an AI can build with
What made Once UI 2.0 possible
Once UI 2.0 alpha: Measured, not assumed
Colour arrives in pairs
Once UI 1.8: Polish as a feature
Once UI 1.7: Form with Intent
From products to systems: a new service model
Magic Journal: A different medium for long-form writing
Supa Social: The next milestone of our journey
Once UI is open source. Here's how we plan to keep it that way.
Designing a timeless, AI-native brand
Once UI 1.5: Curiosity in code
Once UI 1.4 migration guide: The breakpoint object
Launching in the AI-native era: A guide for indie builders
The first design system for indie builders
Builder Stories
Side and Once UI: From café website to reservation system
SatisPro and Once UI: Scaling smarter review management
IQON and Once UI: Scaling with a design system
Dev: Blending science and code with Once UI
JExcellence and Once UI: Turning experiments into enterprise apps
OsmyReal: Launching a platform for mobile gaming with minimal coding
Sponsored
Aveiro
aveiro.app
Sponsored
Aveiro
aveiro.app
TrademarkTrademark
Once UIDocumentationBlog
© Once UI. All rights reserved.
Built with Aveiro
Guides

Breakpoints sit on the prop

In Once UI, responsive layout is an object on the prop — not a media query. Pass xl, l, m, s, or xs overrides; they cascade downward. Same tokens at every width; hide, stack, or tighten without writing CSS.
Updated 2h ago
Once UI 2.0: A system an AI can build with
Once UI does not ask you to write a media query for every layout tweak. Responsive behaviour sits on the same props you already use — direction, gap, padding, hide, columns — as an object keyed by breakpoint. That is the whole model. The rest of this post is how to use it without inventing CSS.

the cascade

Breakpoints run largest to smallest: xl → l → m → s → xs. A value set at a larger size applies to every smaller size until a smaller key overrides it. Defaults under LayoutProvider are roughly: xs 480, s 768, m 1024, l 1440, xl 1920. You rarely need the numbers. You need the cascade.
<Row
  gap="24"
  padding="32"
  fillWidth
  s={{ direction: "column", gap: "16", padding: "16" }}
>
  <Column fillWidth>{/* sidebar or aside */}</Column>
  <Column fillWidth>{/* main */}</Column>
</Row>
At s and below the row becomes a column and tightens. Above s it stays a row with the base props. No @media block. No duplicate markup.

hide and show

hide is a layout prop like any other. Put chrome where it belongs at each width instead of fighting display: none in a stylesheet.
<Row fillWidth horizontal="between" paddingX="24" paddingY="16">
  <Logo wordmark />
  <Row gap="8" hide="s">
    {/* desktop nav */}
  </Row>
  <IconButton icon="menu" hide m={{ hide: false }} />
</Row>
Exact hide recipes vary by component; the idea does not. Decide what the small surface should carry, then override upward or downward — do not keep both layouts painted and hope CSS wins.

direction when Flex earns it

Prefer Row and Column for fixed direction. Reach for Flex when the axis itself must flip at a breakpoint — that is the one case a preset cannot express cleanly:
<Flex
  direction="row"
  gap="32"
  fillWidth
  m={{ direction: "column", gap: "16" }}
>
  <Column fillWidth>{/* left */}</Column>
  <Column fillWidth>{/* right */}</Column>
</Flex>
Card, Dialog, and Mask inherit the same Flex breakpoint props, so a card can tighten padding or change direction without a wrapper invented only for CSS.

Grid: columns, not direction

Grid does not take direction / horizontal / vertical. Override (and spacing) instead:
columns
<Grid
  columns="3"
  gap="24"
  fillWidth
  m={{ columns: "2" }}
  s={{ columns: "1", gap: "16" }}
>
  {/* cells */}
</Grid>
Same cascade. Same tokens. The prop that defines the layout is the one you override.

What not to do

  • Do not write a media query to change gap or padding on a Once UI layout. Put the override on the prop.
  • Do not duplicate a whole tree for "mobile" and "desktop" when a direction or hide override would do.
  • Do not invent pixel breakpoints in style. Use the keys the provider already owns.
  • Do not assume every prop is responsive on every component — Grid refuses Flex-only keys; check the type if TypeScript complains.
The longer reference lives in the breakpoints docs. This post is the decision tree: overrides on the prop, cascade downward, hide or stack instead of a second stylesheet.