Back to Blog
Frontend13 min readJun 2026

CSS Fundamentals: The Box Model & Layout

The mental models that make CSS stop fighting you, the box model, the cascade, normal flow, and modern layout with Flexbox and Grid (and when to reach for which).

FrontendCSSLayoutFlexbox
SB

Sri Balaji

Founder · TheSimplifiedTech

On this page

Why won't this div center?

Every developer has lived this moment. You have a box. You want it in the middle of the screen. You try margin: auto. Nothing. You try text-align: center. The text moves, the box doesn't. You add padding, and now the box is *bigger* instead of *centered*. Twenty minutes later you are googling "how to center a div" for the fortieth time in your career, half-convinced CSS is broken.

CSS is not broken. It is doing *exactly* what you told it to, you just don't yet have the mental model for what "it" is. Once you internalize four ideas, the box model, the cascade, normal flow, and modern layout (Flexbox and Grid), CSS stops feeling like a slot machine and starts feeling like a language. This article installs those four models.

Who this is for

Anyone who can write a little HTML and CSS but feels like layout "just happens to" them rather than something they control. If you've ever pasted a `display: flex` from Stack Overflow without knowing why it worked, this is for you. No build tools, no framework, just CSS and a browser.

Every element is a box

In CSS, everything is a rectangular box, and layout is just the art of sizing those boxes and arranging them relative to each other.
The one sentence that unlocks CSS

That <h1>, that <p>, that <button>, even an inline <span>, the browser draws each one as a box. And each box is built in layers, like a parcel ready for shipping. Picture mailing a fragile mug:

The mug itselfcontent, your text or image
Bubble wrap snug around the mugpadding, space inside the box, around the content
The cardboard box wallsborder, the visible edge of the box
Empty space between this parcel and the next on the shelfmargin, space outside the box, pushing neighbours away
Content out, padding hugs it, border wraps it, margin keeps the neighbours at arm's length.

Padding is *inside* the box and shares its background colour. Margin is *outside* and is always transparent. Get those two confused and you will spend hours wondering why a background colour bleeds where you didn't expect it, almost always a padding-vs-margin mix-up.

The box model, layer by layer

wrapped bywrapped bywrapped by
Content

text / image

Padding

inside spacing

Border

visible edge

Margin

outside spacing

The four nested layers of every CSS box, from the content out to the margin.

Read it from the inside out. To find how much horizontal room an element *actually* takes on screen by default, you add it all up. Here is the walkthrough for a box declared width: 200px:

  1. 1

    Start with the content width

    You wrote `width: 200px`. By default that 200px applies to the content box only, the mug, not the packaging.

  2. 2

    Add padding to both sides

    `padding: 20px` adds 20px left + 20px right = 40px. The box is now 240px wide on screen.

  3. 3

    Add the border to both sides

    `border: 5px solid` adds 5px left + 5px right = 10px. Now 250px.

  4. 4

    Margin pushes neighbours, not the box

    `margin: 10px` doesn't change the box's own width, it reserves 10px of empty space *outside* it. The rendered box stays 250px; its neighbours just keep their distance.

  5. 5

    Feel the surprise

    You asked for 200px and got a 250px box. This off-by-padding-and-border math is the #1 reason CSS layouts overflow, and `box-sizing` (next section) makes it vanish.

Normal flow: what happens before you do anything

Before any flex or grid, the browser already lays elements out using normal flow. The two rules worth memorizing: block elements (<div>, <p>, <h1>) stack vertically and take the full available width. Inline elements (<span>, <a>, <strong>) sit side by side on a line and only take as much width as their content.

This is why width on a <span> seems to do nothing (inline boxes ignore it) and why two <div>s never sit beside each other on their own (block boxes claim the whole row). For years, developers fought this with float to force side-by-side layout. Don't, floats were designed for wrapping text around images, not for page layout. Flexbox and Grid are the modern, purpose-built tools.

Flexbox vs Grid: one dimension or two

Both Flexbox and Grid are layout *systems* you switch a container into. The single question that tells you which to use: am I laying things out along one line, or in a grid of rows and columns?

FlexboxGrid
DimensionsOne at a time (a row OR a column)Two at once (rows AND columns)
Mental modelDistribute items along a single axisPlace items into a defined grid of cells
Sizing driven byMostly the content itselfMostly the container's defined tracks
Best forNavbars, toolbars, button rows, centering one thing, card internalsPage layouts, image galleries, dashboards, anything with aligned rows + columns
Turn it on`display: flex` on the parent`display: grid` on the parent
Rule of thumbContent arranged in a line → FlexboxLayout with a defined structure → Grid
Reach for Flexbox for content arranged in a line; reach for Grid for a true 2D structure.

They are friends, not rivals

Real pages use both: Grid for the overall page skeleton (header / sidebar / main / footer), Flexbox *inside* each region to line up its contents. You don't pick one for the whole app, you pick the right one per container.

