Positioning
Everything so far has followed normal document flow - each box stacks below the last, in source order.
position is how you pull an element out of that flow, or anchor it to something other than "wherever
it landed." Five values, each answering "positioned relative to what?"
The five values
static is the default. No repositioning, top/left/right/bottom do nothing. Every element
is static until you say otherwise.
relative keeps the element in normal flow - it still takes up its original space - but lets you
nudge it with top/left/right/bottom, offset from where it would have been.
}
What just happened: .badge shifted up 4px visually, but the space it originally occupied in the
layout is still reserved - other elements don't move to fill the gap.
absolute removes the element from normal flow entirely and positions it relative to its nearest
ancestor that has a position other than static. If no ancestor qualifies, it falls back to the
<html> element - which is almost never what you want, and the most common reason position: absolute
"doesn't work."
fixed positions relative to the browser viewport itself, ignoring scrolling entirely - it stays
glued to the same spot on screen as the page scrolls underneath it.
sticky behaves like relative until the page scrolls past a threshold you set (top: 0, for
instance), then it locks in place like fixed - but only within its parent's boundaries. Scroll the
parent out of view and the sticky element scrolls away with it.
Worked example 1: a sticky header
Add a header to your About Me page that stays visible while you scroll the bio text below it.
}
What just happened: while the page is scrolled to the top, the header sits in normal flow, exactly
where its HTML position puts it. Scroll down, and the moment the header would scroll past top: 0, it
sticks there instead - staying visible above the content scrolling underneath. No JavaScript, no manual
scroll-tracking.
⚠️ The gotcha. position: sticky silently does nothing if any ancestor has overflow: hidden,
overflow: auto, or a fixed height that clips it - the sticky element can't escape a container that
doesn't let its content overflow. If your sticky header refuses to stick, check every parent up the
tree for an overflow rule.
Worked example 2: a centered modal overlay
A modal needs two things: a dark backdrop covering the whole screen, and a centered box on top of it.
This is the pairing that makes absolute click - a positioned parent, and an absolutely positioned
child anchored to it.
Thanks for visiting my page!
}
}
What just happened: .modal-backdrop uses fixed to cover the entire viewport regardless of scroll
position, dimmed by a semi-transparent black background. .modal uses absolute, and because its
parent .modal-backdrop has position: fixed (not static), the modal positions relative to that
backdrop instead of falling back to <html>. top: 50%; left: 50% puts its top-left corner at the
backdrop's center - then transform: translate(-50%, -50%) shifts it back by half its own width and
height, so the modal's actual center lands on the backdrop's center, not its corner.
💡 Key point. position: absolute only does something useful once you've deliberately given some
ancestor a non-static position to anchor to. That pairing - "positioned parent, absolutely positioned
child" - is the pattern behind dropdowns, tooltips, badges on avatars, and modals alike.
Recap
staticis the default - no offsets apply.relativenudges an element while keeping its original space reserved.absoluteremoves the element from flow and anchors it to the nearest non-static ancestor (or<html>if none exists).fixedanchors to the viewport and ignores scrolling.stickyisrelativeuntil a scroll threshold, then behaves likefixedwithin its parent - breaks silently underoverflow: hiddenon an ancestor.- Centering with
absoluteneeds three things together: a positioned parent,top/left: 50%, andtransform: translate(-50%, -50%).
Test what you just learned:
[
{
"q": "An element has `position: absolute` and none of its ancestors have a position set. What does it position relative to?",
"choices": ["Its immediate parent, always", "The <html> element", "It stays in normal flow like static"],
"answer": 1,
"explain": "absolute anchors to the nearest ancestor with a non-static position. With no qualifying ancestor, it falls back to the html element - the most common cause of absolute positioning behaving unexpectedly."
},
{
"q": "A sticky header isn't sticking - it scrolls away like a normal element. What's the most likely cause?",
"choices": ["sticky doesn't exist as a real value", "An ancestor has overflow: hidden or auto, which breaks sticky", "top: 0 was set instead of top: 100%"],
"answer": 1,
"explain": "position: sticky is silently disabled by an ancestor with overflow set to anything other than visible."
},
{
"q": "Why does the centered modal need `transform: translate(-50%, -50%)` in addition to `top: 50%; left: 50%`?",
"choices": ["top/left alone centers the element's top-left corner, not its actual center", "transform is required for position: absolute to work at all", "It's purely a decorative animation"],
"answer": 0,
"explain": "top: 50%; left: 50% places the corner at the midpoint. Shifting back by half the element's own width and height centers the element itself."
}
]
Positioning moves individual elements around, but it isn't how you build real page layouts - rows, columns, and grids of content are their own tool. That's Flexbox and Grid, the natural next guide from here.
← Phase 3: Colors, Units, and Typography · Guide overview
Check your understanding 3 questions
1. An element has `position: absolute` and none of its ancestors have a position set. What does it position relative to?
2. A sticky header isn't sticking - it scrolls away like a normal element. What's the most likely cause?
3. Why does the centered modal need `transform: translate(-50%, -50%)` in addition to `top: 50%; left: 50%`?