Diagnostic skill for the canonical layout failures of flex, grid, sticky, viewport units, and stacking contexts. The reader has a broken layout and needs the one-line fix plus the underlying mental model.
Quick Reference
The seven essential resets
/* 1. Universal border-box (every modern CSS reset) */
*, *::before, *::after { box-sizing: border-box; }
/* 2. Flex children : reset the min-content floor */
.flex-child { min-inline-size: 0; min-block-size: 0; }
/* 3. Grid track that MUST shrink below content : use minmax(0, 1fr) */
.grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
/* 4. Long-word safety on text containers */ .prose { overflow-wrap: anywhere; }
/* 5. Mobile-safe full-viewport hero */ .hero { block-size: 100dvh; }
/* 6. Sticky needs a defined inset AND a non-overflowing ancestor */ .sticky-nav { position: sticky; inset-block-start: 0; }
/* 7. Isolate stacking context when nesting z-index */ .layer-root { isolation: isolate; }
### Five most common bugs : one-line diagnosis
| Symptom | Diagnosis | Fix |
|---|---|---|
| Flex child stretches container | `min-width` defaulted to `min-content` | `min-inline-size: 0;` on child |
| `1fr 1fr 1fr` columns not equal | `1fr` = `minmax(auto, 1fr)` | `minmax(0, 1fr)` |
| `position: sticky` does nothing | Ancestor `overflow: hidden/auto/scroll` OR missing `inset` | Remove ancestor overflow OR set `top: 0` |
| Hero overflows on mobile | `100vh` excludes dynamic toolbar | `100dvh` (or `100svh` for guaranteed-fit) |
| `z-index: 9999` ignored | Elements in different stacking contexts | Raise parent context OR `isolation: isolate` |
## Decision Trees
### Tree 1 : Flex vs grid for this layout?
Is the layout primarily ONE-DIMENSIONAL (a row of buttons, a column of list items, navigation, a toolbar)? YES -> flexbox. flex / flex-direction / gap.
Is the layout TWO-DIMENSIONAL (cards in a grid, dashboard regions, template layouts with named areas)? YES -> CSS Grid. grid-template-columns / grid-template-rows / grid-template-areas.
Does the layout need to align items in both axes AND wrap to multiple rows? YES -> Grid with auto-fit / auto-fill. Flex with wrap works but loses cross-axis alignment between rows.
Hard call : single row that wraps to multiple rows on small screens? YES -> Both work. Flex with wrap = simpler. Grid auto-fit = equal-width tracks.
### Tree 2 : `fr` or `auto` for grid track?
Should the track grow equally with siblings, sharing leftover space? YES -> 1fr. But: 1fr = minmax(auto, 1fr); long content can push the track wider than its share. For forceful equal split : minmax(0, 1fr).
Should the track size to its content (no growth)? YES -> auto. Track shrinks to max-content; expands no further.
Should the track size to its content but capped at a maximum? YES -> fit-content(). E.g., fit-content(20rem) = grows to content but never exceeds 20rem.
Should the track be a fixed width regardless of content? YES -> a length (e.g., 200px, 16rem, 30ch).
Mixing fixed + fr + auto? -> first fixed, then auto, then fr for remaining space. Example : grid-template-columns: 200px auto minmax(0, 1fr);
### Tree 3 : Is `min-width: 0` needed?
Is the element a FLEX or GRID item? YES -> next question NO -> not needed; reset does not apply
Does the content inside the item have an intrinsic minimum size (text strings, images with intrinsic width, fixed-width children)? YES -> next question NO -> probably not needed
Is the item supposed to SHRINK below the intrinsic minimum (truncate text, hide overflow, fit narrower container)? YES -> add min-inline-size: 0 (and / or min-block-size: 0) on the item. Default min-content floor would otherwise prevent shrinking. NO -> leave default.
## Patterns
### Pattern A : Flex child overflows fix
```css
/* Symptom : long text in flex item pushes container wider */
.row { display: flex; gap: 1rem; }
.row > .item {
flex: 1;
min-inline-size: 0; /* THE FIX : override min-content floor */
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Per the flex spec, the default min-width: auto on flex items resolves to min-content, which is the size of the longest unbreakable word. min-width: 0 (min-inline-size: 0 for logical) lets the item shrink below this floor.
Pattern B : Equal-width grid columns
/* WRONG : 1fr columns may not be equal under content pressure */
.row-bad { display: grid; grid-template-columns: 1fr 1fr 1fr; }
/* CORRECT : minmax(0, 1fr) forces equal */
.row-good { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
1fr is shorthand for minmax(auto, 1fr). The auto minimum equals min-content, so a track with long text refuses to shrink below its longest word. minmax(0, 1fr) overrides to force equal split.
Pattern C : Sticky that actually sticks
.app {
/* CRITICAL : no ancestor between sticky and its scroll root may have overflow != visible */
/* That includes the body in many resets. Check the chain. */
}
.sticky-header {
position: sticky;
inset-block-start: 0; /* REQUIRED : without an inset, sticky behaves like static */
background: var(--surface);
z-index: 10;
}
position: sticky requires : (a) a defined inset-* value (e.g., top: 0), (b) a scrolling ancestor (no overflow: hidden | auto | scroll between sticky and its actual scroll root unless that ancestor IS the scrolling container with a defined height), (c) sufficient parent height for the sticky range to be visible.
Pattern D : Mobile viewport units
/* WRONG : extends past visible viewport on mobile (browser chrome) */
.hero-bad { block-size: 100vh; }
/* CORRECT : dynamic, always fills visible viewport */
.hero-good { block-size: 100dvh; }
/* Conservative : guaranteed to fit even with toolbar visible */
.modal-content { max-block-size: 100svh; }
/* Maximum extent (toolbar hidden) */
.fullscreen { block-size: 100lvh; }
| Unit | Meaning |
|---|---|
vh |
Static viewport height (initial) |
dvh |
Dynamic viewport height (changes with chrome) |
svh |
Small viewport height (chrome shown, smallest extent) |
lvh |
Large viewport height (chrome hidden, largest extent) |
vw / dvw / svw / lvw |
Same family on the inline axis |
Pattern E : Long-word overflow safety
/* Long URLs, long unbreakable strings, code snippets in prose */
.prose {
overflow-wrap: anywhere;
/* anywhere : break inside any character. Older alternative : break-word. */
}
/* Stronger : break inside CJK / non-Latin too */
.code-inline {
word-break: break-word;
}
overflow-wrap: anywhere is the modern recommendation. word-break: break-word is the older alias. Both prevent a long URL from blowing out a card.
Pattern F : Stacking context isolation
/* Symptom : z-index: 9999 ignored. */
/* Cause : a parent element creates a stacking context, trapping the child. */
/* Common stacking-context creators : */
.transformed { transform: translateZ(0); } /* creates context */
.translucent { opacity: 0.99; } /* creates context */
.filtered { filter: blur(0); } /* creates context */
.fixed { position: fixed; } /* creates context */
.sticky { position: sticky; } /* creates context */
.contained { contain: layout; } /* creates context */
.willing { will-change: transform; } /* creates context */
.isolated { isolation: isolate; } /* explicit context */
/* The fix : raise the parent's z-index OR explicitly isolate to scope z-index inside */
.layer-root { isolation: isolate; } /* all child z-index now scoped here */
z-index only orders elements WITHIN the same stacking context. A child inside a transformed parent cannot escape to overlap a sibling of that parent, no matter how large its z-index.
Pattern G : Margin collapse (when wanted and when not)
/* Margins collapse between adjacent siblings : */
.p + .p { /* effective gap = max(prev margin-bottom, next margin-top) */ }
/* Margins collapse parent <-> first child unless parent has padding / border / overflow / BFC */
.parent-wants-children-margin {
/* nothing : child margin-top "escapes" up to the parent */
}
.parent-blocks-child-margin {
padding-block-start: 1px; /* or border, or display: flex/grid, or overflow: auto, or display: flow-root */
}
Margins do NOT collapse for : flex items, grid items, floats, absolutely positioned elements, inline-blocks, elements inside a Block Formatting Context. Prefer display: flex / grid + gap for predictable spacing.
Out of Scope
- Flexbox / grid syntax tutorials (covered in
[[frontend-syntax-css-grid-subgrid]]and elsewhere). - Container query syntax (covered in
[[frontend-syntax-css-container-queries]]). - Fluid responsive sizing (covered in
[[frontend-impl-responsive-layout-fluid]]). - Animation performance / jank (covered in
[[frontend-errors-animation-jank]]). - Viewport-unit deep dive (covered in
[[frontend-errors-units-rendering-viewport]]).
Hard Rules (Binding)
- NEVER assume
box-sizing: content-boxis acceptable. Apply the universalborder-boxreset at the top of every stylesheet. - NEVER use
1fralone when equal split is required. Alwaysminmax(0, 1fr). - NEVER set a flex / grid child width without considering whether
min-inline-size: 0is needed. Defaultmin-contentfloor breaks shrinking. - NEVER use
100vhfor full-viewport mobile layouts. Use100dvh(orsvh/lvhper intent). - NEVER use
overflow: hiddenon an ancestor of aposition: stickyelement unless the ancestor is explicitly the scrolling container. - NEVER rely on
z-indexto escape a stacking context. Raise the parent's context OR addisolation: isolateto scope. - NEVER ship user-supplied text into a card without
overflow-wrap: anywhere. A single long URL breaks the layout. - NEVER use
grid-auto-flow: densefor content that has reading-order significance. Visual order will diverge from DOM order, breaking screen-reader and keyboard navigation.
Reference Links
references/methods.md: full intrinsic-sizing keywords, stacking-context creator list, viewport-unit table, margin-collapse rulesreferences/examples.md: before / after debugging snippets for flex overflow, sticky inside overflow, viewport-unit mobile demo, grid 1fr fixreferences/anti-patterns.md: 7 anti-patterns with symptom, diagnostic step, root cause, fix- MDN : Mastering margin collapsing (verified 2026-05-19)
- MDN : box-sizing (verified 2026-05-19)
- MDN : CSS flexible box layout
- MDN : CSS grid layout
- MDN : position
Cross-References
[[frontend-syntax-css-grid-subgrid]]: subgrid syntax + implicit-tracks rule[[frontend-syntax-css-container-queries]]: container query unit fallback details[[frontend-impl-responsive-layout-fluid]]: fluid sizing, intrinsic typography[[frontend-errors-units-rendering-viewport]]: viewport-unit deep dive[[frontend-errors-animation-jank]]: performance issues separate from layout pitfalls[[frontend-syntax-css-nesting-logical-properties]]: logical sizing variants