The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
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

01

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.

Compare static and relative boxes
.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.

Back to quick reference ↑
02

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.

Badge in the logical top-end corner
<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>
Back to quick reference ↑
03

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.

A viewport help control
.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; } }
Back to quick reference ↑
04

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.

Sticky section headings inside a panel
<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>
Back to quick reference ↑
05

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.

A compact application layer scale
: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); }
Back to quick reference ↑
06

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.

Contain component-internal layers
.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; }
Back to quick reference ↑
07

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.

Contained dialog presentation
<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.

Back to quick reference ↑

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.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. World Wide Web ConsortiumCSS Positioned Layout Module Level 3w3.org
  2. World Wide Web ConsortiumCSS Positioned Layout Module Level 4w3.org
  3. MDN Web DocsStacking contextdeveloper.mozilla.org
  4. MDN Web DocsUnderstanding z-indexdeveloper.mozilla.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback