Positioning
Prerequisites: The box model
position takes an element out of the normal flow (or nudges it within
it). The offset properties top, right, bottom, left (or
inset for all four) then move it.
| Value | Positioned relative to | Leaves a gap in the flow? |
|---|---|---|
static |
— (the default; offsets ignored) | — |
relative |
Its own normal position | Yes |
absolute |
The nearest positioned ancestor | No |
fixed |
The viewport | No |
sticky |
Normal flow, until it hits an offset while scrolling | Yes |
Absolute inside relative
The classic pattern: a relative parent becomes the containing
block for absolute children.
<style>
body { margin: 0; }
.card { position: relative; width: 300px; height: 200px; margin: 50px; }
.badge { position: absolute; top: 10px; right: 10px; width: 60px; height: 20px; }
.cover { position: absolute; inset: 0; }
</style>
<div class="card">
<span class="badge">New</span>
<a class="cover" href="/product" aria-label="View product"></a>
</div>
<script>
const r = sel => document.querySelector(sel).getBoundingClientRect();
console.log("card:", r(".card").left, r(".card").top);
console.log("badge:", r(".badge").left, r(".badge").top);
console.log("cover:", r(".cover").width, "x", r(".cover").height);
</script>
card: 50 50
badge: 280 60
cover: 300 x 200
- The badge sits 10px from the card's top-right corner: 50 + 300 − 10 − 60 = 280px from the left of the page.
inset: 0stretched the link over the entire card — a common way to make a whole card clickable.- Without
position: relativeon the card, both would be positioned against the page instead.
Fixed and sticky
.cookie-banner { position: fixed; inset-inline: 0; bottom: 0; }
.site-header { position: sticky; top: 0; z-index: 10; background: white; }
th { position: sticky; top: 0; }
fixedelements stay put while the page scrolls. Make sure they don't cover content — especially on small screens and when zoomed.stickyelements scroll normally until they reach the offset, then stick — within their parent. It needs an offset (top: 0) and fails if an ancestor hasoverflow: hiddenorauto.
Centering an absolute element
.modal {
position: fixed;
inset: 0;
margin: auto;
width: min(30rem, 90vw);
height: fit-content;
}
With inset: 0 and margin: auto, the browser centers a box of known
size. (The native dialog element does this for you.)
Don't lay out pages with position
Absolute positioning removes elements from the flow, so nothing reacts to
their size — text overlaps as soon as content changes. Use Flexbox and
Grid for layout, and position for overlays, badges, tooltips, and
sticky headers.