Skip to main content

📦 Box Model and Positioning

Every element the browser draws is a rectangular box — even a single word. Once you can see those boxes and control the space inside and around them, CSS layout stops feeling like guesswork. This lesson gives you that x-ray vision: the box model, the sizing rule that trips up every beginner, and the position property that lets boxes escape the normal flow.

Week 1 · Wednesday: CSS Basics · Lecture 2

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Name the four layers of the box model and control each with CSS
  • Explain content-box vs border-box and pick the right one
  • Use margin and padding shorthand, and predict when vertical margins collapse
  • Choose the correct display value for an element's role in the flow
  • Position elements with static, relative, absolute, fixed, and sticky
  • Layer overlapping elements predictably with z-index

Estimated Time: 65 minutes

Practice: Build a centered card, a fixed header, and a centered modal overlay from scratch.

In This Lesson

The Box Model

Picture a framed photo on a wall. The photo itself is the content. The mat board around it is padding — inside the frame, part of the piece. The frame is the border. And the empty wall you leave between this frame and the next is the margin. CSS builds every element out of exactly these four nested layers.

Nested box model: margin surrounds border, border surrounds padding, padding surrounds content margin border padding content
From the inside out: content → padding → border → margin. Padding is inside the border; margin is the gap outside it.
graph LR A[Content] --> B[+ Padding] B --> C[+ Border] C --> D[+ Margin]
.box {
    width: 300px;              /* content width */
    height: 200px;             /* content height */
    padding: 20px;             /* space inside the border */
    border: 5px solid #333;    /* the frame */
    margin: 10px;              /* space outside, pushing neighbors away */
}

💡 Padding vs margin — which do I want?

