DNAðŸŽĻ CSSThe Box Model & CSS Positioning
ðŸĢHatchlingCSSBox ModelPositioningFundamentals

The Box Model & CSS Positioning

Every element on the web is a rectangular box. The box model governs its dimensions; positioning governs its placement. Misunderstanding either causes layout bugs that no amount of flexbox can fix.

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

Propertyblockinlineinline-block
Width/HeightRespectedIgnoredRespected
Vertical margin/paddingRespectedIgnored (won't push siblings)Respected
Horizontal margin/paddingRespectedRespectedRespected
Line breakBefore & afterNoNo
Default width100% of parentContent widthContent 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, or right)
  • 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

PropertyIn Flow?Positioned AgainstCreates Stacking Context
staticYesN/A (no offset)No
relativeYesIts original positionOnly with z-index
absoluteNoNearest positioned ancestorOnly with z-index
fixedNoViewport (unless transformed ancestor)Always
stickyYes (until stuck)Scroll container thresholdAlways

Interview Signal

Senior candidates demonstrate:

  1. Box model precision — border-box vs content-box, can calculate actual element dimensions, knows why border-box is the universal reset
  2. Margin collapse — When it happens, when it doesn't, how flow-root/BFC prevents it
  3. Positioning fluency — Containing block rules, fixed being broken by transform, sticky failing with overflow: hidden
  4. Display model — Why inline ignores width/height, when to use inline-block, how display creates formatting contexts
  5. Layout debugging — Can diagnose why an element isn't positioned correctly by tracing the containing block chain