Box model
The CSS box model describes how every element’s content, padding, border, and margin combine to occupy space on the page. The display property determines whether an element generates a block-level box (display: block), an inline box (display: inline), or one of the more substantial layout contexts (flex, grid, etc.). The box-sizing property admits substantial control over how the box’s total size is computed: content-box (default — width applies to content only) vs border-box (width includes padding and border — conventionally preferred). The position property admits static (default), relative, absolute, fixed, and sticky positioning. The combination — content/padding/border/margin, the box-sizing choice, the display modes, the positioning system — is the substance of CSS’s spatial model.
The box
Every element is a box with four areas:
┌──────── margin ─────────┐
│ ┌────── border ──────┐ │
│ │ ┌── padding ────┐ │ │
│ │ │ content │ │ │
│ │ └──────────────┘ │ │
│ └───────────────────┘ │
└────────────────────────┘
.box {
width: 200px;
height: 100px;
padding: 1rem;
border: 2px solid black;
margin: 1rem;
background: lightblue;
}
The default box-sizing: content-box produces total width:
total width = width + padding-left + padding-right + border-left + border-right
= 200 + 16 + 16 + 2 + 2 = 236px
total space (with margin) = total width + margin-left + margin-right
= 236 + 16 + 16 = 268px
box-sizing
The box-sizing: border-box admits width includes padding and border:
.box {
box-sizing: border-box;
width: 200px;
padding: 1rem;
border: 2px solid black;
/* Total width is 200px (content area is 200 - 32 - 4 = 164px) */
}
The conventional contemporary discipline applies border-box globally:
*, *::before, *::after {
box-sizing: border-box;
}
The mechanism admits substantial layout simplification — width is what you specify.
display
The principal display values:
.block { display: block; } /* full width, new line */
.inline { display: inline; } /* flows with text */
.inline-block { display: inline-block; } /* inline but admits width/height */
.none { display: none; } /* removed from layout */
.flex { display: flex; } /* flex container */
.grid { display: grid; } /* grid container */
.contents { display: contents; } /* removes the box but keeps children */
The principal differences:
| Display | Block | Inline | Inline-block |
|---|---|---|---|
| Full-width default | Yes | No | No |
| Admits width/height | Yes | No | Yes |
| Margin top/bottom | Yes | No | Yes |
| New line | Yes | No | No |
Block elements: <div>, <p>, <h1>–<h6>, <section>, <article>, <header>, <footer>, <form>, <ul>, <li>.
Inline elements: <span>, <a>, <strong>, <em>, <img>, <br>.
Margin
.box {
margin: 1rem; /* all sides */
margin: 1rem 2rem; /* vertical horizontal */
margin: 1rem 2rem 1rem; /* top, h, bottom */
margin: 1rem 2rem 1rem 0; /* top, right, bottom, left */
}
/* Individual: */
.box {
margin-top: 1rem;
margin-right: 2rem;
margin-bottom: 1rem;
margin-left: 2rem;
}
/* Logical (admits internationalisation): */
.box {
margin-block: 1rem; /* top + bottom */
margin-inline: 2rem; /* left + right (in LTR) */
margin-block-start: 1rem; /* top in LTR */
margin-inline-end: 1rem; /* right in LTR */
}
/* Auto margins (centre): */
.centered {
width: 800px;
margin: 0 auto; /* horizontal centre */
}
/* Negative margins: */
.box { margin-top: -1rem; } /* admit overlap */
Margin collapsing
A distinctive CSS feature: adjacent vertical margins collapse to the larger of the two:
h1 { margin-bottom: 2rem; }
p { margin-top: 1rem; }
The space between <h1> and <p> is 2rem, not 3rem.
The principal rules:
- Vertical only — horizontal margins do not collapse.
- Adjacent siblings — collapse.
- Parent and first/last child — collapse if no padding/border separates them.
- Empty blocks — collapse top and bottom.
The conventional defence (where collapse is undesirable):
.parent {
padding: 1px 0; /* admit parent padding */
/* or */
overflow: auto; /* establish block formatting context */
/* or */
display: flex; /* flex containers don't collapse */
}
Padding
.box {
padding: 1rem; /* all sides */
padding: 1rem 2rem; /* vertical horizontal */
padding: 1rem 2rem 1rem 0; /* top, right, bottom, left */
padding-top: 1rem;
padding-block: 1rem;
padding-inline: 2rem;
}
Padding does not collapse — admit substantial space-establishment.
Border
.box {
border: 1px solid black; /* width style color */
border-width: 1px;
border-style: solid;
border-color: black;
/* Per side: */
border-top: 2px solid red;
border-right: 1px dashed blue;
/* Border radius: */
border-radius: 0.5rem; /* all corners */
border-radius: 0.5rem 0; /* TL+BR, TR+BL */
border-radius: 50%; /* circle/ellipse */
border-top-left-radius: 1rem;
/* Outline (drawn outside, doesn't affect layout): */
outline: 2px solid blue;
outline-offset: 4px;
}
The principal border styles: solid, dashed, dotted, double, groove, ridge, inset, outset, none, hidden.
width and height
.box {
width: 200px;
height: 100px;
/* Sizing keywords: */
width: auto; /* default */
width: 100%; /* parent's width */
width: 50vw; /* 50% viewport width */
/* Min/max: */
min-width: 100px;
max-width: 800px;
min-height: 50px;
max-height: 100vh;
/* Modern: */
width: fit-content; /* shrink to content */
width: max-content; /* widest content */
width: min-content; /* narrowest content */
inline-size: 200px; /* logical width */
block-size: 100px; /* logical height */
}
The conventional discipline:
- Use
max-widthoverwidthfor substantial responsive design. - Use
min-height: 100vhfor full-viewport sections. - Use
width: 100%withmax-widthfor “full width up to a limit”.
.container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
Length units
The principal CSS units:
/* Absolute: */
width: 100px; /* pixels */
width: 1in; /* inches */
width: 2.54cm; /* centimetres */
width: 12pt; /* points */
/* Font-relative: */
width: 1em; /* current element's font-size */
width: 1rem; /* root element's font-size (typically 16px) */
width: 1ex; /* x-height */
width: 1ch; /* width of '0' character */
/* Viewport-relative: */
width: 100vw; /* 100% viewport width */
height: 100vh; /* 100% viewport height */
width: 50vmin; /* 50% of smaller dimension */
width: 50vmax; /* 50% of larger dimension */
/* New (CSS Values L4): */
height: 100dvh; /* dynamic viewport height (excludes mobile UI) */
height: 100svh; /* small viewport height */
height: 100lvh; /* large viewport height */
/* Percentages: */
width: 50%; /* 50% of parent */
The conventional discipline:
remfor font sizes, spacing — admits substantial root-font-size scaling.emfor spacing relative to current font.pxfor borders, fixed thresholds.%for proportional widths.vh/vwfor full-viewport layouts.dvhfor mobile-friendly viewports.
Position
.static { position: static; } /* default; in normal flow */
.relative { position: relative; top: 10px; } /* offset from normal position */
.absolute { position: absolute; top: 0; left: 0; } /* relative to nearest positioned ancestor */
.fixed { position: fixed; top: 0; } /* relative to viewport */
.sticky { position: sticky; top: 0; } /* sticks when scrolling */
The principal differences:
| Position | Removed from flow? | Reference | Use |
|---|---|---|---|
static | No | — | Default |
relative | No | Original position | Anchor for absolute children |
absolute | Yes | Nearest positioned ancestor | Tooltips, dropdowns, overlays |
fixed | Yes | Viewport | Headers, modals |
sticky | Partially | Scroll container | Sticky headers/sidebars |
For absolute positioning, the anchor is the nearest ancestor with position: relative (or absolute/fixed/sticky):
.parent {
position: relative; /* establish containing block */
}
.tooltip {
position: absolute;
top: 100%; /* below parent */
left: 0;
}
For sticky:
.sticky-header {
position: sticky;
top: 0;
z-index: 10;
background: white;
}
The element sticks to the top as the user scrolls past — substantial for navigation bars.
z-index
For positioned elements, z-index controls stacking:
.modal {
position: fixed;
z-index: 100;
}
.tooltip {
position: absolute;
z-index: 50;
}
.regular {
z-index: auto; /* default */
}
The principal rules:
z-indexworks only on positioned elements (notstatic).- Higher wins over lower.
- Stacking contexts — substantial scoping; child can’t escape parent’s stacking context.
Overflow
When content exceeds container:
.box {
overflow: visible; /* default; content overflows */
overflow: hidden; /* clipped */
overflow: scroll; /* always show scrollbars */
overflow: auto; /* scrollbars when needed */
/* Per axis: */
overflow-x: hidden;
overflow-y: auto;
/* Modern: */
overflow: clip; /* like hidden but no scrolling */
text-overflow: ellipsis; /* with overflow:hidden + white-space:nowrap */
overscroll-behavior: contain; /* prevent scroll chaining */
}
For single-line ellipsis:
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
For multi-line ellipsis (modern browsers):
.truncate-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
Common patterns
Reset
The conventional box-sizing reset:
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 0;
}
Centred container
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
Aspect ratio
.ratio-16-9 {
aspect-ratio: 16 / 9;
width: 100%;
}
.square {
aspect-ratio: 1;
}
The aspect-ratio admits substantial responsive sizing without padding tricks.
Sticky footer
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
footer {
/* sticks to bottom when content is short */
}
Modal overlay
.modal-backdrop {
position: fixed;
inset: 0; /* shorthand: top/right/bottom/left = 0 */
background: rgba(0, 0, 0, 0.5);
z-index: 100;
display: flex;
align-items: center;
justify-content: center;
}
.modal {
background: white;
padding: 2rem;
border-radius: 0.5rem;
max-width: 500px;
max-height: 80vh;
overflow: auto;
}
The inset: 0 admits substantial conciseness for full-coverage absolute/fixed positioning.
Floating action button
.fab {
position: fixed;
bottom: 1rem;
right: 1rem;
width: 56px;
height: 56px;
border-radius: 50%;
background: blue;
color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
Sticky table header
table {
width: 100%;
}
thead th {
position: sticky;
top: 0;
background: white;
z-index: 1;
}
Responsive image
img {
max-width: 100%;
height: auto;
display: block;
}
The pattern admits substantial responsive image scaling.
Card with shadow
.card {
background: white;
padding: 1.5rem;
border-radius: 0.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
Logical properties
For internationalisation:
.box {
/* Physical (LTR-specific): */
margin-left: 1rem;
/* Logical (admits RTL): */
margin-inline-start: 1rem;
padding-block: 1rem 2rem; /* top 1rem, bottom 2rem */
padding-inline: 1rem; /* left + right */
border-inline-start: 2px solid blue; /* "left" in LTR */
}
The mechanism admits substantial RTL support.
Block formatting context
To prevent margin collapse and to clear floats:
.bfc {
overflow: auto; /* establishes BFC */
/* or: */
display: flow-root; /* modern, explicit */
/* or: */
display: flex;
/* or: */
display: grid;
}
inset shorthand
.absolute-cover {
position: absolute;
inset: 0; /* top: 0; right: 0; bottom: 0; left: 0 */
/* ... */
}
.modal {
position: fixed;
inset: 0;
/* equivalent to top:0; right:0; bottom:0; left:0 */
}
.partial {
inset: 1rem 2rem; /* top/bottom: 1rem; left/right: 2rem */
}
Scroll-snap
.carousel {
overflow-x: auto;
scroll-snap-type: x mandatory;
display: flex;
}
.slide {
flex: 0 0 100%;
scroll-snap-align: start;
}
contain for performance
.card {
contain: layout paint; /* admit substantial isolation */
}
.list-item {
contain: layout style;
}
The contain admits substantial browser optimisations — substantial for hot animations.
A note on display: none vs visibility: hidden
.removed { display: none; } /* gone from layout */
.invisible { visibility: hidden; } /* invisible but takes space */
.transparent { opacity: 0; } /* visible position, transparent */
The principal differences:
display: none— removed from DOM layout; not focusable; not announced by screen readers.visibility: hidden— takes layout space; not focusable; not announced.opacity: 0— takes layout space; focusable; announced; admits transitions.
For animatable hiding:
.fading {
opacity: 1;
transition: opacity 0.3s;
}
.fading.hidden {
opacity: 0;
pointer-events: none;
}
A note on the conventional discipline
The contemporary CSS box-model advice:
- Use
box-sizing: border-boxglobally — admits substantial layout simplification. - Use
max-widthoverwidthfor responsive containers. - Use logical properties (
margin-block,padding-inline) for internationalisation. - Use
inset: 0for full-coverage positioning. - Use
position: stickyfor substantial sticky headers/sidebars. - Use
aspect-ratiofor substantial responsive sizing. - Use
display: flow-rootto establish BFC (overoverflow: auto). - Use
remandemoverpxfor substantial scalability. - Avoid margin collapse pitfalls — use flex/grid containers.
- Use
containfor substantial performance optimisation. - Use
dvhfor mobile-friendly full-viewport heights.
The combination — content/padding/border/margin, the box-sizing modes, the display values, the positioning system, the substantial length units, the logical properties for internationalisation — is the substance of CSS’s spatial model. The discipline produces predictable, well-spaced layouts with substantial flexibility for substantial design systems.