The Fundamentals
Chapter 2 — The Fundamentals: Box Model, Display, and Positioning
Every element on a page is a rectangular box. The box model defines how that
rectangle is sized. The display property defines how boxes relate to one
another in flow. The position property defines whether a box participates
in normal flow at all, and if not, where it lands instead. These three systems interact
constantly — understanding each one precisely is the foundation of layout work.
1. The Box Model
Every box has four concentric layers. From inside out:
- Content — where text and child elements render. Sized by
width/height. - Padding — transparent space inside the border. Inherits the element's background.
- Border — a drawn edge around the padding. Can have colour, style, width, radius.
- Margin — transparent space outside the border. Always transparent — cannot have a background.
box-sizing — the most important CSS reset
By default, width and height size only the content area
(box-sizing: content-box). Adding padding and border makes the element
larger than the declared width — a constant source of layout bugs. The fix is universal:
width: 300px + padding: 20px + border: 2pxTotal width = 300 + 40 + 4 = 344px The box is wider than you said. Every time you add padding or border you must mentally subtract from width. Breaks percentage-based layouts constantly.
width: 300px + padding: 20px + border: 2pxTotal width = 300px (padding + border eat into it) The box is exactly as wide as you said. Content shrinks to accommodate padding and border. Layouts become predictable.
Margin collapse
Vertical margins between adjacent block elements collapse to the larger of the two values — they do not add together. This is intentional (consistent paragraph spacing) but surprises developers constantly.
Margin collapse rules:
- Only happens vertically (top/bottom margins). Horizontal margins never collapse.
- Only between block-level elements in normal flow. Flex/grid children never collapse margins.
- Parent and first/last child margins collapse if no border, padding, or overflow separates them.
- An empty block's top and bottom margins collapse into one.
- Margins of different signs are added (a positive and negative cancel partially).
padding-top: 1px
or overflow: hidden to a parent prevents parent–child collapse. But the cleaner
modern approach is to use Flexbox or Grid on the parent — flex/grid containers never
collapse their children's margins.
Negative margins
2. The display Property
display controls two things simultaneously: how the element itself
participates in its parent's layout (the outer display type), and how it lays out its
own children (the inner display type). The two-value syntax makes this explicit:
Every display value explained
overflow: hidden clearfix hack.visibility: hidden which hides visually but preserves the space and keeps the element in the accessibility tree.Block Formatting Context (BFC)
A BFC is an independent layout region. Elements inside don't affect elements outside, and vice versa. Understanding what creates a BFC explains many CSS "quirks":
3. Positioning
The position property removes an element from normal flow (or modifies
its position within it) using the offset properties top, right,
bottom, left, and inset.
| Value | In normal flow? | Offset reference | Creates stacking context? | Typical use |
|---|---|---|---|---|
| static | Yes | None — offsets ignored | No | Default. Every element is static unless changed. |
| relative | Yes (holds its space) | Its own normal-flow position | If z-index ≠ auto | Nudge from normal position. More importantly: establish a containing block for absolute children. |
| absolute | No (removed) | Nearest positioned ancestor (non-static) | If z-index ≠ auto | Tooltips, dropdowns, badges, overlays — anything that needs to float over other content. |
| fixed | No (removed) | Viewport (initial containing block) | Yes (always) | Sticky nav bars, cookie banners, floating chat buttons. Unaffected by scroll. |
| sticky | Yes — until threshold | Nearest scrolling ancestor | Yes (always) | Table headers, section headers that stick on scroll. Behaves like relative until the offset threshold is hit. |
The containing block — the key to understanding absolute positioning
An absolutely positioned element is placed relative to its containing block
— the nearest ancestor that has a position value other than static.
If no such ancestor exists, it falls back to the initial containing block (the viewport).
This is why position: relative is added to a parent even when you don't
want to move the parent itself:
transform, filter, or perspective value other
than none becomes the containing block for absolutely positioned
descendants — even without position: relative. This catches developers
off guard when a fixed element stops being fixed because an ancestor
has a CSS transform applied to it.
The inset shorthand
Centering with absolute positioning
sticky — how it actually works
4. z-index and Stacking Contexts
z-index controls which element appears on top when boxes overlap.
But it only works on elements that are positioned (anything except static)
or are flex/grid items. The critical detail most developers miss: z-index values only
compare within the same stacking context.
What creates a stacking context
- The root element (
<html>) — the base stacking context position: relative/absolute+z-indexother thanautoposition: fixedorsticky(always)opacityless than 1transform,filter,perspective,clip-path(non-none)isolation: isolate— the clean, explicit way to create onewill-changewith certain properties- Flex/grid items with
z-indexother than auto
In the diagram above, the child's z-index: 9999 only competes within
the parent's stacking context. The parent itself has z-index: 10 in the root
context. No matter how high the child's z-index goes, it cannot render above something
at the root context level with z-index: 11.
5. overflow
overflow controls what happens when content is larger than its box.
It is a shorthand for overflow-x and overflow-y.
overflow: hidden, overflow: auto, or
overflow: scroll, the sticky element will not stick. This is the most
common reason sticky mysteriously stops working. Use overflow: clip
if you need to clip without creating a scroll container.
Chapter Summary
| Concept | Key point |
|---|---|
| box-sizing | Always set *, *::before, *::after { box-sizing: border-box }. With border-box, declared width includes padding and border — no mental arithmetic needed. |
| margin collapse | Vertical block margins collapse to the larger value. Flex/grid containers prevent collapse. display: flow-root prevents parent–child collapse without side effects. |
| display | Controls outer type (how the element sits in its parent's flow) and inner type (how it lays out its own children). flex/grid set both at once. |
| flow-root | Creates a BFC cleanly. Use it to contain floats or prevent margin collapse escaping a parent, instead of the old overflow: hidden hack. |
| position: relative | Doesn't move the element visually unless you add offsets. Main purpose: establish a containing block for absolutely positioned children. |
| position: absolute | Removed from flow. Positioned relative to nearest non-static ancestor. If none, falls back to viewport. |
| transform breaks fixed | Any ancestor with a CSS transform becomes the containing block for fixed/absolute children. Fixed elements stop being fixed relative to the viewport. |
| sticky failures | Must have an offset. Any ancestor with overflow: hidden/auto/scroll breaks it. Only works within its parent's bounds. |
| Stacking contexts | z-index only compares within the same stacking context. An element can't escape its parent's context no matter how high its z-index. Use isolation: isolate to create contexts deliberately. |
- Box model maths: Create a div with
width: 200px,padding: 20px,border: 5px solid. Withoutbox-sizing: border-box, how wide is it? With it? Verify in DevTools by hovering the element in the Elements panel. - Margin collapse: Create two adjacent
<p>elements each withmargin: 24px 0. Measure the gap. Now wrap them in a<div>and adddisplay: flex; flex-direction: columnto the wrapper. Measure again. Explain the difference. - Positioned badge: Build a card with a "NEW" badge positioned at the top-right corner using
position: absolute. Ensure the badge stays anchored to the card, not the viewport. - Sticky header: Create a page with multiple sections each 400px tall. Give each section a heading with
position: sticky; top: 0. Then addoverflow: hiddento the section. Observe what breaks and fix it. - Stacking context trap: Create two overlapping absolutely positioned divs. Give one
z-index: 1and its childz-index: 999. Give the otherz-index: 2. Confirm thatz-index: 999cannot beatz-index: 2.