Skip to main content
PProfileMatch

CSS Box Model and Normal Flow: Margin, Padding, Border

Web DevelopmentLesson 2 of 45 min read

What you'll learn

  • Name the four layers of the box model and compute an element's total width
  • Use box-sizing: border-box and understand why it makes sizing predictable
  • Predict margin collapsing between block siblings and when it does not happen
  • Explain how block, inline and inline-block elements behave in normal flow

On this page

  1. Four layers, one box
  2. Margin collapsing: the gap is 30, not 50
  3. box-sizing: border-box makes the numbers honest
  4. Normal flow: where things go before you ask
  5. Common mistakes
  6. How this shows up in real projects and interviews

You set an event ticket card to width: 300px, then add padding and a border, and the card now pokes out of its 300-pixel column by 44 pixels. You did nothing wrong in your own logic. The browser measured differently than you did. Every element on a page is a rectangular box with layers, and "width" can mean different layers depending on one property. Once that is clear, most spacing puzzles dissolve.

Four layers, one box

Layers of one element, from the inside out
+--------------------------------------+
|              margin                  |
|  +--------------------------------+  |
|  |           border               |  |
|  |  +--------------------------+  |  |
|  |  |        padding           |  |  |
|  |  |  +--------------------+  |  |  |
|  |  |  |      content       |  |  |  |
|  |  |  +--------------------+  |  |  |
|  |  +--------------------------+  |  |
|  +--------------------------------+  |
+--------------------------------------+

Content holds the text or children. Padding is breathing room inside the border and shows the background. Border is the visible edge. Margin is empty space outside the border and is always transparent. By default, width and height set the size of the content only, which is called box-sizing: content-box.

