The layout contract
The 13 rules every buttery screen follows, so nothing overlaps, overflows or shifts, and how they're checked.
The layout contract is the set of rules buttery's components follow and its checks enforce: things sit in flow with gaps between them, take their sizes from tokens, and never overlap, overflow or move after a press. Follow it in your own screens, and the same checks keep them right at every width.
The 13 rules
Flow layout
Siblings are laid out by
Stack,InlineorGrid, with agapfrom the spacing scale. A component never sets its own outer margin, and nothing uses negative margins.Out-of-flow is owned
absolute,fixedandstickybelong to the layer primitives (Layer,Popover,Scrim,StickyandFloatingLanes) and to a component's own parts, such as a slider's thumb. App code uses the primitives.Floating UI goes in lanes
Toasts and notices stack 8px apart in a
FloatingLaneslane, never wider than it and clear of sticky bars and toolbars, so they never cover each other.Overlays avoid collisions
Popovers and menus open 8px from their trigger without covering it, stay 8px inside the window, and scroll inside when they're taller than the room.
Sizes come from tokens
Controls take their heights from the height tokens and fixed widths from the width tokens. Everything else takes the width its container gives it.
Children can shrink
Every flex and grid child in buttery's primitives has
min-width: 0, so long content shrinks instead of pushing its neighbors off the screen.Text never overflows
One-line control text truncates with an ellipsis and keeps the whole text in a tooltip, as
Text truncatedoes. Body text wraps at 60 characters a line and display headings at 22, and numbers that change use tabular figures so they don't push their neighbors.Media keeps its shape
Images and canvases keep their aspect ratio, and
FitBoxfits content of a given aspect into the space it has, as large as it can and centered.Containers, not the viewport
Components respond to the room they're given, through container queries, so the same component works in a 340px inspector and on a 1200px page.
Overflow by priority, not by wrapping
A row that runs out of room moves its least important items into More with
OverflowCluster. Applied filter pills are the exception:FilterBarwraps them, so each one stays in sight to remove.Scrolling stays inside
The page never scrolls sideways, and a panel scrolls on its own without taking the page with it.
Sticky things reserve space
Sticky bars publish their height as
--bt-sticky-top, so a link to a heading lands below them instead of under them.Nothing moves after a press
Loading states, skeletons and labels that change, such as Continue becoming Finish, keep their exact size, and fonts load without moving text. Only what the press is about moves.
Do and don't
The first rule in code: the parent sets the space between its children.
import type { ReactElement } from "react";
import { Stack, Text } from "@butteryui/core";
import { Button } from "@butteryui/react";
export function Notice(): ReactElement {
return (
<Stack gap={3}>
<Text variant="title-sm">Payouts are paused</Text>
<Button size="sm">Add a bank account</Button>
</Stack>
);
}The Stack owns the gap, so the button sits right wherever the notice is used.
.notice-button {
margin-top: 12px;
}A margin on a child moves it wherever it's reused, and adds to whatever gap its next parent sets.
How it's checked
While you work
LayoutGuardoutlines anything that overlaps, overflows, crowds the next line, breaks a word, sits out of line or drifts off the scale. ⌥⇧L hides the outlines and ⌥⇧G shows the grid.In your tests
expectButtery(page)from@butteryui/testruns the same checks in Playwright, at every width, theme and density you ask for, with a fix for each problem.In your styles
buttery's stylelint config fails raw colors, lengths off the scale,
z-indexvalues outside a layer band, negative margins andtransition: all.
Related
- Stack, Inline and Grid
The three flow primitives that lay out everything in buttery, and the divider between their children.
- Responsive
Why buttery components respond to their container rather than the window, and how to do the same.
- Check your layout
Catching overlaps, overflow and layout shift while you work and in your tests.