The Box Model & CSS Positioning
The box model and positioning are the two most fundamental layout concepts in CSS. They determine how big elements are and where they sit. Every layout system â Flexbox, Grid, normal flow â operates on top of these primitives.
The Box Model
Every element generates a rectangular box with four areas, from inside out:
âââââââââââââââââââââââââââââââ margin âââââââââââââââââââââââââââââââ
â â
â âââââââââââââââââââââââââââââ border ââââââââââââââââââââââââââ â
â â â â
â â ââââââââââââââââââââââââââ padding âââââââââââââââââââââ â â
â â â â â â
â â â âââââââââââââââââââââââ content ââââââââââââââââ â â â
â â â â â â â â
â â â â width à height â â â â
â â â â â â â â
â â â âââââââââââââââââââââââââââââââââââââââââââââââââ â â â
â â â â â â
â â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â â
â â â â
â ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââcontent-box vs border-box
The box-sizing property determines what width and height control:
/* content-box (default) â width applies to content only */
.content-box {
box-sizing: content-box;
width: 200px;
padding: 20px;
border: 2px solid black;
/* Actual visible width: 200 + 40 + 4 = 244px */
}
/* border-box â width includes padding and border */
.border-box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 2px solid black;
/* Actual visible width: 200px (content shrinks to 156px) */
}Always use border-box â it makes layout predictable:
*, *::before, *::after {
box-sizing: border-box;
}This is the single most important CSS reset rule. Without it, adding padding to a width: 100% element makes it overflow its parent.
Margin Collapse
Vertical margins between adjacent block elements collapse â the larger margin wins:
.heading { margin-bottom: 30px; }
.paragraph { margin-top: 20px; }
/* Gap between them: 30px (not 50px) â margins collapse */Collapse rules:
- Only vertical margins collapse (not horizontal)
- Only between adjacent block-level siblings or parent/first-child
- Does NOT happen with: floats, absolute/fixed elements, flex/grid items, elements with overflow (not
visible), elements with padding/border separating the margins
/* Parent-child margin collapse */
.parent { margin-top: 0; }
.child { margin-top: 20px; }
/* The child's margin "leaks" out of the parent â parent gets 20px top margin */
/* Fix: add padding or border to the parent */
.parent {
padding-top: 1px; /* Prevents collapse */
}
/* Better fix: use a BFC */
.parent {
display: flow-root; /* Creates Block Formatting Context */
}display: inline vs block vs inline-block
| Property | block | inline | inline-block |
|---|---|---|---|
| Width/Height | Respected | Ignored | Respected |
| Vertical margin/padding | Respected | Ignored (won't push siblings) | Respected |
| Horizontal margin/padding | Respected | Respected | Respected |
| Line break | Before & after | No | No |
| Default width | 100% of parent | Content width | Content width |
/* inline elements ignore width, height, and vertical margin */
span {
width: 200px; /* Ignored */
height: 100px; /* Ignored */
margin-top: 20px; /* Ignored */
margin-left: 10px; /* Applied */
padding: 10px; /* Applied visually but doesn't push block siblings */
}
/* inline-block: inline flow with block box model */
.badge {
display: inline-block;
width: 120px;
padding: 4px 8px; /* All dimensions respected */
}CSS Positioning
position: static (Default)
Element is in normal document flow. top, right, bottom, left, and z-index have no effect.
position: relative
Element stays in normal flow but is offset from its original position:
.element {
position: relative;
top: 10px; /* Pushes down 10px from original position */
left: 20px; /* Pushes right 20px from original position */
}- The original space is preserved â other elements don't move
- Creates a containing block for absolutely positioned descendants
- Creates a stacking context when combined with
z-index
position: absolute
Element is removed from flow and positioned relative to its containing block (nearest positioned ancestor):
.parent {
position: relative; /* Establishes containing block */
}
.child {
position: absolute;
top: 0;
right: 0;
/* Placed at top-right corner of .parent */
}If no ancestor is positioned, it positions relative to the initial containing block (the viewport-sized area at the top of the document).
The Centering Pattern
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}top: 50% moves the element's top edge to the center. translate(-50%, -50%) shifts it back by half its own size.
position: fixed
Like absolute, but relative to the viewport. Stays in place during scroll:
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
body {
padding-top: 64px; /* Prevent content from hiding behind fixed navbar */
}Gotcha: transform, filter, or will-change on any ancestor creates a new containing block, breaking fixed positioning. The element becomes "fixed" relative to that ancestor, not the viewport.
position: sticky
Hybrid â normal flow until a scroll threshold, then sticks:
.table-header {
position: sticky;
top: 0;
z-index: 10;
background: white;
}Sticky requirements:
- Must have a threshold (
top,bottom,left, orright) - Only sticks within its scrollable ancestor
- Won't work if any ancestor has
overflow: hidden(the element has nowhere to stick)
/* Common sticky failure â overflow on parent */
.parent {
overflow: hidden; /* Prevents sticky from working */
}
/* Fix: use overflow: clip instead */
.parent {
overflow: clip; /* Hides overflow without creating scroll container */
}Sticky Table Headers
thead th {
position: sticky;
top: 0;
background: white;
box-shadow: 0 1px 0 #e5e7eb;
}Sticky Sidebar
.sidebar {
position: sticky;
top: 80px; /* Below the fixed navbar */
max-height: calc(100vh - 80px);
overflow-y: auto;
}inset Shorthand
/* Instead of: */
.overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
/* Use: */
.overlay {
position: absolute;
inset: 0;
}
/* With different values: */
.element {
inset: 10px 20px 30px 40px; /* top right bottom left */
}Positioning Comparison
| Property | In Flow? | Positioned Against | Creates Stacking Context |
|---|---|---|---|
static | Yes | N/A (no offset) | No |
relative | Yes | Its original position | Only with z-index |
absolute | No | Nearest positioned ancestor | Only with z-index |
fixed | No | Viewport (unless transformed ancestor) | Always |
sticky | Yes (until stuck) | Scroll container threshold | Always |
Interview Signal
Senior candidates demonstrate:
- Box model precision â
border-boxvscontent-box, can calculate actual element dimensions, knows whyborder-boxis the universal reset - Margin collapse â When it happens, when it doesn't, how
flow-root/BFC prevents it - Positioning fluency â Containing block rules,
fixedbeing broken bytransform,stickyfailing withoverflow: hidden - Display model â Why
inlineignores width/height, when to useinline-block, howdisplaycreates formatting contexts - Layout debugging â Can diagnose why an element isn't positioned correctly by tracing the containing block chain