Layouts that survive real content
intermediate·8 min·Markdown for agents
What this lesson teaches
Stress-test a layout with long words, translations and big text, fix the classic overflow traps, and write CSS that mirrors itself for right-to-left languages.
This card looks finished. Now feed it real-world content: switch on German, a long URL, and 200% text. The CSS is live, so it breaks for real.
Benachrichtigungseinstellungen
https://studio.example.com/projects/acme-website-redesign/issues/4821?view=board&filter=mine
.text { } /* refuses to shrink */
.btn { width: 96px; height: 36px; overflow: hidden; }
.actions{ display: flex; gap: 12px; }A layout that fits the copy in the mockup has been tested exactly once. Translations lengthen text, readers zoom, names are long and APIs return surprises. Design for the worst content, not the sample. That's a .
Grow, don't clip
- Size text containers with
max-widthandmin-height, never a fixedwidthorheight. Awidth: 96px; overflow: hiddenbutton is fine in English and cut in German. - Let rows wrap:
flex-wrap: wrap; gap: 12px. - If a box must cap its height, pair
max-heightwithoverflow-y: auto, so the content scrolls instead of vanishing. - Short strings are the riskiest. A one-word button can double in length in translation.
- Never park a critical action where clipping can reach it: the bottom of a modal taller than the screen, or anything the on-screen keyboard covers.
Test with : fake text like [Ŝéţţîñĝš ~~~~] that runs 30–40% longer, plus one long real language such as German.
The overflow traps
Most "why is there a sideways scrollbar" bugs are one of these:
| You see | Fix |
|---|---|
| A grid column or flex child with long text pushes off-screen | minmax(0, 1fr) on the track, min-width: 0 on the child |
width: 100vw scrolls sideways on desktop | width: 100% (100vw includes the scrollbar) |
height: 100vh panel cut off on a phone | 100dvh (next lesson) |
| A modal taller than the screen | max-height: 100dvh, a scrolling body, the buttons outside the scroll area |
The first one has a name: . Grid tracks and flex items refuse to shrink below their longest unbreakable thing, so one URL can push a whole layout off the side.
.layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); }
.row > .text { min-width: 0; overflow-wrap: anywhere; }Breakpoints and containers
Break where the layout stops fitting, not at 768px because a preset says so. Write queries in rem, so a reader with a larger default font gets the narrow layout sooner.
A reusable component should adapt to its container, not the window: container-type: inline-size on the parent, @container (width < 400px) on the child. Two traps: a container query can only style the container's children, never the container itself; and a container with no definite width (a shrink-to-fit flex item) collapses to zero.
Hint at what scrolls
A row of cards that ends exactly at the screen edge looks complete, and nobody scrolls it. Let the next card peek 16–32px. A "Show more" button names what it hides: "Show 12 more results". Content scrolling under a sticky header uses scroll-padding-block-start so focused fields and anchor targets aren't hidden under it.
Start and end, not left and right
Arabic and Hebrew read right to left. A layout written in left and right has to be rewritten for them; one written in start and end mirrors itself. Switch the direction below.
margin-left, text-align: leftmargin-inline-start, text-align: startLogical properties flipped the whole row for free. The physical one keeps the avatar's gap and the text alignment on the wrong side, and its back arrow points the wrong way.
are the start/end versions of every physical one:
| Physical | Logical |
|---|---|
margin-left, padding-right | margin-inline-start, padding-inline-end |
left, right (positioned) | inset-inline-start, inset-inline-end |
text-align: left | text-align: start |
Tailwind ml-4, pl-4 | ms-4, ps-4 |
Rules for from Jakub Krehel's work:
- Keep visual order equal to DOM order. Moving things with
orderorrow-reversemakes keyboard and screen-reader order disagree with what's on screen. - Mirror only icons that point along the reading direction: back and forward arrows, reply, progress chevrons. Never a play button, a clock, a checkmark or a logo.
- Physical stays physical where the glass is physical. Safe-area insets are about the phone's real left and right edges, so they need an RTL override.
- Wrap mixed-direction values in , like the English company name inside the Arabic sentence above, so punctuation lands on the correct side.
dir in devtools checks the layout, not the content. Test with real RTL text and an English name inside it.
Stress-test this component and fix what breaks: - Try pseudo-localized strings (+40%), German labels, a 120-character URL, a 40-character username and 200% text. - Text containers use max-width/min-height, never fixed width/height; rows flex-wrap with gap. - Grid tracks minmax(0, 1fr); flex children with text min-width: 0; overflow-wrap: anywhere on user text. - width: 100% instead of 100vw; no overflow: hidden used to hide a scrollbar. - Convert left/right to logical properties (margin-inline-start, text-align: start, ms-/ps- in Tailwind). Mirror only direction-bearing icons; wrap mixed-direction values in <bdi>.
Quick check
A two-column grid uses grid-template-columns: 240px 1fr, and a long URL in the main column makes the page scroll sideways. What's the fix?
In the skill
The nodes your agent reads when it does this work.
- Growth and overflowLayouts break when content grows — a German label, 200% text, a long URL. Size text boxes with max-width and min-height, let rows wrap, test with pseudo-localization. minmax(0, 1fr) and min-width 0 stop min-content blowout; 100% not 100vw; 100dvh panes; container queries for components (and the container-type collapse); breakpoints where the layout stops fitting; peeks and scroll-padding for what scrolls.
layout/growth-and-overflow - Logical properties and RTLWrite anything that mirrors as leading and trailing — margin-inline-start, inset-inline-end, text-align start, float inline-start. Physical left and right are for physical geometry (safe-area insets) with a [dir="rtl"] override; translateX and background-position have no logical form. Visual order matches DOM order, only direction-bearing icons mirror, and mixed-direction values go in <bdi>.
layout/logical-properties-rtl
Connected lessons
What this lesson builds on and leads to, across every course.