Use padding to give content breathing room inside a box (and it takes the box's background color). Use margin to create space between boxes. A background color extends through the padding but not the margin — a handy way to tell which one you're actually seeing.

box-sizing: the Sizing Trap

Here's the surprise that catches everyone. By default, width sets the width of the content only — padding and border are then added on top. So a "300px" box can render much wider than 300px.

Default: content-box

.box {
    width: 300px;
    padding: 20px;
    border: 5px solid black;
}
/* Actual rendered width = 300 + 20*2 + 5*2 = 350px  😱 */

The fix: border-box

.box {
    box-sizing: border-box;
    width: 300px;
    padding: 20px;
    border: 5px solid black;
}
/* Actual rendered width = 300px exactly — padding & border grow inward */
content-box makes the total wider than the set width; border-box keeps the total equal to the set width content-box (default) content 300px total rendered width = 350px border-box content shrinks to fit total = 300px
With border-box, the number you write is the number you get — padding and border eat into it instead of stacking on top.

Set it once, globally

Nearly every modern stylesheet opts every element into border-box right at the top. Do this and box sizing stops being something you ever think about again:

*, *::before, *::after {
    box-sizing: border-box;
}

✅ Make this your first line of CSS

The global border-box reset is one of the highest-value three lines in front-end development. It means width: 50% plus padding actually fits in half the space, and grids stop overflowing their containers.

Margin & Padding

Shorthand: 1, 2, 3, or 4 values

The margin and padding shorthands read clockwise from the top. The number of values you give changes their meaning:

.a { margin: 10px; }             /* all four sides: 10 */
.b { margin: 10px 20px; }        /* vertical 10 | horizontal 20 */
.c { margin: 10px 20px 15px; }   /* top 10 | horizontal 20 | bottom 15 */
.d { margin: 10px 20px 15px 25px; } /* top | right | bottom | left (clockwise) */

A memory aid for the four-value form: Top, Right, Bottom, Left — "TRouBLe," clockwise starting at 12 o'clock.

Centering with margin: auto

/* Horizontally center a fixed- or max-width block */
.container {
    max-width: 1200px;
    margin: 0 auto;   /* 0 top/bottom, auto left/right splits the leftover space */
}

Margin collapse — the quiet gotcha

When two block elements sit one above the other, their vertical margins don't add up — they collapse to the larger of the two. This only happens vertically, and only with margins (never padding).

.box1 { margin-bottom: 20px; }
.box2 { margin-top: 30px; }
/* The visible gap between them is 30px, NOT 50px */

⚠️ Surprised by a gap that's "too small"?

Margin collapse is usually the culprit. If you need the two margins to actually sum, put the elements in separate formatting contexts (e.g. add padding or a border to the parent), or switch the parent to a flex/grid container, where margins never collapse.

Borders & Radius

The border shorthand

/* width | style | color — style is required, or nothing shows */
.box { border: 2px solid #333; }

/* Each side can differ */
.box {
    border-top: 2px solid red;
    border-bottom: 1px dotted green;
}

/* Common styles: solid, dashed, dotted, double, none */

Rounded corners

.rounded { border-radius: 10px; }              /* all corners */

.pill    { border-radius: 999px; }             /* fully rounded ends */

.circle  {                                     /* a perfect circle */
    width: 100px;
    height: 100px;
    border-radius: 50%;
}

💡 border-radius: 50% on a square = circle

A radius of 50% rounds each corner to half the box's size, which meets in the middle — turning an equal-sided box into a circle and any rectangle into an ellipse. It's the standard trick for round avatars and icon buttons.

The display Property

Before an element can be positioned, you need to understand how it flows. The display property sets an element's fundamental behavior in the document.

graph TD A[display] --> B[block] A --> C[inline] A --> D[inline-block] A --> E[none] A --> F[flex / grid] B --> B1[Full width, starts a new line] C --> C1[Flows with text, ignores width/height] D --> D1[Flows inline, but respects width/height] E --> E1[Removed from the layout entirely] F --> F1[Becomes a layout container]
ValueNew line?Respects width/height?Typical elements
blockYesYesdiv, p, section
inlineNoNospan, a, strong
inline-blockNoYesbuttons, nav items
nonehidden elements
flex / gridYes (block-level)Yeslayout containers
.badge   { display: inline-block; width: 60px; text-align: center; }
.hidden  { display: none; }   /* gone — takes up no space at all */

⚠️ display: none vs visibility: hidden

display: none removes the element from layout completely — as if it isn't there. visibility: hidden hides it but keeps its space reserved. And opacity: 0 makes it invisible yet still clickable. Three different tools for three different jobs.

Positioning

The position property lets an element break out of the normal document flow and be placed with the offset properties top, right, bottom, and left. There are five values.

graph TD A[position] --> B[static — default, normal flow] A --> C[relative — offset from its own spot] A --> D[absolute — placed within nearest positioned ancestor] A --> E[fixed — pinned to the viewport] A --> F[sticky — relative until it hits a threshold, then fixed]

1. static — the default

.el { position: static; }
/* Normal flow. top/left/right/bottom have no effect here. */

2. relative — nudge from home

.el {
    position: relative;
    top: 20px;
    left: 30px;   /* moves 20px down and 30px right of where it would sit */
}
/* Its original space is preserved — neighbors don't move in. */

Relative positioning has a second, more important job: it makes an element the reference point for any absolute children inside it.

3. absolute — placed against an ancestor

.parent   { position: relative; }  /* establishes the reference frame */
.badge    {
    position: absolute;
    top: 0;
    right: 0;   /* pins to the top-right corner of .parent */
}
/* Removed from normal flow — other content ignores it. */

📖 The relative-parent / absolute-child pattern

This is the single most common positioning recipe on the web: a "notification dot" on an icon, a close button in a modal corner, a caption over an image. Set position: relative on the container, position: absolute on the thing you want to pin. Without a positioned ancestor, an absolute element climbs all the way up to the page itself.

4. fixed — pinned to the screen

.back-to-top {
    position: fixed;
    bottom: 20px;
    right: 20px;   /* stays put in the corner even as the page scrolls */
}

5. sticky — the hybrid

.section-header {
    position: sticky;
    top: 0;   /* scrolls normally until it reaches the top, then sticks there */
}

Sticky is how table headers and the table of contents on this very page stay visible as you scroll past their section. It behaves like relative until the offset threshold is crossed, then like fixed — but only within its parent's bounds.

Z-index & Stacking

When positioned elements overlap, z-index decides who's on top — higher numbers sit in front. It only affects elements whose position is something other than static.

.behind { position: absolute; z-index: 1; }
.middle { position: absolute; z-index: 2; }
.front  { position: absolute; z-index: 3; }  /* drawn on top of the others */

An element establishes a new stacking context when it is positioned with a z-index, or has properties like opacity below 1, a transform, or a filter. Inside a stacking context, all z-index values are relative to that context — which is why a child with z-index: 9999 can still sit behind an element in a different, higher context.

⚠️ z-index only works on positioned elements

If your z-index "isn't doing anything," the element is almost certainly still position: static. Give it relative, absolute, fixed, or sticky first.

Practical Layout Patterns

Here's how the pieces combine into things you'll build constantly.

Centered page container

.container {
    max-width: 1200px;
    margin: 0 auto;      /* center horizontally */
    padding: 0 20px;     /* breathing room on small screens */
}

Fixed site header

.site-header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    background: #fff;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    z-index: 1000;
}
/* Push page content down so it isn't hidden under the header */
body { padding-top: 60px; }

Centered modal over a dim overlay

.modal-overlay {
    position: fixed;
    inset: 0;                          /* shorthand for top/right/bottom/left: 0 */
    background: rgba(0, 0, 0, 0.5);    /* the dim backdrop */
    z-index: 9999;
}
.modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);  /* true centering, any size */
    background: #fff;
    padding: 24px;
    border-radius: 8px;
    z-index: 10000;                     /* above the overlay */
}

