Flow utility
One CSS rule that adds consistent vertical spacing between any child elements — no margins scattered through your markup, no specificity battles.
Without .flow
Article title
This paragraph sits directly beneath the heading with no breathing room between elements.
Another paragraph crammed up against the one before it.
- List item one
- List item two
With .flow
Article title
This paragraph breathes. Every sibling gets consistent space above it from a single rule.
Another paragraph, perfectly spaced without any individual margins.
- List item one
- List item two
1em
Quick implementation
.flow > * + * {
margin-block-start: var(--flow-space, 1em);
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building a design-system utility layer.
Goal: Implement a "flow" utility class that adds consistent vertical spacing
between sibling child elements using the lobotomized owl selector — no
JavaScript, no individual margins scattered across components.
Technical constraints:
- Use the selector .flow > * + * targeting only direct children.
- Apply margin-block-start (not margin-top) to respect writing modes.
- Use var(--flow-space, 1em) so callers can override spacing locally
by setting --flow-space on the .flow element or any ancestor.
- Do not set margin on the first child — the + combinator handles this.
- The rule should be exactly one declaration and work for any mix of
block-level children: p, h1–h6, ul, ol, blockquote, pre, figure.
Framework variant (pick one):
A) Single utility class in a global stylesheet — drop .flow on any
container and all direct children receive consistent spacing.
B) React component — accept a <Flow> wrapper that renders a div with
the .flow class; accept a "space" prop (string, e.g. "1.5rem") that
sets --flow-space as an inline CSS custom property.
Edge cases to handle:
- Nested .flow elements each create their own spacing context; inner
--flow-space overrides do not bleed out to the parent.
- If a child is itself a .flow container, its first child will still
receive margin-block-start from the parent's owl selector — add a
reset (.flow .flow) if needed to prevent doubled spacing.
- When --flow-space is set to 0, the first-child exclusion still holds;
only subsequent siblings collapse to zero, not the container itself.
- Document that .flow is not a replacement for gap in flex/grid layouts;
it is specifically for document-flow content such as article prose.
Return CSS only (or a React component if variant B is chosen).
Why this works
The .flow utility is a single CSS rule from the Every Layout system by Heydon Pickering and Andy Bell. At its core is the lobotomized owl selector — > * + * — which targets any direct child element that is immediately preceded by another sibling. The + combinator means the first child is never matched, so it never gets a top margin. Every subsequent sibling does.
The result: one rule handles every combination of p, h2, ul, blockquote, or pre inside a prose container. You never need to write p + h2 { margin-top: … } or ul + p { margin-top: … } rules for each element pair.
The > child combinator keeps the selector scoped to direct children only. Grandchildren are unaffected, so nested .flow containers each manage their own spacing without interference from ancestors.
Customising --flow-space
Because the spacing value comes from var(--flow-space, 1em), you can override it at any level in the cascade. Set it on the .flow element itself, or on any ancestor, and all children within that scope pick up the new value. This makes it trivial to create tighter or looser flow regions without touching the utility class:
/* Default 1em spacing */
.flow > * + * {
margin-block-start: var(--flow-space, 1em);
}
/* Tight spacing for a sidebar */
.sidebar {
--flow-space: 0.5rem;
}
/* Generous spacing for a long-form article */
.article-body {
--flow-space: 1.5rem;
}
/* Per-element override — create extra space before a heading */
.article-body h2 {
--flow-space: 2.5rem;
}
The per-element override on h2 works because margin-block-start is applied to the element itself, and --flow-space is resolved in that element's own scope — so setting it on h2 only affects the margin above that heading, not every sibling.
Flow vs gap
The flow utility and gap solve similar problems but in different contexts. Use gap when elements are arranged with display: flex or display: grid — it distributes space between all items uniformly and works in both axes. Use .flow for document-flow content: article prose, card bodies, form sections, or any container where elements stack in normal block flow.
The key difference is that gap requires a flex or grid formatting context, whereas .flow works on any block container. Additionally, gap applies equal space on both sides of the gap, while margin-block-start only applies space above each element — the first element gets no leading space, which is almost always the right behaviour for prose.
A practical rule: if you reach for flexbox or grid, use gap. If you have a stack of mixed block-level content — headings, paragraphs, lists — use .flow.