Every Element Is a Box, Whatever It Looks Like
A heading, a paragraph, an image, a button, an entire page section — as far as the browser's layout engine is concerned, all of them are rectangles. Understanding how one rectangle is measured is the difference between nudging numbers until something looks right and actually knowing why it moved.
Each box is built from four layers, working outward. The content is the text or image itself. Padding is space added inside the box, between the content and the edge, and it takes the element's background colour with it. The border draws a line at that edge. Margin is space outside the border, pushing neighbouring elements away, and it is always transparent — a margin never shows a background.
That distinction between padding and margin is the one to fix in your head early, because it decides how things look. On a card, padding is the breathing room between the card's text and the card's own edge; margin is the gap between this card and the next one. Use padding when you want a coloured box to be bigger and roomier, and margin when you want two things to sit further apart.
/* Reading a box from the inside out */
.card {
/* content */
width: 320px;
/* padding — inside the border, painted with the background */
padding: 24px;
/* border — the line at the edge */
border: 2px solid #e3e3ea;
/* margin — space outside, always transparent */
margin: 16px;
background: #ffffff;
} - Content — the text, image or child elements
- Padding — space inside the border; takes the element's background
- Border — the visible line between padding and margin
- Margin — space outside the border; always transparent
- Padding makes a box roomier; margin pushes other boxes away
Why width: 300px Does Not Give You a 300px Box
By default, the width you set applies to the content area only. Padding and border are then added on top. So a box with width: 300px, padding: 20px and a 5px border actually occupies 350 pixels across: 300 for the content, 20 of padding on each side, and 5 of border on each side. This behaviour has a name — box-sizing: content-box — and it is what every browser does unless you say otherwise.
Nobody thinks this way in practice. When you say a sidebar is 300 pixels wide, you mean the whole visible thing is 300 pixels, not that it becomes 350 the moment you add comfortable padding. The default forces you to keep doing arithmetic in your head, and it breaks the instant you use percentages.
That percentage case is where it really bites. Two columns at width: 50% should sit side by side and fill the row exactly. Add padding: 20px to each and they now measure 50% plus 40 pixels each, the pair no longer fits, and the second column drops onto a new line. The layout looks broken, the CSS looks correct, and the cause is invisible unless you know where to look.
/* The default: box-sizing: content-box */
.sidebar {
width: 300px;
padding: 20px;
border: 5px solid #ccc;
}
/* Actual space taken on screen:
300 + 20 + 20 + 5 + 5 = 350px */
/* The percentage version of the same problem */
.column {
width: 50%;
padding: 20px; /* now each column is 50% + 40px */
float: left; /* the second one wraps to a new line */
} - Open developer tools and look at the box-model diagram at the bottom of the Computed panel. It shows the content size, padding, border and margin of the selected element as concentric rectangles with real numbers in them. It answers "why is this 350 pixels wide" faster than any amount of reading.
box-sizing: border-box, the Line Almost Everyone Writes
Setting box-sizing: border-box changes what width means: it becomes the width of the whole visible box, with padding and border measured inside that figure instead of added to it. A box with width: 300px, padding: 20px and a 5px border is now exactly 300 pixels wide, and the content area shrinks to 250 to make room. The same rule applies to height.
This is what people actually mean when they set a width, so the standard practice is to switch it on for the entire page in the first few lines of the stylesheet. The reset below covers every element and both generated-content pseudo-elements, so nothing is left behaving the old way. Write it once, at the top of the file, and stop doing arithmetic forever.
Now revisit the broken two-column layout: with border-box, two columns at 50% with 20 pixels of padding each still add up to exactly 100%, and they sit side by side. Nothing else changed. In the demo below, both boxes are given identical widths, padding and borders — the only difference between them is the box-sizing value. Change the padding on both and watch one box grow while the other holds its size.
/* Put this at the top of every stylesheet you write */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Now the number means what you expect */
.sidebar {
width: 300px;
padding: 20px;
border: 5px solid #ccc;
}
/* Total width on screen: 300px.
Content area: 300 - 40 - 10 = 250px. */
/* And percentages behave */
.column {
width: 50%;
padding: 20px; /* still 50% in total — the columns fit */
} HTML
<p class="label">Both boxes below ask for width: 260px, padding: 20px, border: 5px.</p>
<div class="box content-box">box-sizing: content-box<br>(the browser default)</div>
<div class="ruler"></div>
<div class="box border-box">box-sizing: border-box<br>(what you usually want)</div>
<div class="ruler"></div>
<p class="label">The grey ruler is exactly 260px wide. Compare each box against it, then change the padding on <code>.box</code> and look again.</p> CSS
body {
font-family: system-ui, Arial, sans-serif;
padding: 24px;
background: #f4f4f7;
}
.label {
color: #555555;
font-size: 14px;
max-width: 420px;
}
.box {
width: 260px;
padding: 20px;
border: 5px solid #d1039e;
background: #ffffff;
margin-bottom: 4px;
line-height: 1.4;
}
.content-box {
box-sizing: content-box; /* real width: 260 + 40 + 10 = 310px */
}
.border-box {
box-sizing: border-box; /* real width: 260px exactly */
}
.ruler {
width: 260px;
height: 8px;
background: #999999;
margin-bottom: 24px;
} box-sizingis not inherited, which is why the reset uses the universal selector instead of setting it onbodyand hoping it spreads.
What border-box Does Not Cover
border-box includes padding and border. It does not include margin, and it never will — margin is space between boxes, not part of the box. An element with width: 100% and margin: 0 20px is still 40 pixels too wide for its parent and will overflow, whatever the box-sizing. The usual fixes are to drop the width and let a block element fill the space naturally, or to move the spacing to padding on the parent, or to use width: calc(100% - 40px) when you genuinely need both.
It is also worth knowing what happens when you ask for the impossible. With border-box, if padding and border together exceed the width you declared, the content area cannot go below zero — the box simply ends up as wide as its padding and border require, and your declared width is quietly ignored. A 40-pixel-wide box with 30 pixels of padding on each side will be 60 pixels wide no matter what you write.
Two more properties belong to this picture. outline draws a line like a border but takes up no space at all — it is painted over the surroundings — which makes it perfect for focus rings, because adding one never shifts the layout. And overflow decides what happens when the content is genuinely bigger than the box you gave it: keep the default visible and it spills out, set hidden and it is clipped, set auto and the browser adds a scrollbar only when one is needed.
/* Margin is never inside the width */
.bad { width: 100%; margin: 0 20px; } /* overflows by 40px */
.good { margin: 0 20px; } /* block fills what is left */
.also { width: calc(100% - 40px); margin: 0 20px; } /* explicit and correct */
/* Padding can win over width */
.tiny { box-sizing: border-box; width: 40px; padding: 30px; }
/* Rendered 60px wide — the content area cannot be negative. */
/* outline takes no space, so focus rings never shift the layout */
.button:focus-visible {
outline: 3px solid #d1039e;
outline-offset: 2px;
}
/* Deciding what happens to content that does not fit */
.scroll-area { height: 200px; overflow: auto; }
.clipped { height: 200px; overflow: hidden; } box-sizing: border-boxcovers content, padding and border — never marginmin-widthandmax-widthoverridewidth, which is how you cap an element without fixing its sizemax-width: 100%on images stops them bursting out of narrow containersoutlineis drawn outside the border and occupies no space in the layoutoverflow: autoshows a scrollbar only when the content actually needs one- Never remove a focus outline without replacing it — keyboard users rely on it to see where they are
