CSS layout is one of the most fundamental skills in frontend development, and it's where a lot of developers either click into confidence or quietly struggle for years.
The good news: CSS layout has three layers you can learn in order. Master each one, and you have everything you need to build real interfaces.
https://www.youtube.com/watch?v=rIO5326FgPE
Every element on a web page — a paragraph, a button, a navigation bar — is a rectangular box. The box model describes the four zones that make up that rectangle. Understanding it will explain why things are the size they are, why elements don't sit where you expect, and how to fix it when they don't.
┌─────────────────────────────────┐
│ MARGIN │ (space outside the element)
│ ┌───────────────────────────┐ │
│ │ BORDER │ │ (the visible edge)
│ │ ┌─────────────────────┐ │ │
│ │ │ PADDING │ │ │
│ │ │ ┌───────────────┐ │ │ │
│ │ │ │ CONTENT │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
.card {
width: 300px;
padding: 24px; /* space inside the box */
border: 2px solid #e2e8f0;
margin: 16px; /* space outside the box */
}
Here's something that trips up almost everyone. By default, CSS adds padding and border on top of the width you set. So this card is not 300px wide — it's 348px:
/* box-sizing: content-box (the default) */
.card {
width: 300px;
padding: 24px; /* adds 48px (24 × 2) */
border: 2px solid #ccc; /* adds 4px (2 × 2) */
/* actual width: 300 + 48 + 4 = 352px */
}
This is almost never what you want. The fix is box-sizing: border-box, which makes the width include padding and border:
/* box-sizing: border-box */
.card {
box-sizing: border-box;
width: 300px;
padding: 24px;
border: 2px solid #ccc;
/* actual width: exactly 300px. padding lives inside. */
}
The universal best practice is to set this globally so you never have to think about it:
/* Put this at the top of every CSS file you write */
*,
*::before,
*::after {
box-sizing: border-box;
}
<aside> ⚠️
Warning
If you forget box-sizing: border-box and wonder why your carefully-sized elements are overflowing their containers, this is almost certainly the cause. Make the global reset a habit from day one.
</aside>
Both margin and padding accept 1–4 values using the same shorthand pattern:
/* 1 value: all four sides */
margin: 16px;
/* 2 values: top/bottom, left/right */
margin: 8px 16px;
/* 4 values: top, right, bottom, left (clockwise from top) */
margin: 8px 16px 24px 16px;
You can also target individual sides:
.section-title {
margin-top: 32px;
margin-bottom: 8px;
padding-left: 16px;
}
Adjacent vertical margins collapse into one (the larger of the two wins). This only happens with vertical margins between block elements, not with padding.
.paragraph-a { margin-bottom: 24px; }
.paragraph-b { margin-top: 16px; }
/* The gap between them is 24px, not 40px */
<aside> 💡
Info
Margin collapse is intentional: it's what makes body text readable without having to carefully balance every margin. But it can be surprising when margins inside a container "escape" to the outside. If you hit this, adding padding: 1px or overflow: hidden to the parent will prevent it.
</aside>
<aside> ⌨️
Hands On
Open the browser DevTools (F12) and inspect any element on a webpage. In the Elements panel, scroll to the bottom of the Styles pane. You'll see a visual diagram of the box model showing the exact computed values for margin, border, padding, and content. Try hovering over each zone in the diagram and watch the page highlight that region.
</aside>
https://www.youtube.com/watch?v=K74l26pE4YA
Flexbox (Flexible Box Layout) is a CSS layout mode designed for arranging items in a single row or column. It gives you fine control over how items grow, shrink, align, and distribute space — without the float hacks and clearfix workarounds that predated it.
The core idea: you turn a parent element into a flex container, and its direct children automatically become flex items that respond to your layout rules.
.nav {
display: flex; /* that's it — children are now flex items */
}
These properties go on the parent (the flex container):
flex-direction — which axis items flow along
.container {
display: flex;
flex-direction: row; /* default: left → right */
flex-direction: column; /* top → bottom */
flex-direction: row-reverse; /* right → left */
}
justify-content — alignment along the main axis (direction of flow)
.container {
display: flex;
justify-content: flex-start; /* default: pack items at start */
justify-content: flex-end; /* pack items at end */
justify-content: center; /* center items */
justify-content: space-between; /* first and last at edges, even gaps */
justify-content: space-around; /* equal space around each item */
justify-content: space-evenly; /* equal space between all items */
}
align-items — alignment along the cross axis (perpendicular to flow)
.container {
display: flex;
align-items: stretch; /* default: items fill cross-axis height */
align-items: flex-start; /* align to start of cross axis */
align-items: flex-end; /* align to end of cross axis */
align-items: center; /* center on cross axis */
}
gap — consistent spacing between items (replaces margin hacks)
.container {
display: flex;
gap: 16px; /* equal gap in all directions */
gap: 8px 16px; /* row gap, column gap */
}
flex-wrap — whether items can wrap to a new line
.card-grid {
display: flex;
flex-wrap: wrap; /* items wrap instead of shrinking into oblivion */
}
These properties go on the children (flex items):
flex — shorthand for how an item grows, shrinks, and what its base size is
/* flex: grow shrink basis */
.item { flex: 1; } /* shorthand for flex: 1 1 0% — grow equally */
.item { flex: 0 0 200px; } /* fixed 200px, no growing or shrinking */
.item { flex: 2; } /* grows at twice the rate of flex: 1 items */
align-self — overrides align-items for a single item
.item-special {
align-self: flex-end; /* just this item aligns differently */
}
Centered content (vertically and horizontally)
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Navigation bar
.navbar {
display: flex;
justify-content: space-between; /* logo left, links right */
align-items: center;
padding: 0 24px;
height: 64px;
}
.navbar__links {
display: flex;
gap: 24px; /* consistent spacing between links */
}
Card row with equal-width cards
.card-row {
display: flex;
gap: 16px;
}
.card {
flex: 1; /* each card takes up equal space */
padding: 24px;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
Sidebar layout
.layout {
display: flex;
gap: 24px;
}
.sidebar {
flex: 0 0 240px; /* fixed 240px, never grows or shrinks */
}
.main-content {
flex: 1; /* takes all remaining space */
}
<aside> 💡
Info
When items have flex-direction: row (the default), justify-content controls horizontal alignment and align-items controls vertical alignment. When you switch to flex-direction: column, those axes flip. Keep this in mind when the alignment properties aren't doing what you expect.
</aside>
<aside> ⌨️
Hands On
Create an index.html file with this starter:
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
Style .container with display: flex and experiment with justify-content values: flex-start, center, space-between, space-evenly. Then try adding flex-direction: column and see how the same properties behave differently.
</aside>
One of flexbox's most useful patterns — items that sit in a row on wide screens and wrap gracefully on smaller ones:
.projects {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.project-card {
flex: 1 1 280px; /* grow, shrink, but never below 280px */
padding: 24px;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
Each card has a minimum width of 280px. When the container gets narrow, cards wrap to the next row automatically — no media queries required.
<aside> 🎉
</aside>
https://www.youtube.com/watch?v=uuOXPWCh-6o
CSS Grid is a two-dimensional layout system. Where flexbox handles one direction at a time (row or column), Grid lets you define rows and columns simultaneously and place elements precisely within that structure.