
The CSS Box Model Explained: Content, Padding, Border, and Margin
Every element you put on a web page, from a paragraph to a button to an image, is drawn as a rectangle. The rules that decide how big that rectangle is, how much breathing room it gets, and how it sits next to its neighbors are called the CSS box model. It's one of the first concepts people learn in CSS, and also one of the most common sources of confusion later: widths that come out larger than expected, gaps that appear from nowhere, and margins that seem to vanish.
This guide breaks down each layer of the box, explains how the browser calculates an element's size, and covers the behaviors that trip people up, including box-sizing, margin collapsing, and the difference between block and inline boxes.
The Four Layers of the Box
From the inside out, every box has four areas:
- Content: where text, images, or child elements live. Its size is set by
widthandheight(or determined by the content itself). - Padding: transparent space between the content and the border. It takes on the element's background color.
- Border: a line (or no line) wrapping the padding. Controlled by
border-width,border-style, andborder-color. - Margin: space outside the border that separates the element from its neighbors. It's always transparent.
Here's a simple element that uses all four:
<div class="box">Hello, box model</div>
.box {
width: 300px;
padding: 20px;
border: 5px solid #38bdf8;
margin: 30px;
background: #e0f2fe;
}
The background color fills the content and padding areas and extends under the border. The margin stays transparent, showing whatever is behind the element.
How Width Is Calculated: content-box
Here's the part that surprises beginners. With default settings, the width you set applies only to the content area. Padding and border are added on top.
For the .box above:
| Layer | Left | Content | Right | Total |
|---|---|---|---|---|
| Content | 300px | 300px | ||
| Padding | 20px | 20px | 40px | |
| Border | 5px | 5px | 10px | |
| Rendered width | 350px | |||
| Margin | 30px | 30px | 60px | |
| Space taken up | 410px |
So a box you declared as 300px wide actually renders 350px wide on screen and occupies 410px of horizontal space including margins. This default behavior is called box-sizing: content-box.
It becomes a real problem with percentage widths:
.column {
width: 50%;
padding: 1rem;
border: 1px solid #cbd5e1;
}
Two of these side by side don't fit, because each is 50% plus 2rem of padding plus 2px of border. The second column wraps onto a new line.
The Fix: box-sizing: border-box
With box-sizing: border-box, the width and height you set include the content, padding, and border. The content area shrinks to make room.
.box {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 5px solid #38bdf8;
}
Now the rendered box is exactly 300px wide. The content area is 300 - 40 - 10 = 250px. Margins are still added outside, since they're not part of the box's own size.
Almost every modern project applies border-box globally:
*,
*::before,
*::after {
box-sizing: border-box;
}
An alternative pattern sets it on the root and lets it inherit, which makes it easier for a specific component (like a third-party widget) to opt back into content-box:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
Either is fine. The important thing is to pick one and apply it at the top of your stylesheet, so every width in your project means the same thing.
Content: Sizing the Inner Box
The content area's size can come from explicit properties or from the content itself.
Explicit sizes
width, height, min-width, max-width, min-height, and max-height constrain the box. A common, robust pattern for containers:
.container {
width: 100%;
max-width: 72rem;
margin-inline: auto;
}
Intrinsic sizes
CSS also has keywords that size a box based on its content:
min-content: the narrowest the box can be without overflowing (roughly, the longest word).max-content: the width the content would take if it never wrapped.fit-content: uses the available space, but never more thanmax-contentand never less thanmin-content.
.tag {
width: fit-content;
padding: 0.25rem 0.75rem;
border-radius: 999px;
background: #ede9fe;
}
Logical properties
Instead of width and height, you can use inline-size and block-size, which adapt to the writing mode. In English (horizontal, left-to-right) they map to width and height. In vertical writing modes, they swap. The same logic applies to padding, border, and margin: padding-inline, margin-block-start, border-inline-end, and so on. They're supported in all current major browsers and are a good default for new code.
Padding: Space Inside
Padding adds space between the content and the border. It accepts one to four values, clockwise from the top:
.button {
padding: 0.75rem; /* all sides */
padding: 0.75rem 1.5rem; /* top/bottom, left/right */
padding: 0.5rem 1rem 0.75rem; /* top, left/right, bottom */
padding: 0.5rem 1rem 0.75rem 2rem; /* top, right, bottom, left */
}
A few things to know:
- Padding can't be negative. Margins can.
- Percentage padding is relative to the containing block's inline size (width), even for
padding-topandpadding-bottom. That quirk was the basis of the old "padding hack" for fixed aspect-ratio boxes. Today you should useaspect-ratioinstead:
.video-frame {
width: 100%;
aspect-ratio: 16 / 9;
}
- Padding is part of the clickable area. Increasing padding on links and buttons is the easiest way to make touch targets bigger without changing the visual design much.
Border: The Edge of the Box
The border sits between padding and margin. The shorthand takes width, style, and color:
.card {
border: 1px solid #e2e8f0;
border-radius: 12px;
}
A border with no style won't render. border: 2px #000 shows nothing because border-style defaults to none. It's a classic "why isn't my border showing?" bug.
Outlines and box shadows don't affect layout
If you want a visual edge without changing the element's size, reach for outline or box-shadow. Neither takes up space in the box model:
.input:focus-visible {
outline: 2px solid #38bdf8;
outline-offset: 2px;
}
.card {
box-shadow: 0 0 0 1px #e2e8f0; /* a "border" that doesn't add size */
}
This is particularly useful for hover and focus states. Adding a border on hover changes the element's dimensions and causes the layout to shift; an outline or shadow doesn't.
Margin: Space Outside
Margins push the element away from its neighbors. They use the same one-to-four-value syntax as padding, but they have some unique behaviors.
Negative margins
Margins can be negative, pulling the element toward or over its neighbors:
.full-bleed {
margin-inline: -1rem; /* extend past a parent's 1rem padding */
}
Auto margins
margin: auto distributes available space. On a block with a set width, margin-inline: auto centers it horizontally. In flex and grid layouts, auto margins absorb free space in any direction, which makes them useful for pushing items apart.
Percentage margins
Like padding, percentage margins are always relative to the containing block's width, including top and bottom margins.
Margin Collapsing
This is the behavior that causes the most confusion. In normal block layout, vertical margins between adjacent blocks collapse into a single margin equal to the larger of the two.
<h2 class="heading">Section title</h2>
<p class="lead">Intro paragraph.</p>
.heading {
margin-bottom: 24px;
}
.lead {
margin-top: 16px;
}
You might expect 40px of space between them. You get 24px, the larger of the two. Horizontal margins never collapse.
Parent and child collapsing
Margins can also collapse between a parent and its first or last child, if nothing separates them (no padding, border, or inline content):
<section class="hero">
<h1 class="hero-title">Welcome</h1>
</section>
.hero {
background: #0f172a;
}
.hero-title {
margin-top: 48px;
}
Instead of pushing the heading down inside the dark hero, the 48px margin "escapes" and appears above the hero, pushing the whole section down. The background doesn't extend into that space.
How to prevent collapsing
Any of these stops a parent and child's margins from collapsing:
- Add
paddingorborderto the parent. - Make the parent a flex or grid container (margins never collapse inside flex or grid layouts).
- Make the parent establish a new block formatting context with
display: flow-root.
.hero {
display: flow-root;
background: #0f172a;
}
display: flow-root is the cleanest option when you want normal block layout without collapsing. It's supported in all modern browsers.
A modern approach: spacing with gap
Many teams sidestep collapsing entirely by avoiding vertical margins on components and letting the parent control spacing with gap:
.stack {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
The spacing is predictable, lives in one place, and never collapses.
Block vs. Inline Boxes
The box model applies fully to block boxes, but inline boxes behave differently:
widthandheightare ignored on non-replaced inline elements likespananda.- Horizontal padding, borders, and margins push neighbors apart as you'd expect.
- Vertical padding and borders are drawn, but they don't affect the line height, so they can overlap lines above and below.
- Vertical margins have no effect at all.
.highlight {
padding: 0.5rem;
background: #fef3c7;
}
On an inline span, the yellow background extends vertically, but the text lines don't move apart, which can look broken. If you need the box model to fully apply, use display: inline-block (still flows with text) or display: block.
Replaced elements such as img and video are inline by default but do respect width and height. They also sit on the text baseline, which leaves a small gap underneath images inside containers. display: block or vertical-align: middle removes it.
Inspecting the Box Model in DevTools
Every browser's DevTools has a box model diagram. In Chrome or Edge, select an element, open the Computed tab, and you'll see nested rectangles showing the content size, padding, border, and margin, each labeled with its value. Hovering over an element in the Elements panel highlights the areas in different colors on the page: content in blue, padding in green, margin in orange. It's the fastest way to understand why something is bigger or further away than you expected.
Common Pitfalls
Forgetting box-sizing. If widths are coming out larger than declared, check whether border-box is applied.
Unexpected space from margin collapsing. If a margin appears outside a parent instead of inside, the parent and child margins are collapsing. Use display: flow-root or padding.
Borders that don't render. Always include a border-style.
Layout shift on hover. Adding or thickening a border changes the element's size. Use outline or an inset box-shadow instead, or reserve the space with a transparent border in the default state.
Percentage vertical padding. It's relative to width, not height. Use aspect-ratio for proportional boxes.
Conclusion
The CSS box model is four nested layers: content, padding, border, and margin. By default, width only measures the content, which is why most projects switch to box-sizing: border-box so declared sizes include padding and border. Margins are the odd one out: they can be negative, can be auto, and collapse vertically in normal flow unless something separates them.
Once you can picture those four rectangles around every element, and know how to see them in DevTools, most layout mysteries turn into simple arithmetic. That's the real value of understanding the box model: it makes CSS predictable.


