Positioning
Normally elements stay in the document flow: each next block goes under the previous one, inline elements follow each other in a line. The position property lets you shift an element, lay it on top of others or pin it to the edge of the window.
Values of position
| Value | What it does |
|---|---|
static | The normal place in the flow. The default |
relative | A shift from its normal place |
absolute | The element leaves the flow and is placed by coordinates inside its parent |
fixed | Pinned to the browser window and does not scroll |
sticky | Scrolls with the page, but "sticks" at the edge of the window |
The coordinates are set by top, right, bottom and left — the distance from that edge. They have no effect on static.
relative — a shift in place
An element with position: relative moves, but its old place stays taken, and the neighbours do not move:
<style>
.box { padding: 8px; background: lavender; margin-bottom: 4px; }
.moved { position: relative; top: 10px; left: 40px; background: gold; }
</style>
<div class="box">First</div>
<div class="box moved">Shifted 10px down and 40px right</div>
<div class="box">The third one stayed in place</div>The yellow block overlaps the third one: for the other elements it still stands in its old place.
absolute — by coordinates inside the parent
An element with position: absolute is taken out of the flow and placed by coordinates. They are counted from the nearest ancestor whose position is not static — that is why the parent usually gets position: relative:
<style>
.card { position: relative; padding: 16px; background: whitesmoke; }
.badge { position: absolute; top: 8px; right: 8px; padding: 2px 8px; background: crimson; color: white; }
</style>
<div class="card">
<span class="badge">New</span>
A product card. The badge is pressed into the top right corner.
</div>This is how badges, "close" buttons and captions over pictures are made. To attach an element to a block it does not sit inside, use anchor positioning — see the lesson "Anchor positioning".
inset — all sides at once
The inset property is a shorthand for the four coordinates. inset: 0 stretches an absolute element over the whole parent — handy for a darkening layer:
<style>
.photo { position: relative; height: 100px; background: steelblue; }
.shade { position: absolute; inset: 0; background: rgb(0 0 0 / 0.4); color: white; padding: 12px; }
</style>
<div class="photo">
<div class="shade">A layer over the whole block</div>
</div>fixed and sticky
fixed pins an element to the window: a "back to top" button or a chat in the corner is made this way. sticky keeps an element in place while its container scrolls. Scroll the list in the example:
<style>
.list { height: 140px; overflow: auto; border: 1px solid silver; }
.list h3 { position: sticky; top: 0; margin: 0; padding: 6px 8px; background: lightskyblue; }
.list p { margin: 0; padding: 6px 8px; }
</style>
<div class="list">
<h3>Letter A</h3><p>Anna</p><p>Anton</p><p>Arthur</p>
<h3>Letter B</h3><p>Boris</p><p>Bella</p><p>Ben</p>
<h3>Letter C</h3><p>Clara</p><p>Chris</p>
</div>sticky needs at least one coordinate, such as top: 0 — otherwise there is nowhere to stick.
Common mistake
If no ancestor has a position, an absolute element counts its coordinates from the whole page and flies off into its corner:
<style>
.card { padding: 16px; background: whitesmoke; margin-top: 40px; }
.badge { position: absolute; top: 8px; right: 8px; background: crimson; color: white; }
</style>
<div class="card">
<span class="badge">Where am I?</span>
The card has no position: relative.
</div>The badge ended up in the corner of the whole example, not the card. Give the parent position: relative.