Positioning lets you break the normal flow of the page. You can stick a header to the top, place a badge in the corner of a card, or make a popup float over everything. Each position value changes how an element behaves in its own way.
Static (The Default)
Every element starts with position: static. It just sits where it naturally falls in the document flow. No funny business:
.box {
position: static;
/* this is the default โ you rarely write this */
}
Try it Yourself โ
Static elements ignore the top, right, bottom, and left properties. They follow the normal layout like good citizens.
Relative
position: relative keeps the element in the normal flow, but lets you nudge it around using top, right, bottom, and left:
.box {
position: relative;
top: 20px;
left: 30px;
}
The element moves relative to where it would normally be. The original space it occupied is preserved โ other elements do not fill it in.
Absolute
position: absolute takes the element out of the normal flow entirely. It positions itself relative to the nearest positioned ancestor (an ancestor with a position other than static):
.card {
position: relative;
}
.badge {
position: absolute;
top: 0;
right: 0;
}
This places the badge in the top-right corner of the card. If no ancestor is positioned, absolute elements position themselves relative to the <html> element.
Fixed
position: fixed pins an element to the viewport. It stays in the same spot even when you scroll:
.navbar {
position: fixed;
top: 0;
width: 100%;
}
Fixed elements are great for sticky headers, cookie consent banners, and back-to-top buttons. Just be careful not to cover your content โ you will need padding on the body to compensate.
Sticky
position: sticky is like a mix of relative and fixed. The element scrolls normally until it hits a certain point, then sticks:
.section-header {
position: sticky;
top: 0;
}
As you scroll down, the section header stays at the top of the viewport until its container scrolls past. This is perfect for table headers or category labels in a list.