Skip to content
ExamsLibraryFor agentsConnect your agent

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.

  1. 1Grow, don't clip
  2. 2The overflow traps
  3. 3Breakpoints and containers
  4. 4Hint at what scrolls
  5. 5Start and end, not left and right

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

Änderungen speichernAbbrechen
.text   { }                               /* refuses to shrink */
.btn    { width: 96px; height: 36px; overflow: hidden; }
.actions{ display: flex; gap: 12px; }
Fits the mockup, fails the world. Then apply the fixes.

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-width and min-height, never a fixed width or height. A width: 96px; overflow: hidden button 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-height with overflow-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 seeFix
A grid column or flex child with long text pushes off-screenminmax(0, 1fr) on the track, min-width: 0 on the child
width: 100vw scrolls sideways on desktopwidth: 100% (100vw includes the scrollbar)
height: 100vh panel cut off on a phone100dvh (next lesson)
A modal taller than the screenmax-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: left
سارةأرسلت ملفًا إلى Acme Inc.٩:٤١
margin-inline-start, text-align: start
سارةأرسلت ملفًا إلى Acme Inc.٩:٤١

Logical 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.

Physical CSS stays put. Logical CSS mirrors, arrow included.

are the start/end versions of every physical one:

PhysicalLogical
margin-left, padding-rightmargin-inline-start, padding-inline-end
left, right (positioned)inset-inline-start, inset-inline-end
text-align: lefttext-align: start
Tailwind ml-4, pl-4ms-4, ps-4

Rules for from Jakub Krehel's work:

  • Keep visual order equal to DOM order. Moving things with order or row-reverse makes 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.
Flipping dir in devtools checks the layout, not the content. Test with real RTL text and an English name inside it.
Stress-test and fix· for your agent
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?

What this lesson builds on and leads to, across every course.

  • Grouping by space
  • Routing the job
  • Break it, then ship it