The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Use normal positioning | .item { position: static; } | View examples |
| Offset without leaving flow | .item { position: relative; inset-block-start: .5rem; } | View examples |
| Position inside an ancestor | .badge {
position: absolute;
inset-block-start: .5rem;
inset-inline-end: .5rem;
} | View examples |
| Establish a containing block | .card { position: relative; } | View examples |
| Fill a containing block | .overlay { position: absolute; inset: 0; } | View examples |
| Use flow-relative edges | .badge {
inset-block-start: .5rem;
inset-inline-end: .5rem;
} | View examples |
| Fix to the viewport | .help {
position: fixed;
inset-block-end: 1rem;
inset-inline-end: 1rem;
} | View examples |
| Make a heading sticky | .heading { position: sticky; inset-block-start: 0; } | View examples |
| Define a scroll container | .panel { max-block-size: 20rem; overflow: auto; } | View examples |
| Set a positioned stack level | .popover { position: absolute; z-index: 10; } | View examples |
| Isolate component stacking | .component { isolation: isolate; } | View examples |
| Recognize a transform context | .panel { transform: translateZ(0); } | View examples |
| Recognize opacity stacking | .panel { opacity: .99; } | View examples |
| Center an overlay item | .dialog {
position: absolute;
inset: 50% auto auto 50%;
transform: translate(-50%, -50%);
} | View examples |
| Cover a container | .scrim {
position: absolute;
inset: 0;
background: rgb(0 0 0 / .55);
} | View examples |
| Layer dialog content | .dialog { position: relative; z-index: 1; } | View examples |
Use normal flow, flexbox, or grid for primary layout and positioning for overlays, badges, sticky controls, and deliberate offsets. Most z-index surprises come from the containing block or stacking context, so diagnose those relationships before increasing numbers.
Step by step
Detailed examples
Choose positioning only for intentional exceptions to flow
static participates normally and ignores insets. relative keeps its layout footprint but paints at an offset, so other boxes do not move into the vacated visual area. absolute and fixed are removed from flow and may overlap content. Use grid or flexbox when surrounding elements should respond to the box's size.
.static-note { position: static; }
.relative-note {
position: relative;
inset-block-start: .5rem;
inset-inline-start: 1rem;
} Note: The relative note's original space remains reserved.
Anchor absolute boxes to the intended ancestor
An absolutely positioned box uses its containing block to resolve insets and percentages. A common pattern is position:relative on the component root without any offset, then absolute positioning on a child. Transforms, containment, and other properties can also establish containing blocks, so inspect ancestors when an overlay attaches somewhere unexpected.
<article class="card">
<h2>Release notes</h2>
<span class="badge">New</span>
</article>
<style>
.card { position: relative; padding: 2rem 1rem 1rem; }
.badge {
position: absolute;
inset-block-start: .5rem;
inset-inline-end: .5rem;
}
</style> Keep fixed controls reachable and unobtrusive
fixed positioning normally uses the viewport and stays visible while the document scrolls. A transformed ancestor can instead establish its fixed containing block. Reserve space for fixed headers, account for small screens and safe areas, and avoid covering content, browser zoom targets, or consent controls. Print styles often need to restore static positioning.
.help-button {
position: fixed;
inset-block-end: max(1rem, env(safe-area-inset-bottom));
inset-inline-end: max(1rem, env(safe-area-inset-right));
z-index: 20;
}
@media print { .help-button { display: none; } } Define both a sticky threshold and scroll context
sticky behaves like relative positioning until a threshold such as inset-block-start is reached, then remains constrained within its containing block and nearest scrollport. It needs a non-auto inset on the axis that should stick. Ancestor overflow, insufficient scrolling space, or a short containing block commonly explains why it appears not to work.
<div class="panel">
<section><h2>Today</h2><p>Several rows of content…</p></section>
<section><h2>Yesterday</h2><p>Several rows of content…</p></section>
</div>
<style>
.panel { max-block-size: 20rem; overflow: auto; }
.panel h2 {
position: sticky;
inset-block-start: 0;
margin: 0;
background: Canvas;
}
</style> Apply z-index within the correct stacking context
z-index does not create one global page-wide ladder. It orders an element inside its stacking context, and the entire context is then painted as one unit among its siblings. For positioned boxes, an integer z-index establishes a local stacking context. Prefer a small documented layer scale over arbitrary ever-growing values.
:root {
--layer-header: 10;
--layer-popover: 20;
--layer-modal: 30;
}
.site-header { position: sticky; inset-block-start: 0; z-index: var(--layer-header); }
.popover { position: absolute; z-index: var(--layer-popover); }
.modal { position: fixed; inset: 0; z-index: var(--layer-modal); } Find the ancestor trapping a z-index value
The root is a stacking context, and many features create more: positioned integer z-index, fixed or sticky positioning, opacity below one, transforms, filters, isolation:isolate, and several containment modes. A child cannot escape its ancestor's context no matter how large its z-index becomes. Remove an accidental context or move the overlay to an appropriate ancestor rather than inflating the number.
.media-card {
position: relative;
isolation: isolate;
}
.media-card__image { position: relative; z-index: 0; }
.media-card__scrim { position: absolute; inset: 0; z-index: 1; }
.media-card__content { position: relative; z-index: 2; } Build overlays as one isolated layer system
A component overlay usually needs a positioned root, an inset scrim, and content above it. isolation:isolate contains those layer numbers. Centering with a transform works for unknown dimensions, but grid place-items:center is often simpler when the overlay wrapper can participate in layout. Modal behavior also requires focus management, dismissal, and background inertness beyond CSS.
<div class="overlay" role="presentation">
<div class="scrim"></div>
<section class="dialog" role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm change</h2>
<button type="button">Cancel</button>
</section>
</div>
<style>
.overlay { position: fixed; inset: 0; isolation: isolate; display: grid; place-items: center; }
.scrim { position: absolute; inset: 0; z-index: 0; background: rgb(0 0 0 / .55); }
.dialog { position: relative; z-index: 1; max-inline-size: 32rem; background: Canvas; }
</style> Note: JavaScript must manage focus, Escape dismissal, and restoration for a complete modal.
Local code tester
Try CSS positioning and stacking
Move a badge, sticky heading, and layered overlay while observing their containing blocks and local stack order.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