💡 The translate(-50%, -50%) centering trick

top: 50%; left: 50% places the element's top-left corner at the center. The translate(-50%, -50%) then shifts it back by half its own width and height — landing the element's center on the page's center, no matter how big it is.

Practice & Quiz

🏋️ Exercise 1: Predict the rendered width

Goal: Given the CSS below, what is the element's total on-screen width in each case?

/* Case A */
.a { width: 200px; padding: 15px; border: 5px solid; }

/* Case B */
.b { box-sizing: border-box; width: 200px; padding: 15px; border: 5px solid; }
💡 Hint

Case A is the default content-box: add padding on both sides, then border on both sides, to the content width. Case B is border-box: the declared width already includes them.

✅ Solution

Case A = 240px (200 + 15×2 + 5×2). Case B = 200px — padding and border are absorbed into the 200px, so the content area shrinks to 160px.

🏋️ Exercise 2: Pin a "NEW" badge to a card corner

Goal: Given a .card containing a .badge, position the badge in the top-right corner of the card, slightly overlapping the edge.

<div class="card">
    <span class="badge">NEW</span>
    <h3>Product name</h3>
</div>
💡 Hint

The card must be the reference frame (position: relative), and the badge must be position: absolute. Use negative offsets to overlap the edge.

✅ Solution
.card {
    position: relative;
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
}
.badge {
    position: absolute;
    top: -10px;
    right: -10px;
    background: crimson;
    color: #fff;
    padding: 4px 8px;
    border-radius: 999px;
    font-size: 12px;
}

🎯 Quick Quiz

Question 1: With the default box model, an element has width: 100px, padding: 10px, and border: 2px solid. What is its rendered width?

Question 2: To position a badge relative to a card, what must the card have?

Question 3: Which position value keeps an element in the flow, then pins it once it scrolls to a threshold?

Best Practices & Pitfalls

✅ Do

  • Set a global box-sizing: border-box reset at the top of your CSS
  • Reach for flexbox or grid for real layout; use positioning for overlays and small nudges
  • Use margin: 0 auto to center a fixed- or max-width block horizontally
  • Keep a small set of named z-index tiers (e.g. header 1000, modal 10000) instead of random big numbers

❌ Don't

  • Expect vertical margins to add up — remember they collapse
  • Reach for float to build page layouts; that era ended with flexbox and grid
  • Sprinkle z-index: 9999 everywhere; it just moves the fight up a level
  • Forget that z-index and offsets do nothing on a static element

⚠️ Floats: know them, rarely use them

.image { float: left; margin-right: 20px; }  /* text wraps around it */

float was originally for wrapping text around an image, and that one job it still does well. It was later abused to build multi-column layouts — a job now done far better by flexbox and grid. Recognize floats in old code; don't reach for them for layout.

Summary

🎉 Key Takeaways

  • Every element is a box of four layers: content → padding → border → margin
  • box-sizing: border-box makes the width you set the width you get — apply it globally
  • Margin/padding shorthand runs clockwise; vertical margins collapse to the larger value
  • display sets flow behavior; position lets elements escape it — static, relative, absolute, fixed, sticky
  • The relative-parent / absolute-child pattern pins elements; z-index layers positioned elements

📚 Additional Resources

🚀 What's Next?

You can size, space, and place boxes. Next you'll fill them with beautiful detail: Colors, Typography, and Basic Styling — color formats, font properties, web fonts, backgrounds, and shadows.

🎉 Great work!

The box model is the mental model every professional carries in their head. Once you see the boxes, layout becomes a conversation instead of a wrestling match.