In short
The CSS position property controls where an element sits on the page. Learn static, relative, absolute, fixed and sticky with real examples: a card badge, a sticky header and a back-to-top button. What does the position property do?
The CSS position property controls where an element sits on the page. Learn static, relative, absolute, fixed and sticky with real examples: a card badge, a sticky header and a back-to-top button.
What does the position property do?
By default, the browser places elements in the normal flow: block elements stack from top to bottom, and inline elements sit side by side like words in a sentence. The position property lets you take an element and move it somewhere else, or pin it in place.
position has five values. After you set one, you move the element with the offset properties: top, right, bottom and left. The shorthand inset sets all four at once, just like margin: inset: 0 means top: 0; right: 0; bottom: 0; left: 0.
| Value | Stays in normal flow? | Positioned relative to... |
|---|---|---|
static | Yes | Not positioned (offsets do nothing) |
relative | Yes | Its own normal position |
absolute | No | The nearest positioned ancestor |
fixed | No | The browser window (viewport) |
sticky | Yes | Its scroll container, once it reaches the offset |
static and relative positioning
position: static is the default for every element. The element sits in the normal flow, and top, left and the other offsets are ignored.
position: relative keeps the element in the normal flow, but lets you nudge it away from where it would normally be. The space it originally took stays reserved, so other elements don't move.
.price-tag {
position: relative;
top: -4px;
left: 10px;
}
The element moves 4 pixels up and 10 pixels to the right, but the elements around it act as if it never moved. Nudging is rarely needed, though. The most important job of position: relative is to become the anchor for absolutely positioned children, as you will see next.
How to use position: absolute (badge on a card)
position: absolute removes the element from the normal flow. Other elements behave as if it does not exist, and it can sit on top of them. Its offsets are measured from its containing block: the nearest ancestor that has a position other than static. If there is none, it uses the whole page.
Think of a sticker on a photo frame. The frame (position: relative) is the anchor, and the sticker (position: absolute) is placed in a corner of that frame.
<div class="product-card">
<span class="badge">Sale</span>
<img src="https://picsum.photos/400/250" alt="Running shoes">
<h3>Running Shoes</h3>
<p>$59.00</p>
</div>
.product-card {
position: relative;
width: 280px;
border: 1px solid #e5e7eb;
border-radius: 12px;
padding: 12px;
font-family: system-ui, sans-serif;
}
.product-card img {
width: 100%;
border-radius: 8px;
display: block;
}
.badge {
position: absolute;
top: 20px;
right: 20px;
background: #dc2626;
color: white;
font-size: 0.8rem;
font-weight: bold;
padding: 4px 10px;
border-radius: 999px;
}
A red "Sale" badge appears over the top-right corner of the product image. Because the card has position: relative, the badge's top: 20px; right: 20px is measured from the card's corner, not the page's corner.
Common mistake: Forgetting
position: relativeon the parent. The absolute element then looks for the next positioned ancestor, often finds none, and jumps to the corner of the whole page. If your badge "flies away", check the parent first.
position: fixed and position: sticky
position: fixed also removes the element from the flow, but it is positioned relative to the viewport (the visible part of the browser window). It stays in the same spot on the screen while the page scrolls. A back-to-top button is a perfect example.
position: sticky is a mix. The element scrolls normally until it reaches the offset you set, like top: 0, and then it sticks there while its parent is still on screen. A sticky header is the classic use.
<header class="site-header">CodeCraft</header>
<main class="content">
<p>Scroll down to see the header stick and the button stay in place.</p>
<!-- Add a lot of content here so the page can scroll -->
</main>
<a href="#" class="back-to-top" aria-label="Back to top">↑</a>
.site-header {
position: sticky;
top: 0;
background: #1f2937;
color: white;
padding: 16px 24px;
font-weight: bold;
}
.content {
min-height: 200vh;
padding: 24px;
}
.back-to-top {
position: fixed;
right: 24px;
bottom: 24px;
width: 48px;
height: 48px;
line-height: 48px;
text-align: center;
background: #2563eb;
color: white;
border-radius: 50%;
text-decoration: none;
font-size: 1.4rem;
}
When you scroll down, the dark header moves up until it touches the top of the window, then it stays there. The round blue button sits in the bottom-right corner the whole time. Clicking it jumps back to the top, because href="#" links to the top of the page.
Note:
position: stickyneeds an offset liketop: 0, or it behaves likerelative. It also stops working if a parent element hasoverflow: hiddenoroverflow: auto, because then that parent becomes the scroll container.
Tip: A fixed or sticky header can cover content when you jump to an anchor link. Add
scroll-margin-top: 80px;to your section headings so they land below the header.
Practice exercise
Build a profile card with a round avatar image. Use position: absolute to place a small green "online" dot on the bottom-right edge of the avatar. Then add a fixed "Chat with us" button to the bottom-left corner of the page.
Hint: wrap the avatar <img> in a <div> with position: relative and display: inline-block, then place the dot inside that <div>.
Key takeaways
staticis the default; offsets liketopandlefthave no effect on it.relativenudges an element and, more importantly, becomes the anchor for absolute children.absoluteleaves the normal flow and is positioned from its nearest positioned ancestor.fixedstays in one spot on the screen;stickyscrolls until it hits its offset, then sticks.insetis a shorthand fortop,right,bottomandleft.
Frequently asked questions
What is the difference between absolute and relative position?
A relative element stays in the normal flow and moves from its own normal spot. An absolute element leaves the flow completely and is placed from the corner of its nearest positioned ancestor.
Why is my position: sticky not working?
The three usual reasons are: you forgot an offset like top: 0, a parent has overflow set to hidden, auto or scroll, or the parent is the same height as the sticky element, so there is no room to stick.
How do I center an absolutely positioned element?
Give it inset: 0 and margin: auto with a set width and height. The element is then centered inside its positioned parent.