ticket.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Event tickets</title>
  <style>
    body   { font-family: system-ui, sans-serif; margin: 0; padding: 16px; background: #f4f1ea; }
    .ticket {
      width: 300px;
      padding: 20px;
      border: 2px solid #1d3557;
      margin-bottom: 30px;
      background: white;
    }
    .ticket + .ticket { margin-top: 20px; }
    .stub { border-style: dashed; }
  </style>
</head>
<body>
  <div class="ticket"><h2>Gate A</h2><p>Row 12, seat 8</p></div>
  <div class="ticket stub"><h2>Gate B</h2><p>Row 3, seat 1</p></div>
</body>
</html>

Two white cards sit on a cream background, near the top-left corner with 16 pixels of space around the page. Each card's content is 300 pixels wide, but its painted box is 300 + 20 + 20 + 2 + 2 = 344 pixels wide, because padding and border are added outside the content. The second card has a dashed border. The cards are stacked vertically because div is a block element, and the gap between them is not 50 pixels, which is the next surprise.

Margin collapsing: the gap is 30, not 50

The first card has margin-bottom: 30px; the second has margin-top: 20px. When the vertical margins of adjacent block siblings touch, they collapse into a single margin equal to the larger one. The gap is therefore 30 pixels. Collapsing only happens vertically, only between block boxes in normal flow, and does not happen between flex or grid items. A margin can also collapse through an empty parent, or between a parent and its first child if no border, padding or content separates them, which is why a child's margin-top sometimes appears to push its parent down instead.

box-sizing: border-box makes the numbers honest

ticket.css (add at the top)
*, *::before, *::after {
  box-sizing: border-box;
}

.ticket {
  width: 300px;      /* now includes padding and border */
  padding: 20px;
  border: 2px solid #1d3557;
}

With border-box, the width you write is the width you get. The card is exactly 300 pixels wide in total, leaving 300 - 44 = 256 pixels for the content. Most teams put this reset at the top of every stylesheet. It also makes width: 100% with padding behave: without it, a 100%-wide box with padding overflows its parent.

Propertycontent-box (default)border-box
width: 300px; padding: 20px; border: 2pxTotal 344px, content 300pxTotal 300px, content 256px
width: 100%; padding: 20pxOverflows parent by 40pxFits parent exactly
height with paddingPadding adds to the heightPadding eats into the set height

Normal flow: where things go before you ask

Normal flow is how the browser lays things out when you apply no positioning or layout properties. Block-level elements (div, p, h2, ul, section) start on a new line and stretch to fill the width available. Inline elements (span, a, strong) sit side by side within a line, wrap like words, and ignore width, height and vertical margin. inline-block is the hybrid: it flows like a word but accepts width, height and vertical spacing.

flow.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Normal flow</title>
  <style>
    * { box-sizing: border-box; }
    .panel { max-width: 40rem; margin: 0 auto; padding: 1rem; border: 1px solid #888; }
    .chip  { display: inline-block; width: 6rem; padding: 0.25rem 0.5rem; background: #cfe8d5; text-align: center; }
    .note  { width: 6rem; height: 3rem; background: #ffd9d9; }   /* a span: width and height ignored */
    .fixed { height: 4rem; overflow: auto; border: 1px dashed #444; }
  </style>
</head>
<body>
  <div class="panel">
    <span class="chip">Ready</span>
    <span class="chip">Boarding</span>
    <span class="chip">Closed</span>
    <p><span class="note">This span ignores its width and height.</span></p>
    <div class="fixed">Line one<br>Line two<br>Line three<br>Line four<br>Line five</div>
  </div>
</body>
</html>

The panel is centred horizontally (margin: 0 auto works because it is a block with a defined max-width), at most 40 rem wide, and shrinks on narrow screens. The three chips sit on one line, each exactly 6 rem wide, separated by a small gap that is actually the whitespace between the tags in the source. The pink note is only as big as its text, because inline elements ignore width and height. The last box is exactly 4 rem tall and shows a scrollbar, because overflow: auto gives content that does not fit a scroll area instead of spilling out.

Common mistakes

  • Setting width and padding without `border-box`. The element overflows its container and the page gains a horizontal scrollbar.
  • Applying `width` to a `span` or `a`. Nothing changes; set display: inline-block or block first.
  • Expecting margins to add. Two adjacent vertical margins of 30px and 20px produce 30px, not 50px. Check the Computed tab's box diagram to see actual values.
  • Centering with `margin: 0 auto` without a width. A block already fills its parent, so there is nothing to centre; add max-width or width.
  • Fixed heights on text containers. Text overflows the box when the font is larger or the language is longer. Prefer min-height or let content decide.
  • Inline gaps surprising you. Whitespace between inline-block elements renders as a space about a character wide; remove it, or switch the parent to flexbox.

How this shows up in real projects and interviews

"Explain the box model" is probably the most-asked CSS question, and "what is margin collapsing?" is the follow-up that separates people who have read about it from people who have been bitten by it. In real code you will spend a lot of time in DevTools hovering the box diagram, because spacing bugs are almost always a padding, margin or box-sizing mismatch. Next, the flexbox lesson changes normal flow for rows and columns of items.

Try it yourself

Build a stack of three notification boxes that are each 320px wide with 16px padding and a 3px left border. Put 24px margin-bottom on each. First use the default box-sizing and measure the total width in DevTools; then apply the border-box reset and measure again. Finally, give the second box margin-top: 40px and work out the gap between boxes one and two before checking it.

Show a hint

DevTools shows the box diagram in the Computed tab. The gap between boxes one and two should be the larger of 24px and 40px, not their sum.

Key takeaways

  • Every element is content, padding, border and margin; width by default sizes only the content.
  • Set box-sizing: border-box globally so the width you write is the width you get.
  • Adjacent vertical block margins collapse to the larger one; spacing with gap avoids this.
  • Block elements fill the line, inline elements flow like words and ignore width and height, and inline-block does both.

Test yourself

A trick from this lesson's ride: Default width is the content only: padding and border are added outside it. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What is the CSS box model?

The box model describes each element as nested layers: content, padding, border and margin. Width and height normally apply to the content box, while padding, border and margin add to the space the element takes up. Understanding it explains most spacing and overflow problems.

What does box-sizing: border-box do?

It makes width and height include padding and border, so a box with width: 300px is 300 pixels wide in total. Without it, padding and border are added on top of the width. Most stylesheets apply it to all elements with a universal selector.

Why are my CSS margins not adding up?

Vertical margins between adjacent block elements collapse, so the larger margin wins instead of the sum. A parent and its first or last child can also collapse margins when nothing separates them. Margins do not collapse horizontally or inside flex and grid containers.