CSS Layout Mastery for
Backend Developers
Flexbox and Grid are not "tricky CSS" — they are two constraint solvers with a clear API. Once you understand the axes, the alignment model and the sizing algorithm, every layout becomes a two-minute decision instead of a two-hour fight.
01 Why Layout Feels Broken to Backend Developers
You already solved hard layout problems — network topology, database sharding, distributed consensus. CSS layout is easier than all of them. You just haven't been given the right mental model.
CSS layout without the right mental model is like assembling IKEA furniture without the manual — you have all the pieces, you can almost see how they fit, and every time you tighten one bolt another part goes crooked. The manual exists. It's just written in layout-spec language. This article is the translated version.
When you write SELECT … JOIN … WHERE …, you're not telling the database how
to walk the tables — you're describing the result. The query planner decides on hash joins,
index scans, sort orders. CSS layout is the same. You write
display: flex; justify-content: space-between and the browser's layout engine
figures out the exact pixel positions. You're declaring constraints, not issuing commands.
🧠 The three questions layout actually answers
Every layout problem — every single one — is a variation of these three questions:
- Where do the boxes go? (positioning, flow, flex, grid)
- How big are they? (width, height, flex-basis, min/max, intrinsic sizing)
- How do they align with each other? (justify-content, align-items, place-self)
When a layout breaks, it's always because one of these three answers is wrong — or missing. Debug in that order.
🧮 Layout vs Backend — side-by-side
| Concept | Backend world | Layout world | Feels weird because… |
|---|---|---|---|
| Flow | Sequential execution | Document flow (block vs inline) | Elements flow in two directions |
| Sizing | Explicit allocation | Intrinsic + extrinsic sizing | Content can dictate size |
| Alignment | Explicit coordinates | Constraint relationships | Alignment is relative, not absolute |
| Overflow | Buffer errors | Silent clipping or scroll | The browser hides overflow by default |
| Responsiveness | Config per environment | Continuous constraint re-solving | Resizes in real time, no deploy |
02 The Box Model — Every Element is a Rectangle
Before you can lay out boxes, you have to know what a box is. Four layers, always in this order, from inside to outside.
📦 The four layers
- Content — the actual text, image or nested element.
- Padding — space between content and border. Inherits background.
- Border — the visible edge. Can be styled.
- Margin — transparent space outside the border, pushing neighbours away.
The gift is the content. The tissue paper is the padding. The box itself is the border. The empty space between boxes on a shelf is the margin. Now — box-sizing: border-box means "when I say the box is 200px, I mean the box including the tissue paper and walls." That's the size you actually care about.
📐 box-sizing: the single most impactful line in CSS
width: 100px → total 140px
width: 100px → total 100px
/* Content box (default) — padding and border expand the visible size */
.content-box {
box-sizing: content-box;
width: 100px;
padding: 16px;
border: 4px solid;
/* Actual rendered width: 100 + 32 + 8 = 140px */
}
/* Border box — padding and border are INSIDE the declared width */
.border-box {
box-sizing: border-box;
width: 100px;
padding: 16px;
border: 4px solid;
/* Actual rendered width: 100px */
}
/* The universal rule — always at the top of your stylesheet */
#app *,
#app *::before,
#app *::after {
box-sizing: border-box;
}
📏 width, min-width, max-width — the sizing hierarchy
Every element has three width constraints. The browser resolves them in this order:
| Property | Wins when… | Common use |
|---|---|---|
| min-width | Always wins if the calculated width is smaller | Prevent buttons shrinking below tap-target size |
| max-width | Wins if the calculated width exceeds it | Cap content width on ultrawide monitors |
| width | The default preference, subject to min/max | Explicit sizing |
min-width: auto by default — they refuse to shrink
below their content. This is why long text in a flex child breaks layouts. The fix is almost always
min-width: 0 on the flex item.
✅ Do This
- Always apply
box-sizing: border-boxglobally - Use
max-widthinstead ofwidthfor responsive content - Reach for
min-width: 0when flex children overflow - Prefer padding on the parent over margin on each child
❌ Not That
- Mixing
content-boxandborder-boxon the same page - Setting
width: 100%on everything - Using
height: 100vhon mobile (browser chrome causes bugs) - Assuming padding will increase the box size
03 Display — The Layout Mode Switch
display is the most important property in CSS. It decides
which layout algorithm runs on an element's children. Get this right and half your layout
problems vanish.
Think of display like a car's drive mode selector: Eco, Sport, Off-road. Same engine, completely different behaviour. block, inline, flex, grid — each is a different "drive mode" that changes how children are laid out. You are not styling the element; you are choosing which engine evaluates its children.
| Value | What it does | Children behave like | Use when |
|---|---|---|---|
block | Full width, new line before/after | Normal flow | Paragraphs, sections, divs |
inline | Sits in text flow, respects only horizontal padding | Text fragments | Links, spans, emphasis |
inline-block | Inline flow but respects box properties | Small blocks inline | Buttons, badges, chips |
flex | 1D constraint solver — row OR column | Flex items | Navbars, toolbars, card rows |
grid | 2D constraint solver — rows AND columns | Grid items | Page layouts, dashboards, galleries |
none | Removes from flow entirely | — | Hiding elements |
block is like a bare-metal server — takes the whole rack, one job at a time.
inline is like a co-operative micro-thread — fits snugly in a flow.
flex is like a container orchestrator handling one queue of services in a single row.
grid is like a full cluster scheduler arranging pods in rows and columns.
Same concept: you pick the isolation model that matches the workload.
✅ Do This
- Set
display: gridon the parent to lay out children - Use
display: nonefor hidden overlays and menus - Remember:
display: flexon an element makes it both block-level and a flex container - Combine
display: inline-flexfor pill buttons that size to content
❌ Not That
- Using
display: inlineand expecting width/height to work - Toggling
displayfor animations (it can't be transitioned) - Setting
display: flexon every div "just in case" - Forgetting
display: noneremoves elements from the accessibility tree
04 Positioning — The Escape Hatch
Flexbox and Grid solve 95% of layouts. For the other 5% — overlays, tooltips, badges, sticky headers — you need positioning.
📍 The five position values
- static — the default. Element sits in normal flow.
top/left/right/bottomare ignored. - relative — stays in flow, but can be nudged with
top/left. Creates a positioning context for absolute children. - absolute — removed from flow, positioned relative to the nearest positioned ancestor.
- fixed — removed from flow, positioned relative to the viewport. Stays put on scroll.
- sticky — stays in flow until a scroll threshold, then behaves like fixed until its parent leaves view.
static is a paper on a desk. relative is the same paper nudged by a finger. absolute is a sticky note placed anywhere on the page. fixed is a sticky note glued to your monitor — you can scroll all you want, it stays. sticky is a sticky note that only sticks when you scroll past it, then un-sticks at the bottom of its section. Sticky is the conditional one — that's what makes it so useful.
/* Relative parent + absolute child — the classic overlay pattern */
.card {
position: relative; /* creates the coordinate system */
}
.card .badge {
position: absolute;
top: 12px;
right: 12px;
}
/* Sticky header that stays put while scrolling */
.page-header {
position: sticky;
top: 0;
z-index: 100;
background: var(--app-surface);
backdrop-filter: blur(12px);
}
/* Full-screen modal overlay */
.modal-overlay {
position: fixed;
inset: 0; /* shorthand for top:0 right:0 bottom:0 left:0 */
z-index: 9999;
}
/* Tooltip positioned under its trigger */
.tooltip-trigger {
position: relative;
}
.tooltip {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
}
✅ Do This
- Use
position: relativeon the parent before adding absolute children - Use
inset: 0instead of four separate properties - Set
z-indexon the parent, not the child, for stacking - Prefer sticky for headers and sidebars over fixed
❌ Not That
- Absolute positioning for whole page layouts
- z-index: 999999 — it means your stacking is already broken
- Using position: fixed for elements that should scroll
- Forgetting that fixed elements are outside the normal layout flow
05 Flexbox — The 1D Constraint Solver
Flexbox lays out items along a single axis — either a row or a column. It is the right tool for navbars, toolbars, card rows, and anything where you want "N items distributed in a line."
Flexbox is a train. The container is the locomotive, deciding the direction. Each item is a car — some short, some long, some that want to be as long as possible. The main axis is the direction of travel (row or column). The cross axis is the perpendicular. Everything in Flexbox — justify, align, grow, shrink — is about how the cars arrange themselves along and across the track. One track, one direction. That's the whole constraint.
🧭 Main axis vs cross axis — the single concept that unlocks Flexbox
When you set flex-direction: row:
- Main axis runs horizontally (left → right)
- Cross axis runs vertically (top → bottom)
justify-contentcontrols horizontal distributionalign-itemscontrols vertical alignment
When you set flex-direction: column:
- Main axis runs vertically (top → bottom)
- Cross axis runs horizontally (left → right)
justify-contentcontrols vertical distributionalign-itemscontrols horizontal alignment
The properties never change. The axes swap. Remember this and every "why isn't align-items working?" confusion dies.
justify-content is like Array.map with a distribution strategy —
items are placed along one axis with even spacing, edge alignment, or centered grouping.
flex-grow is like spreading leftover memory across processes by weight.
flex-shrink is like a load balancer under pressure, deciding which services
give up resources first.
🔑 The complete Flexbox property reference
| Property | Applied to | What it does |
|---|---|---|
display: flex | Parent | Turns the element into a flex container |
flex-direction | Parent | row | row-reverse | column | column-reverse |
flex-wrap | Parent | nowrap | wrap | wrap-reverse |
justify-content | Parent | Distribute items along the main axis |
align-items | Parent | Align items along the cross axis |
align-content | Parent | Distribute wrapped rows along the cross axis |
gap | Parent | Space between items (row-gap + column-gap) |
flex-grow | Child | How much leftover space this item claims |
flex-shrink | Child | How much this item shrinks under pressure |
flex-basis | Child | The starting size before grow/shrink apply |
align-self | Child | Override the parent's align-items for one item |
order | Child | Change visual order without changing DOM order |
flex: 1 is shorthand for flex: 1 1 0% —
grow 1, shrink 1, basis 0. The basis: 0% is what makes items
distribute space equally rather than by content size. If you want equal-width columns,
that's what you want.
🧮 flex-grow, flex-shrink, flex-basis — in plain English
flex-grow
"How much of the leftover space do I want?" 0 means "I don't want any — keep me at my natural size." 1 means "I'll take a share." Two items with grow 1 and 2 split leftover space 1:2.
flex-shrink
"When space is tight, how much do I give up?" 1 (default) means "shrink me if needed." 0 means "keep me at my basis — I refuse to shrink."
flex-basis
"What's my starting size before grow/shrink kick in?" auto uses the element's content/width. 0 means "ignore my content, start from zero."
The magic trio
flex: 1 = 1 1 0% — equal distribution.
flex: auto = 1 1 auto — grow but respect content.
flex: none = 0 0 auto — fixed size, don't grow or shrink.
06 Interactive Flexbox Playground
Change the properties below and watch the three flex items respond instantly. This is the fastest way to build muscle memory for Flexbox.
.container {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
flex-wrap: nowrap;
}
07 Flexbox Recipes — Copy, Paste, Ship
Seven Flexbox patterns that cover 90% of real-world layout needs. Each is battle-tested and works in every modern browser.
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
}
🎯 The seven essential patterns
Perfect centering
Two lines. Works with any content size. display: flex; justify-content: center; align-items: center.
Navbar (logo left, links right)
justify-content: space-between on the container. Logo and nav become flex items at opposite ends.
Equal-width columns
Apply flex: 1 to each child. They split space equally regardless of content.
Sidebar + content
Fixed sidebar gets flex: 0 0 240px, content gets flex: 1. Sidebar keeps its width, content fills the rest.
Sticky footer (content pushes footer down)
Parent is flex-direction: column; min-height: 100dvh. Content gets flex: 1, footer sits at the bottom.
Card with icon + text
display: flex; gap: 12px on the card. Icon stays fixed, text gets flex: 1 and wraps.
Vertically centered text in a button
display: inline-flex; align-items: center; justify-content: center. No line-height hacks needed.
flex: 1 is almost always what you want.
It reads as "grow to fill, allow shrinking, start from zero."
08 CSS Grid — The 2D Constraint Solver
Grid lays out items in rows AND columns simultaneously. It is the right tool for page layouts, dashboards, image galleries, and any pattern where items need to align in both directions.
Flexbox is one row of cells. Grid is the whole spreadsheet. You define the columns, the rows, and then place items into specific cells. Some cells span two columns. Some span three rows. Grid handles it all. If Flexbox is a train, Grid is a chess board — you decide the board, then you decide where each piece goes.
🧱 Grid's four core concepts
- Tracks — the rows and columns you define with
grid-template-columnsandgrid-template-rows. - Cells — the intersection of a row and a column. Every grid has an implicit cell for every position.
- Lines — the invisible lines between tracks. Line 1 is before the first column, line 2 after it, etc. This is how you place items.
- Areas — named rectangles spanning multiple cells, defined with
grid-template-areas.
/* Simple three-column grid */
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}
/* Responsive grid — auto-fit handles the breakpoints for you */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
}
/* Placing items by line numbers */
.hero {
grid-column: 1 / -1; /* full-width: line 1 to the last line */
grid-row: 1 / 2;
}
/* Named areas — the most readable grid syntax */
.page-layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100dvh;
}
.page-header{grid-area:header}
.page-sidebar{grid-area:sidebar}
.page-main{grid-area:main}
.page-footer{grid-area:footer}
Flexbox is like an auto-incrementing primary key — items find their own order.
Grid is like declaring a PRIMARY KEY (row, col) on a junction table — you know exactly
where every item lands. grid-template-areas is like a migration file: it declares
the entire schema in one readable block, and every item's position is obvious at a glance.
🔑 The fr unit and minmax()
The fractional unit
1fr 2fr 1fr splits available space into 4 equal parts, then assigns 1, 2, 1. It's proportional — not absolute. This is why grids resize gracefully without any media queries.
minmax(min, max)
minmax(260px, 1fr) means "at least 260px, but grow to fill available space." Combined with auto-fit, this gives you a fully responsive grid in one line.
auto-fit vs auto-fill
auto-fit collapses empty tracks so items fill the row. auto-fill keeps empty tracks, so items stay their min size. Use auto-fit for card grids, auto-fill for aligned galleries.
repeat()
repeat(3, 1fr) is shorthand for 1fr 1fr 1fr. repeat(auto-fit, minmax(260px, 1fr)) is the single most useful line in modern CSS layout.
09 Interactive Grid Playground
Change the grid template below and watch six items rearrange in two dimensions instantly.
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: auto;
gap: 10px;
}
10 Grid Recipes — Real-World Layouts
Six complete layout patterns that you will use in almost every project. Each is production-ready and mobile-friendly.
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
}
🎯 The six essential Grid patterns
Responsive card grid
One line: repeat(auto-fit, minmax(260px, 1fr)). Cards flow from 1 → 4 columns as the viewport grows, no media queries.
Holy grail layout
Header, footer, two sidebars and main content using grid-template-areas. The named-areas syntax is what makes it readable.
Dashboard with sidebar
grid-template-columns: 260px 1fr. Sidebar is fixed, main content fills the rest and scrolls independently.
Magazine / masonry-style
Different items span different numbers of rows or columns using grid-column: span 2. Featured items get more space.
Centered content with max-width
display: grid; place-items: center; min-height: 100dvh. The modern replacement for the old margin: auto hack.
Full-bleed layout
Break out of a centered container using grid-template-columns: 1fr min(65ch, 100%) 1fr. Text stays readable while images go edge-to-edge.
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr))This one line replaces 4 media queries, works at every viewport width, and never breaks. Learn it, remember it, use it everywhere.
11 Flexbox vs Grid — The Decision Tree
The single most common question. The answer is simpler than you think: Flexbox is 1D, Grid is 2D.
🔄 Use Flexbox when…
- Items are laid out in a single line (row or column)
- You want items to size based on their content
- The layout should adapt to a variable number of items
- You're building: navbars, toolbars, card rows, form rows, button groups
- The spacing depends on the items themselves
🔲 Use Grid when…
- Items need to align in rows AND columns
- You want to explicitly define the layout structure
- Some items span multiple cells (featured cards, full-width headers)
- You're building: page layouts, dashboards, galleries, admin panels
- The layout structure matters more than the content size
Ask yourself: "Is this a row of things, or a grid of things?"
A row of tags → Flexbox. A dashboard of widgets → Grid.
A nav with logo + links → Flexbox. A product page with header, gallery, description, reviews → Grid.
Still unsure? Default to Flexbox for 1D, Grid for 2D. Never fight the tool.
🤝 The pro move: combine them
The best layouts use both. A common architecture:
- Grid for the page skeleton — header, sidebar, main, footer with named areas
- Flexbox inside each region — navbars, toolbars, card layouts, form rows
Grid gives you the map. Flexbox gives you the details. They are not competitors — they are a team.
/* Grid for the page skeleton */
.page {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100dvh;
}
/* Flexbox inside the header */
.page-header {
grid-area: header;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
}
/* Flexbox inside the nav */
.page-header nav {
display: flex;
gap: 20px;
}
/* Grid inside the main content — cards */
.page-main {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
padding: 24px;
}
✅ Do This
- Reach for Flexbox first — it's simpler for most UI
- Reach for Grid when you need 2D alignment
- Combine them at different nesting levels
- Remember that Grid items can be Flex containers
❌ Not That
- Force Grid into a 1D problem (overkill)
- Force Flexbox into a 2D problem (frustration)
- Mix both on the same element
- Assume one is "better" than the other
12 Alignment — The Three-Letter Confusion
align-items, align-content,
align-self. Nearly identical names, completely different jobs.
Here's the cheat code.
| Property | Applied to | What it aligns | Axis |
|---|---|---|---|
justify-content | Container | Items along the main axis | Main |
justify-items | Container (Grid) | Content inside each cell, horizontally | Inline |
justify-self | Item (Grid) | One item's content, horizontally | Inline |
align-items | Container | Items along the cross axis | Cross |
align-content | Container | Wrapped rows/columns (multi-line only) | Cross |
align-self | Item | One item's alignment, overriding align-items | Cross |
place-items | Container | Shorthand for align-items + justify-items | Both |
place-content | Container | Shorthand for align-content + justify-content | Both |
place-self | Item | Shorthand for align-self + justify-self | Both |
justify-content = where the desks are placed in the room (main axis).
align-items = how tall each student sits in their chair (cross axis).
align-self = one student decides to stand up.
align-content = how multiple rows of desks are spaced in the room — only matters when there's more than one row.
flex-wrap: wrap and give the container a height, then
align-content starts working.
🎯 The "I just want to center this thing" master recipe
/* The one-liner — works for any content size */
.center-me {
display: grid;
place-items: center;
min-height: 100dvh;
}
/* Flexbox equivalent — same result */
.center-me-flex {
display: flex;
justify-content: center;
align-items: center;
min-height: 100dvh;
}
/* The old absolute + transform trick — still useful for modals */
.center-me-absolute {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
13 Gap vs Margin — The Modern Spacing Rule
Before gap worked in Flexbox, spacing was a nightmare of
:last-child { margin-right: 0 } hacks. Not any more.
margin is putting a single armrest on each seat — you have to remember to remove the outermost ones. gap is spacing out the rows of seats architecturally — the first and last seat flush against the wall, only the spaces between seats counted. Which one is easier to reason about? That's why gap wins.
| Approach | Adds space on outer edges? | Needs :last-child hack? | When to use |
|---|---|---|---|
gap | No | No | Always inside Flex/Grid containers |
margin-right on children | Yes (on last child) | Yes | Legacy code |
margin negative on container | No | No | Only in specific pre-gap layouts |
margin between unrelated blocks | N/A | N/A | Vertical rhythm between sections |
/* ❌ The old way — requires a :last-child override */
.toolbar .button {
margin-right: 12px;
}
.toolbar .button:last-child {
margin-right: 0;
}
/* ✅ The modern way — gap only adds space BETWEEN items */
.toolbar {
display: flex;
gap: 12px;
}
/* Different horizontal and vertical gaps */
.card-grid {
display: grid;
column-gap: 24px;
row-gap: 16px;
}
/* gap works inside flex too — a modern capability */
.nav-links {
display: flex;
gap: 20px;
flex-wrap: wrap;
row-gap: 8px;
}
✅ Do This
- Use
gapfor all spacing between flex/grid siblings - Use
row-gapandcolumn-gapfor asymmetric spacing - Use margins for vertical rhythm between unrelated page sections
- Set gap values from your spacing scale tokens
❌ Not That
:last-child { margin-right: 0 }hacks- Using
marginon flex children for internal spacing - Mixing gap and margin for the same purpose
- Using negative margins to "fix" gap issues
14 Container Queries — Component-Level Responsiveness
Media queries respond to the viewport. Container queries respond to the size of the parent. This is a fundamental shift — components that truly adapt to their context.
Media queries are like reading NODE_ENV — the whole process reacts to one global.
Container queries are like reading a request header — each handler adapts to its own
incoming context. The same component can render differently in a sidebar vs a hero section,
without any global state changing.
📦 How it works
- Mark a parent as a container:
container-type: inline-size - Optionally name it:
container-name: card - Use
@containerto style children based on that container's size
/* Step 1 — declare the container */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Step 2 — base card (stacked layout) */
.card {
display: flex;
flex-direction: column;
gap: 12px;
}
/* Step 3 — when the PARENT is wide enough, go horizontal */
@container card (min-width: 420px) {
.card {
flex-direction: row;
gap: 20px;
align-items: center;
}
}
/* The same card in a narrow sidebar vs a wide hero renders differently — automatically */
vw is a percentage of the viewport, cqi
is a percentage of the container's inline size. Combine with clamp()
for typography that scales based on the component's container, not the page.
font-size: clamp(1rem, 3cqi, 1.5rem) — that's component-aware fluid type.
✅ Do This
- Mark reusable card/grid/list components as containers
- Name containers when you have nested containers
- Use
container-type: inline-sizefor horizontal responsiveness - Combine with
clamp()and container units for real fluid behaviour
❌ Not That
- Replacing all media queries with container queries
- Forgetting
container-type— nothing will work without it - Using container queries for page-level layout (media queries still own that)
- Nesting unnamed containers — always name them
15 Common Layout Bugs & How to Fix Them
Every one of these has cost a developer an afternoon. Here's the exact cause and fix.
🐛 Bug 1 — Flex child overflows instead of shrinking
Symptom: A flex item refuses to shrink below its content size. Long text breaks out of the layout.
Cause: Flex items default to min-width: auto, which means "don't shrink below content size."
.flex-child {
min-width: 0; /* the one-line fix */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
🐛 Bug 2 — 100vh causes scrollbars or cuts off content on mobile
Symptom: On mobile, min-height: 100vh extends under the browser's address bar or causes unwanted scroll.
Cause: vh doesn't account for dynamic browser chrome.
Fix: Use the modern dvh (dynamic viewport height) unit.
.hero {
min-height: 100dvh; /* dynamic viewport height — always correct */
}
/* Fallback for older browsers */
.hero {
min-height: 100vh;
min-height: 100dvh;
}
🐛 Bug 3 — Absolutely positioned element anchored to the wrong parent
Symptom: An absolute child positions itself relative to the page instead of its parent.
Cause: No ancestor has position: relative — so the nearest positioned ancestor is the viewport.
Fix: Add position: relative to the intended parent.
🐛 Bug 4 — Inline-block whitespace gaps
Symptom: Two inline-blocks sit apart with unexplained whitespace between them.
Cause: Whitespace in the HTML source is rendered as an actual text space between inline elements.
Fix: Use Flexbox (best), or remove the whitespace in HTML, or set font-size: 0 on the parent and reset it on children.
🐛 Bug 5 — Grid item doesn't span as expected
Symptom: grid-column: 1 / 3 spans more or fewer columns than expected.
Cause: Grid lines are 1-indexed and include a line at the end of the grid. 1 / 3 spans columns 1 and 2 (the line after column 2 is line 3).
Fix: Use grid-column: 1 / -1 to span the entire width — it's clearer than counting lines.
🐛 Bug 6 — z-index doesn't work
Symptom: You set z-index: 9999 and the element still sits behind something.
Cause: z-index only works on elements with a position other than static. Also, any ancestor with a transform, filter or opacity creates a new stacking context that traps z-index.
Fix: Give the element position: relative (or absolute/fixed). Check ancestors for transform or filter.
🐛 Bug 7 — Fixed header covers content
Symptom: A fixed or sticky header sits on top of the first bit of content.
Fix: Add padding-top equal to the header height on the main content, or use scroll-padding-top on the html element so anchor links account for it.
html {
scroll-padding-top: 80px; /* anchors respect the fixed header */
}
.main-content {
padding-top: 80px;
}
🐛 Bug 8 — Overflow scroll on the whole page
Symptom: A stray wide element causes horizontal scroll on the entire page.
Fix: Find the culprit with DevTools (look for a red or orange overlay). Then either fix the element's width, or add overflow-x: clip on the root wrapper.
Do NOT just slap overflow-x: hidden on the body — it breaks sticky positioning in many browsers. Use overflow-x: clip instead, which doesn't.
16 Cheat Sheet — Everything on One Screen
Bookmark this. It's the reference card you'll come back to in every project.
Perfect centering
display: grid; place-items: center;
/* or */ display: flex; justify-content: center; align-items: center;
Responsive card grid
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
Navbar (logo + links)
display: flex;
align-items: center;
justify-content: space-between;
Sidebar + content
display: grid;
grid-template-columns: 240px 1fr;
min-height: 100dvh;
Sticky footer
display: flex; flex-direction: column;
min-height: 100dvh;
.main { flex: 1; }
Fixed-width sidebar
.sidebar { flex: 0 0 240px; }
.main { flex: 1; min-width: 0; }
Equal-width columns
display: flex;
.col { flex: 1; }
Wrap + gap on a tag list
display: flex; flex-wrap: wrap;
gap: 8px 12px;
Full-width hero in a grid
grid-column: 1 / -1;
Named page layout
grid-template-areas: "header header" "sidebar main";
Sticky header
position: sticky; top: 0; z-index: 100;
Full-screen modal overlay
position: fixed; inset: 0; z-index: 9999;
Badge overlay on card
.card { position: relative; }
.badge { position: absolute; top: 12px; right: 12px; }
Truncate text in flex child
min-width: 0;
overflow: hidden; text-overflow: ellipsis;
Custom scroll container
overflow-y: auto;
max-height: 60dvh;
Mobile-safe height
min-height: 100dvh;
🔑 Flexbox quick reference
| Goal | Container | Item |
|---|---|---|
| Center one item | justify-content: center; align-items: center | — |
| Push last item right | justify-content: space-between | or margin-left: auto |
| Equal columns | — | flex: 1 |
| Fixed sidebar | — | flex: 0 0 240px |
| Allow text truncation | — | min-width: 0 |
| Wrap onto multiple lines | flex-wrap: wrap | — |
🔲 Grid quick reference
| Goal | Snippet |
|---|---|
| Auto-responsive columns | repeat(auto-fit, minmax(260px, 1fr)) |
| Span all columns | grid-column: 1 / -1 |
| Center all grid items | place-items: center |
| Named layout | grid-template-areas: "header header" "sidebar main" |
| Equal column widths | repeat(3, 1fr) |
| Different horizontal + vertical gaps | column-gap: 24px; row-gap: 16px |
17 Test Yourself
Fifteen questions. Instant feedback. Explanations for every answer.
🎯 Part 2 Knowledge Check
18 Frequently Asked Questions
Layout questions backend developers ask most often.
What is the difference between Flexbox and CSS Grid?
How do I center a div in CSS?
display: grid; place-items: center on the parent. That is two lines, works in every browser, and handles any child size. The older Flexbox equivalent is display: flex; justify-content: center; align-items: center.What does position: sticky do?
When should I use gap instead of margin?
:last-child { margin-right: 0 } hack. Use margin only for spacing between unrelated elements or for vertical rhythm between page sections.What are container queries?
container-type: inline-size on a parent, then use @container (min-width: 400px) to style children. This makes truly reusable components possible.Why does my flex item not shrink below its content size?
min-width: auto, which means they refuse to shrink smaller than their content. Set min-width: 0 on the flex item to allow it to shrink and let text-overflow ellipsis work correctly.What is the difference between align-items and align-content?
align-items aligns items within a single line of a flex/grid container. align-content distributes multiple lines (from flex-wrap or grid rows) along the cross axis. On a single-line flex container, align-content does nothing.Should I use media queries or container queries?
What is the fr unit?
fr is a fractional unit unique to CSS Grid. It splits available space proportionally. 1fr 2fr 1fr gives three columns where the middle gets twice the space of each side. It's the reason Grid layouts resize so gracefully without media queries.What's the best way to make a responsive card grid?
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)). The grid automatically fits as many 260px-minimum columns as possible, and expands them to fill the remaining space. No media queries needed.Why does 100vh cause issues on mobile?
100vh was calculated before this dynamic change, so content either gets cut off or causes unwanted scroll. Use 100dvh (dynamic viewport height) which updates in real time.What comes next in this series?
🗺️ The Full 5-Part Roadmap
Design Tokens & Architecture
Custom properties, theming, namespacing, cascade layers, modern reset, fluid type.
Layout Mastery
Box model, display, positioning, Flexbox, Grid, alignment, container queries and real-world recipes.
Visual Polish
Gradients, elevation systems, glassmorphism, depth, colour theory in code.
Animation & Motion
Transitions, keyframes, scroll-driven animation, and accessibility-safe motion.
Responsive & Performance
Container queries, responsive strategy, rendering performance, and shipping a real component library.
🔗 Continue Learning on FreeLearning365
Free tools, guides and learning paths that pair well with this series.

0 Comments
thanks for your comments!