containing blocks
A containing block is the reference rectangle that an element is positioned and sized relative to. For most elements, the containing block is the parent's content box. For absolutely positioned elements, it's the nearest positioned ancestor's padding box. For fixed elements, it's the viewport. Understanding containing blocks is crucial for accurate positioning, percentage-based sizing, and debugging layout bugs.
What is a containing block?
Every element in the CSS box model has a containing block. The containing block provides a reference frame for:
- Positioning:
top,right,bottom,leftvalues are relative to the containing block - Sizing: Percentage widths and heights are relative to the containing block's dimensions
- Layout: Child elements flow within the containing block's space
<!-- Simple containing block relationship -->
<div class="parent">
<div class="child">I'm positioned relative to parent</div>
</div>
.parent {
width: 300px;
height: 200px;
}
.child {
width: 50%; /* 50% of parent width = 150px */
height: 50%; /* 50% of parent height = 100px */
/* parent is the containing block */
}
.child {
position: relative;
top: 20px;
left: 20px;
/* Positioned relative to parent (containing block) */
}Determining the containing block
Element type | Containing block | Reference |
|---|---|---|
Normal flow (static) | Parent's content box | Block parent or inline parent |
Relative positioned | Parent's content box | Same as static |
Absolutely positioned | Nearest positioned ancestor | position is not static |
Fixed positioned | Viewport (or document in some cases) | Browser window |
Inline elements | Parent inline box | Generated by inline parent |
For static and relative positioned elements
<!-- Parent is the containing block -->
<div class="parent">
<div class="child">Static or relative</div>
</div>
.parent {
width: 400px;
height: 300px;
}
.child {
width: 50%; /* 50% of 400px = 200px */
/* Parent's content box is containing block */
}
<!-- Nested parents -->
<div class="grandparent">
<div class="parent">
<div class="child">Percentage sizing</div>
</div>
</div>
.grandparent { width: 600px; }
.parent { width: 400px; }
.child {
width: 50%; /* 50% of parent (400px) = 200px */
/* parent is containing block, NOT grandparent */
}For absolutely positioned elements
This is where containing blocks get tricky. For absolutely positioned elements, the containing block is the nearest ancestor with a position value other than static.
<!-- With positioned parent -->
<div class="parent">
<div class="child">Absolutely positioned</div>
</div>
.parent {
position: relative; /* creates containing block */
width: 300px;
height: 200px;
}
.child {
position: absolute;
width: 50%; /* 50% of parent = 150px */
top: 20px; /* 20px from parent top */
left: 20px; /* 20px from parent left */
}
<!-- Without positioned parent (containing block is viewport) -->
<div class="static-parent">
<div class="child">Absolutely positioned</div>
</div>
.static-parent {
/* position: static (default) */
/* NOT a containing block */
}
.child {
position: absolute;
width: 50%; /* 50% of VIEWPORT */
top: 20px; /* 20px from viewport top */
left: 20px; /* 20px from viewport left */
}Creating a containing block
Several CSS properties create a containing block for absolutely positioned descendants. These include position: relative, position: absolute, position: fixed, and others.
<!-- These create containing blocks -->
/* position: relative */
.parent {
position: relative;
}
/* position: absolute */
.parent {
position: absolute;
}
/* position: fixed */
.parent {
position: fixed;
}
/* position: sticky */
.parent {
position: sticky;
}
/* transform (any non-identity value) */
.parent {
transform: rotate(10deg);
}
/* opacity < 1 */
.parent {
opacity: 0.5;
}
/* filter (any value) */
.parent {
filter: blur(5px);
}
/* mix-blend-mode (any non-normal value) */
.parent {
mix-blend-mode: multiply;
}
/* will-change (any value) */
.parent {
will-change: transform;
}
<!-- Best practice: use position: relative for clarity -->
.parent {
position: relative; /* explicitly creates containing block */
}
.positioned-child {
position: absolute;
/* Clearly relates to parent */
}Percentage sizing with containing blocks
<!-- Percentage width/height are relative to containing block -->
<div class="container">
<div class="box">Percentage sized</div>
</div>
.container {
width: 500px;
height: 400px;
}
.box {
width: 50%; /* 50% of 500px = 250px */
height: 50%; /* 50% of 400px = 200px */
/* container is containing block */
}
<!-- Percentage with auto width -->
.container {
width: auto; /* width determined by content */
height: 400px;
}
.box {
width: 50%; /* 50% of... what? Container has auto width */
/* Percentage width doesn't work well with auto parent */
}
<!-- Solution: explicit parent width -->
.container {
width: 100%; /* explicit width */
height: 400px;
}
.box {
width: 50%; /* now works: 50% of 100% = 50% */
}Containing block for different properties
Property | Relative to containing block |
|---|---|
| Yes (percentage values) |
| Yes (percentage values) |
| Yes (for positioned elements) |
| Yes (position coordinates) |
| No (relative to element's own box) |
| No (relative to element's own box) |
Common containing block problems and solutions
<!-- Problem: percentage width doesn't work -->
<div class="parent">
<div class="child">Child</div>
</div>
.parent {
/* width: auto (default, based on children) */
}
.child {
width: 50%; /* doesn't work as expected */
}
<!-- Solution: give parent explicit width -->
.parent {
width: 100%; /* explicit width */
}
.child {
width: 50%; /* now works */
}
<!-- Problem: absolute positioning goes to viewport -->
<div class="static-parent">
<div class="child">Positioned</div>
</div>
.static-parent {
/* position: static (not a containing block) */
}
.child {
position: absolute;
top: 0;
left: 0; /* positioned to viewport, not parent */
}
<!-- Solution: add position: relative to parent -->
.static-parent {
position: relative; /* creates containing block */
}
.child {
position: absolute;
top: 0;
left: 0; /* now positioned relative to parent */
}
<!-- Problem: fixed element inside transform -->
.parent {
transform: rotate(10deg); /* creates containing block */
}
.fixed-child {
position: fixed;
top: 0;
left: 0; /* positioned to parent, not viewport! */
}
<!-- Solution: don't use transform on parent with fixed children -->
<!-- or remove transform temporarily for specific fixed elements -->