Normally, elements sit in the flow of the page, one after another from top to bottom. The position property lets you step outside that flow. You can pin a badge to the corner of a card, keep a header at the top of the screen while the page scrolls, or float a chat button in the bottom-right corner.
Positioning is powerful but easy to overuse. It's meant for small, specific placements. For general page layout, such as columns and rows, Flexbox and Grid are far more reliable, because positioned elements don't make room for each other.
The five position values
| Value | Moves relative to | Stays in the flow? |
|---|---|---|
static | Nothing; this is the default and offsets have no effect | Yes |
relative | Its own normal position | Yes, and its original space is kept |
absolute | The nearest positioned ancestor | No |
fixed | The browser window | No |
sticky | Acts normally until you scroll to a set point, then sticks | Yes |
Once an element is positioned, use top, right, bottom and left to move it. The shorthand inset: 0 sets all four to 0, which makes an absolute element fill its anchor completely.
The golden rule: absolute inside relative
An absolutely positioned element looks up through its ancestors for the first one whose position isn't static. If it doesn't find one, it positions itself relative to the whole page, which is rarely what you want.
So the classic pattern is:
.card { position: relative; } /* the anchor */
.card .badge { position: absolute; top: 8px; right: 8px; }Setting position: relative on the card without any offsets doesn't move it at all. It simply turns the card into the anchor for the badge.
z-index: which element is on top
When positioned elements overlap, z-index decides the stacking order. A higher number sits closer to the viewer. It only works on positioned elements and on flex or grid children. Keep values simple, such as 1, 10 and 100, so you can still slot something in between later.
If a high z-index seems to have no effect, check the parent. An element can never rise above the stacking level of a parent that creates its own stacking context, for example through opacity or transform.
CSS Position Syntax
Position properties
.parent { position: relative; }
.child { position: absolute; top: 0; right: 0; z-index: 2; }
.header { position: sticky; top: 0; }
.chat { position: fixed; bottom: 20px; right: 20px; }CSS Position Summary
positiontakes elements out of the normal flow for precise placement.relativenudges an element and also acts as an anchor for absolute children.absolutepositions an element inside its nearest positioned ancestor.fixedstays put in the window;stickysticks once you scroll to it.z-indexcontrols which overlapping element appears on top.
CSS Position Examples
Each example runs in the page. Edit the code and press Run to see what changes.
A badge on a card
<div class="card">
<img src="https://picsum.photos/id/431/240/140" alt="Coffee cup" width="240" height="140">
<span class="badge">-20%</span>
<p>Ethiopian Roast</p>
</div>.card {
position: relative;
width: 240px;
font-family: sans-serif;
}
.card img { display: block; border-radius: 12px; }
.badge {
position: absolute;
top: 10px;
right: 10px;
padding: 4px 10px;
border-radius: 999px;
background: #e5484d;
color: white;
font-weight: bold;
}Line-by-line explanation
- Line 2The card becomes the anchor for anything absolutely positioned inside it.
- Line 10The badge leaves the normal flow…
- Lines 11 to 12…and sits 10px from the card's top-right corner.
Sticky header and fixed button
Scroll inside the result. The header sticks; the chat button never moves.
<header class="top">📌 I'm sticky</header>
<p>Scroll down…</p>
<p style="height: 600px; background: linear-gradient(#fff, #e1e6ff);">Lots of content</p>
<button class="chat">💬</button>.top {
position: sticky;
top: 0;
padding: 10px;
background: #15161b;
color: white;
z-index: 10;
}
.chat {
position: fixed;
right: 16px;
bottom: 16px;
width: 52px;
height: 52px;
border-radius: 50%;
border: 0;
background: #17a865;
font-size: 22px;
}Line-by-line explanation
- Lines 2 to 3Behaves normally until it reaches the top of the scroll area, then stays there.
- Line 7Keeps the header above the content as it scrolls past.
- Lines 11 to 13Pinned to the window's bottom-right corner.
relative nudge and z-index
<div class="stack">
<div class="tile a">A (z 1)</div>
<div class="tile b">B (z 3)</div>
<div class="tile c">C (z 2)</div>
</div>.tile {
position: relative;
width: 110px;
padding: 24px 10px;
border: 2px solid #15161b;
font: bold 14px sans-serif;
}
.a { background: #ffe3d7; z-index: 1; }
.b { background: #e1e6ff; top: -30px; left: 40px; z-index: 3; }
.c { background: #fff1c2; top: -60px; left: 80px; z-index: 2; }Line-by-line explanation
- Line 2
relativelets us nudge each tile with top/left and use z-index. - Lines 10 to 11B and C are pulled up to overlap A. B's z-index (3) puts it on top of everything.
Common CSS Position Mistakes
Absolute without a positioned parent
.card .badge { position: absolute; top: 0; right: 0; }
/* the badge flies to the corner of the whole page */.card { position: relative; }
.card .badge { position: absolute; top: 0; right: 0; }Sticky without a threshold
.header { position: sticky; }.header { position: sticky; top: 0; }Sticky needs top (or another side) to know when to stick. It also won't work if an ancestor has overflow: hidden.
CSS Position in Real Projects
Position in real UIs
Notification dots on app icons, "SALE" ribbons, close buttons on modals, dropdown menus, tooltips, sticky table headers, cookie banners and floating "back to top" buttons are all built with position. The sticky header on this site uses position: sticky; top: 0.
CSS Position Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Pin the button to the bottom-right of the screen.
.help {
position: ;
: 20px;
right: 20px;
}
fixed positions relative to the window.An absolute element is positioned relative to…
position: relative to the parent.Which element has a z-index that actually works?
The "NEW" ribbon should sit in the top-left corner of the product box, not the page. Fix it.
<div class="product">
<span class="ribbon">NEW</span>
<p>Trail Sneakers</p>
</div>.product {
width: 200px;
margin: 40px;
padding: 30px 16px 16px;
border: 2px solid #15161b;
}
.ribbon {
position: absolute;
top: 0;
left: 0;
background: #ffd23f;
padding: 2px 8px;
}- The ribbon sits inside the product box
- Still absolutely positioned
CSS Position Quiz
3 questions to check what you learned.
Which position value is the default?
- relative
- absolute
- static
- normal
What does
position: relative; top: 20px;do?- Moves the element 20px up
- Moves it to 20px from the top of the page
- Moves the element 20px down from its normal spot, keeping its original space
- Nothing
Why might
position: stickynot work?- It's not supported anywhere
- An ancestor has
overflow: hidden, or notopvalue is set - It needs z-index
- It only works on headers
CSS Position Challenge
Avatar with an online dot
Make a green "online" dot sit on the bottom-right of the avatar:
.avatarshould be the positioned parent.dotisposition: absolutewithrightandbottomof4px.dotis a 14px green circle
<div class="avatar">
<img src="https://picsum.photos/id/1005/80/80" alt="Sam's profile photo" width="80" height="80">
<span class="dot"></span>
</div>.avatar { width: 80px; }
.avatar img { display: block; border-radius: 50%; }
- Avatar is the positioned parent
- Dot is absolute
- Dot sits near the bottom-right of the avatar
- Dot is a 14px circle
- Dot is green