023: The Box Model
Learning outcomes
By the end, you can identify content, padding, border, and margin; calculate box dimensions in both sizing models; apply a global border-box setup; distinguish margin from padding; explain vertical margin collapse; and inspect box geometry in DevTools.
Prerequisites and retrieval
Use the typographic portfolio. Retrieve why fixed text heights fail at zoom and which properties inherit. Predict whether a child's padding inherits from its parent (it does not). This exercise focuses on the rectangle generated by each visible element.
Terminology
- Content box: The innermost box area containing text, images, or child boxes. — Source: CSS Box Model 4
- Padding: The area between the content edge and border edge, painted with the background. — Source: MDN: The box model
- Border: The drawn edge area surrounding padding and content. — Source: MDN: The box model
- Margin: The always-transparent outermost area separating an element from siblings. — Source: MDN: The box model
content-box: Default box-sizing where declared width/height apply to content only. — Source: MDN: box-sizingborder-box: Box-sizing keyword where declared width/height include padding and border. — Source: MDN: box-sizing- Margin collapse: Adjoining vertical margins merging into one margin in normal flow. — Source: MDN: Mastering margin collapsing
- Outer size: The margin-box extent: border box plus margins. — Source: CSS Box Model 4
- Box edges: "Each box has four edges: content edge, padding edge, border edge, and margin edge." — Source: CSS Box Model Module Level 4: Box edges
- Logical properties: "Logical properties such as margin-block and padding-inline map to physical sides based on writing mode." — Source: MDN: Logical Properties
Mental model: nested rectangles
Picture four layers from inside out: content, padding, border, margin. Backgrounds paint through the padding to the border edge by default, but not through margin. A clickable element's padding increases its painted and hit area; margin only separates it from neighbors.
With default content-box, an element declared width: 300px; padding: 20px; border: 2px occupies a 344px border box: 300 + 40 + 4. With border-box, the declared 300px includes content, padding, and border. Margins are always outside the declared width.
Set predictable sizing at the start:
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
The universal rule includes generated pseudo-elements and inherits the root decision. This does not remove the need to understand dimensions; it makes component sizing easier.
Beginner example: dissect a project card
<article class="project-card">
<p class="project-card__meta">Accessibility · 2026</p>
<h2>Library finder</h2>
<p>Opening hours and directions presented clearly.</p>
<a class="project-card__link" href="#">Read case study</a>
</article>
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
margin: 0;
padding: 1rem;
background: rgb(241 245 249);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.project-card {
max-inline-size: 32rem;
margin-block: 1.5rem;
padding: 1.25rem;
border: 2px solid rgb(203 213 225);
border-inline-start: 0.4rem solid rgb(37 99 235);
border-radius: 0.75rem;
background: white;
}
.project-card > :first-child { margin-block-start: 0; }
.project-card > :last-child { margin-block-end: 0; }
.project-card__link {
display: inline-block;
padding: 0.5rem 0.75rem;
border: 2px solid rgb(29 78 216);
border-radius: 0.35rem;
}
Inspect .project-card. DevTools' box diagram shows content dimensions in the center, then padding, border, and margin. Toggle padding: content approaches the border. Toggle margin: the card approaches neighboring boxes, but its white background does not expand. Toggle border and observe how border-box accounts for it when an inline size is constrained.
The link becomes inline-block so vertical padding and a border form a predictable visual control while it still sits inline. 025 explains display in depth.
Intermediate example: spacing and margin collapse
Adjacent block margins can collapse:
<section class="prose">
<h2>Process</h2>
<p>First, understand the content.</p>
<p>Then, test the interaction.</p>
</section>
.prose h2 { margin-block: 0 1rem; }
.prose p { margin-block: 1rem 0; }
The touching 1rem vertical margins do not necessarily add to 2rem; in normal block flow they may collapse to one margin. Horizontal/inline margins do not collapse. Padding and borders can prevent parent/child margins from meeting.
A controlled “flow spacing” pattern places space only between siblings:
.flow > * { margin-block: 0; }
.flow > * + * { margin-block-start: 1rem; }
Add class="prose flow". The adjacent sibling selector targets every direct child that follows another direct child. Each gap now has one declared source. This pattern is optional; ordinary margins are valid when you understand them.
Create a callout:
.callout {
margin-block: 2rem;
padding: 1rem;
border: 1px solid rgb(147 197 253);
border-radius: 0.5rem;
background: rgb(239 246 255);
}
The padding is internal breathing room and carries background paint. The margin separates the callout from surrounding prose. Swapping them would change both visual grouping and painted area.
Optional advanced example: outline versus border
Focus indicators often use outline:
a:focus-visible {
outline: 3px solid rgb(234 88 12);
outline-offset: 3px;
}
Unlike a border, an outline generally does not participate in box dimensions, so focus does not shift neighboring content. It may overlap nearby content; provide enough separation. Do not clip focus indicators with unnecessary overflow: hidden.
Mistakes, debugging, and DevTools
- Assuming
width: 100%plus padding fits withcontent-box: it can overflow its parent. - Adding margins to enlarge a click target: margin is not part of the element's clickable painted box; use padding.
- Using negative margins to repair unexplained layout: inspect the box model and source of spacing first.
- Forgetting default heading/paragraph margins: browser CSS is visible in DevTools.
- Expecting vertical margins always to add: check collapse conditions.
- Removing outlines because they sit outside the border: replace them accessibly instead.
- Applying
overflow: hiddenjust to contain rounded corners and clipping focus or content.
Use the element picker and Box Model panel. Hover each region to highlight it on the page. Edit values directly; use arrow keys for increments. Check the Computed pane for box-sizing, used dimensions, and logical-to-physical mappings. If an element is unexpectedly wide, inspect parent width, its own width, both inline padding values, borders, and margins.
Accessibility and performance
Padding can create comfortable target sizes. WCAG 2.2 Target Size (Minimum) generally calls for a 24 by 24 CSS pixel target or sufficient spacing, with exceptions; larger controls are often easier. Do not shrink text just to fit a fixed box. Let boxes grow in the block direction.
Visible keyboard focus may extend outside the border. Avoid clipping it and maintain sufficient contrast. Logical properties such as padding-inline and margin-block adapt to writing modes and directions.
Borders, padding, and radius are inexpensive. Huge blurred shadows can increase paint work, especially during animation or scrolling. Optimize only after measuring; correct responsive geometry is the priority.
Deep dive: calculate a box instead of guessing
With the default content-box model:
.card {
width: 20rem;
padding: 1rem;
border: 2px solid;
}
the declared width describes only the content box. The rendered border-box width is:
20rem + 2rem padding + 4px border.
With:
*,
*::before,
*::after {
box-sizing: border-box;
}
the declared width includes content, padding, and border, so layout calculations are usually easier.
Do not memorize border-box as a magic reset. Understand what changed: the sizing equation.
Deep dive: margin collapse
Vertical margins in normal block flow can collapse instead of adding together.
<section class="section">
<h2>Title</h2>
<p>Intro text.</p>
</section>
h2 {
margin-block-end: 2rem;
}
p {
margin-block-start: 1rem;
}
You might expect 3rem between the boxes, but adjoining block margins can collapse so the larger margin effectively wins.
Margins do not collapse in the same way inside Flexbox or Grid layout. Creating a new formatting context with display: flow-root can also change collapse behavior. This is why random margin changes can appear inconsistent when a component moves from normal flow into Grid.
A maintainable spacing pattern is to let one layer own the relationship:
.flow > * + * {
margin-block-start: 1rem;
}
Now sibling spacing is managed by the wrapper rather than by both adjacent components.
Worked example: content-box bug versus border-box fix
<form class="signup">
<label for="email">Email</label>
<input id="email" type="email">
</form>
Bug:
.signup input {
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #94a3b8;
}
Under content-box, 100% describes the content width, then padding and border are added, so the input can overflow its parent.
Fix:
.signup input {
box-sizing: border-box;
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #94a3b8;
}
A global border-box rule is common because this behavior is usually what interface authors expect.
Deep dive: border, radius, and outline have different jobs
.card {
border: 1px solid #cbd5e1;
border-radius: 0.75rem;
}
.card:focus-within {
outline: 3px solid #2563eb;
outline-offset: 3px;
}
The border participates in the box model. An outline is drawn outside the border edge and does not normally consume layout space. That makes outline especially useful for focus indication.
Never replace a visible focus outline with outline: none unless another equally obvious focus indicator is provided.
Worked example: spacing ownership in a card
Fragile:
.card h2 { margin-bottom: 1rem; }
.card p { margin-top: 0.75rem; margin-bottom: 1rem; }
.card a { margin-top: 2rem; }
More predictable:
.card {
padding: 1.25rem;
}
.card__body > * {
margin: 0;
}
.card__body > * + * {
margin-block-start: 0.75rem;
}
.card__action {
display: inline-block;
margin-block-start: 1.25rem;
}
The card owns outer padding; the body owns vertical rhythm; the action owns its separation from the body. This is easier to reason about than overlapping margins from every child.
Box-model debugging routine
When a component is unexpectedly too large:
- Inspect its box-model diagram in DevTools.
- Check
box-sizing. - Check intrinsic width of children.
- Check percentage widths plus padding/borders.
- Check margins outside the border box.
- Check
min-widthormin-heightconstraints. - Check whether a flex/grid item's automatic minimum size prevents shrinking.
Do not solve every overflow with overflow: hidden; that can hide focus rings, menus, and genuine layout bugs.
Tiered exercises
Checkpoint: diagnose spacing ownership
When a gap looks wrong, select both neighboring elements and ask who should own that relationship. Padding belongs to the component whose background needs breathing room. Margin belongs to separation between components. A gap belongs to a parent layout when all children need consistent spacing. Avoid leaving two unrelated margins to produce an accidental total.
Run a box calculation twice. For inline-size: 20rem, inline padding of 1rem, and borders of 0.125rem, content-box produces a 22.25rem border box. Under border-box, the border box remains 20rem and the content receives the remainder. Add margin-inline: 1rem; the outer occupied width becomes 22rem under border-box because margin is never included. Then resize below 22rem and observe why a max constraint and available-width relationship still matter.
Use DevTools to verify the arithmetic rather than trusting visual estimation. Fractional used values may appear because rem values, zoom, and device scaling resolve to subpixels; this is normal.
Create a nested card with a heading as its first child. Give the heading a block-start margin and remove the card's padding/border temporarily. Observe whether the child margin appears outside the parent through margin collapse. Restore padding and see the boundary change. Do not memorize every collapse combination; remember that adjoining block margins in normal flow can combine, then inspect when spacing appears outside its apparent owner. Flex and Grid containers establish different margin behavior, another reason a layout-mode change can alter spacing unexpectedly.
Finally, compare border and outline while focusing the card link. A thicker border changes geometry unless dimensions accommodate it; outline paints without consuming layout space. This makes outline a strong default focus mechanism, provided it is not clipped and remains visible against adjacent colors.
Foundation: Draw and label the four box layers. Calculate the border-box width of a 240px content-box with 16px inline padding and 2px borders.
Core: Apply the global border-box setup. Build a card with internal padding, external margin, border, radius, and a padded link. Explain why each spacing property was chosen.
Stretch: Create a .flow section with exactly one consistent block gap between direct children. Demonstrate margin collapse before and after and inspect both in DevTools.
The content-box total is 240 + 16 + 16 + 2 + 2 = 276px.
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
.project-card {
max-inline-size: 32rem;
margin-block: 1.5rem;
padding: 1.25rem;
border: 2px solid rgb(203 213 225);
border-inline-start: 0.4rem solid rgb(37 99 235);
border-radius: 0.75rem;
background: white;
}
.project-card__link {
display: inline-block;
padding: 0.5rem 0.75rem;
border: 2px solid currentColor;
border-radius: 0.35rem;
}
.flow > * { margin-block: 0; }
.flow > * + * { margin-block-start: 1rem; }
.project-card__link:focus-visible {
outline: 3px solid rgb(234 88 12);
outline-offset: 3px;
}
Recap and exit questions
Every rendered element generates nested content, padding, border, and margin areas. border-box makes declared sizing include padding and border. Use padding for internal/clickable space and margin for separation.
- What contributes to a
content-boxelement's total width? - Does margin receive the element's background?
- Why does padding enlarge a target but margin does not?
- Which margins can collapse?
- Why is outline useful for focus?
