📦 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-boxvsborder-boxand pick the right one - Use margin and padding shorthand, and predict when vertical margins collapse
- Choose the correct
displayvalue for an element's role in the flow - Position elements with
static,relative,absolute,fixed, andsticky - 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.
.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 */
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.
| Value | New line? | Respects width/height? | Typical elements |
|---|---|---|---|
block | Yes | Yes | div, p, section |
inline | No | No | span, a, strong |
inline-block | No | Yes | buttons, nav items |
none | — | — | hidden elements |
flex / grid | Yes (block-level) | Yes | layout 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.
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-boxreset at the top of your CSS - Reach for flexbox or grid for real layout; use positioning for overlays and small nudges
- Use
margin: 0 autoto center a fixed- or max-width block horizontally - Keep a small set of named
z-indextiers (e.g. header 1000, modal 10000) instead of random big numbers
❌ Don't
- Expect vertical margins to add up — remember they collapse
- Reach for
floatto build page layouts; that era ended with flexbox and grid - Sprinkle
z-index: 9999everywhere; it just moves the fight up a level - Forget that
z-indexand offsets do nothing on astaticelement
⚠️ 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-boxmakes the width you set the width you get — apply it globally- Margin/padding shorthand runs clockwise; vertical margins collapse to the larger value
displaysets flow behavior;positionlets elements escape it — static, relative, absolute, fixed, sticky- The relative-parent / absolute-child pattern pins elements;
z-indexlayers 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.