Build it: a responsive layout

Here is a small but real layout: a centered hero, then a card grid that reflows from three columns to one on narrow screens. Note the very first rule, set box-sizing: border-box globally so width finally means the *total* width, padding and border included. This one line ends the box-model surprise forever.

layout.css
css
/* 1. Make width mean total width, everywhere */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* 2. Flexbox: center one thing on both axes */
.hero {
  display: flex;
  justify-content: center; /* main axis: horizontal */
  align-items: center;     /* cross axis: vertical   */
  min-height: 60vh;
  text-align: center;
}

/* 3. Grid: a responsive card layout, no media query needed */
.cards {
  display: grid;
  /* as many 240px+ columns as fit; they share leftover space */
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
  padding: 2rem;
}

.card {
  /* width:100% now includes padding + border, no overflow */
  width: 100%;
  padding: 1.5rem;
  border: 1px solid #e5e7eb;
  border-radius: 0.75rem;
}

/* 4. Flexbox again, inside each card, to stack + space its parts */
.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

The classic "center a div" problem? Solved in three lines: display: flex; justify-content: center; align-items: center; on the parent. No magic margins, no negative offsets. And the card grid is fully responsive with zero media queries, auto-fit plus minmax lets the browser decide how many columns fit. That is the modern toolset doing the work you used to do by hand.

The cascade & specificity

The C in CSS is Cascading. When two rules target the same element and disagree, the browser needs a tiebreaker. It resolves conflicts in this order: importance, then specificity, then source order.

  1. Importance, an !important declaration beats a normal one. (Use this almost never; see the mistakes below.)
  2. Specificity, a score based on the selector. Inline style="" beats an #id beats a .class beats a plain tag. Roughly: ids are worth more than classes, classes more than tags.
  3. Source order, if specificity ties, the rule written *last* wins. This is why the order of your stylesheet matters.

A concrete tiebreak: a .btn rule and a button rule both set colour. The .btn class wins because a class outranks a tag, regardless of which one you wrote first. Most "my CSS isn't applying!" moments are a more specific rule quietly overriding yours. The fix is almost never !important, it's understanding *which* selector is winning and matching or beating it cleanly.

Keep specificity low and flat

Prefer single classes (`.card-title`) over deep chains (`section.content div.card h2.title`). Flat, low-specificity selectors are easy to override later. Deep, high-specificity ones start a war that ends in `!important`, and a stylesheet nobody can safely change.

Common mistakes that cost hours

  1. No `box-sizing: border-box`. Without it, adding padding makes elements *wider* than the width you set, so they overflow their container. Set it globally once (as in the code above) and never think about box-model math again.
  2. Fighting specificity with `!important`. It feels like a fix; it's a debt. Now the *only* way to override that rule is another !important, and you've started an arms race. Lower your selector specificity instead, !important should be a last resort, not a habit.
  3. Using `float` for layout. Floats were built to wrap text around images. Using them to put boxes side by side leads to collapsed parents, clearfix hacks, and fragile layouts. Use Flexbox or Grid, they were designed for exactly this.
  4. Confusing padding and margin. Padding adds space *inside* (and inherits the background); margin adds space *outside* (always transparent). Reaching for the wrong one is why backgrounds bleed or gaps refuse to appear where you want them.
  5. Vertical margins that mysteriously collapse. Adjacent top/bottom margins merge into the larger of the two rather than adding up. If a gap is half what you expected, margin collapsing is the usual suspect, gap inside a flex/grid container sidesteps it entirely.

Takeaways

The whole article in seven lines

  • Every element is a box: **content → padding → border → margin**, nested like a parcel.
  • Set `box-sizing: border-box` globally so `width` means the *total* width, this kills the #1 layout bug.
  • Padding is inside (inherits background); margin is outside (always transparent).
  • **Normal flow** is the default: block elements stack, inline elements sit in a line.
  • **Flexbox = one dimension** (a row or a column). **Grid = two dimensions** (rows and columns). Use both, per container.
  • The cascade resolves conflicts by **importance → specificity → source order**; keep specificity low and flat.
  • Avoid `!important` and floats-for-layout, they're the debts that cost you hours later.

Where to go next

You now have the four mental models that make CSS predictable. The natural next steps are understanding *why* the browser draws these boxes the way it does, and then how to organize CSS once a project grows past a single file.

  • Read How the Browser Renders a Page to see how the DOM, CSSOM, layout, and paint turn your boxes into pixels, and why some CSS changes are far cheaper than others.
  • Read Component Architecture & Design Systems for how to keep specificity low and styles reusable as your app scales beyond one stylesheet.
  • Follow the full Frontend Engineer path for the foundation-to-senior track these articles belong to.
  • Practice deliberately: rebuild a layout you admire using only Grid for the page skeleton and Flexbox inside each region. Nothing cements these models faster than shipping one real layout.

Want to go deeper?

This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.