By WebNest Studio Engineering Team
CSS Tutorial
CSS Positioning
The position property controls how an element is placed in the document and how it responds to the top, right, bottom, and left offset properties. Positioning is one of the more conceptually tricky parts of CSS because each value changes not just where the element itself sits, but how it interacts with the normal flow of surrounding content.
There are five values to know: static, relative, absolute, fixed, and sticky. Each removes the element from — or keeps it in — normal document flow differently, and each establishes offsets relative to a different reference point.
Why it matters
Badges on icons, dropdown menus, modals, sticky headers and tooltips all depend on the position property. Most confusion comes from one rule: an absolutely positioned element is placed relative to its nearest positioned ancestor. Once that is clear, positioning stops being trial and error.
static and relative
position: static is the default for every element — it sits in normal document flow, and top/right/bottom/left have no effect on it. position: relative also stays in normal flow (it still takes up its original space), but now top/right/bottom/left can nudge it visually away from that original position, without affecting the layout of surrounding elements. Relative positioning is also commonly used just to establish a new containing block for an absolutely positioned child.
absolute and fixed
position: absolute removes the element from normal flow entirely, so surrounding elements act as if it isn't there, and it is positioned relative to its nearest ancestor that has a position value other than static (its "containing block"). If no such ancestor exists, it positions relative to the initial containing block (roughly the page). position: fixed also removes the element from flow, but always positions it relative to the browser viewport, so it stays in place even when the page scrolls — commonly used for sticky headers or floating action buttons.
sticky
position: sticky behaves like relative positioning until the element reaches a specified threshold (e.g. top: 0) during scrolling, at which point it "sticks" and behaves like fixed positioning until its containing block scrolls out of view. It is commonly used for section headers that should stay visible while their section is on screen, then scroll away naturally once the next section begins.
Examples
Absolute positioning relative to a relatively positioned parent
<!--
<div class="card">
<span class="badge">New</span>
<p>Card content...</p>
</div>
-->
.card {
position: relative; /* establishes the containing block */
padding: 1rem;
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}
The "New" badge is pinned to the top-right corner of the card, positioned relative to the card itself rather than the page, because .card establishes the containing block.
A sticky section header that stays visible while scrolling through its section
.section-header {
position: sticky;
top: 0;
background: white;
padding: 0.5rem 1rem;
}
The header scrolls normally with the page until it reaches the top of the viewport, then sticks there while the rest of its section scrolls underneath, releasing once the section ends.
Common Mistakes
- Setting position: absolute on a child without giving any ancestor position: relative, causing the child to jump to the page level instead of staying within its intended container.
- Using position: fixed for an element the user expects to scroll with a specific section, rather than the whole page.
- Forgetting that position: sticky requires a defined offset (like top: 0) to activate, and that it only works within the bounds of its containing block.
- Assuming position: relative removes an element from normal flow the way absolute and fixed do — it does not; the original space is preserved.
Exercise
Place the button in the top-right corner of main, 10px from the top and 10px from the right. It must be positioned relative to main, not to the whole page. Give main a border and some padding so that the corner is visible.
Expected result: the button sits in the top-right corner inside the bordered box.
/* The page contains: <main> with an <h1>, a <p> and a <button> */
main {
border: 2px solid gray;
padding: 20px;
/* TODO: make main the reference for its positioned children */
}
button {
/* TODO: take the button out of the flow and place it in the corner */
}
Show hints
- An absolutely positioned element looks for the nearest ancestor whose
positionis notstatic. position: relativeonmain, with no offsets, makes it that ancestor without moving it.
Show solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */
main {
border: 2px solid gray;
padding: 20px;
position: relative;
}
button {
position: absolute;
top: 10px;
right: 10px;
}
Quick quiz
1. What is the default value of position?
- A. static
- B. relative
- C. absolute
- D. fixed
Show answer
A. A static element is in the normal flow, and top, right, bottom and left have no effect on it.
2. What is a position: fixed element positioned relative to?
- A. Its parent
- B. The viewport
- C. The previous element
- D. The document
Show answer
B. It stays in the same place on screen when the page scrolls.
3. What happens to the space of an element with position: relative; top: 20px?
- A. Other elements move down by 20px
- B. The space is removed
- C. Its original space in the flow is kept, and it is drawn 20px lower
- D. It leaves the flow completely
Show answer
C. A relatively positioned element is shifted visually without affecting its neighbours.
Interview questions
Explain the values of the position property.
Show answer
static is the default: the element is in the normal flow. relative keeps the element's place in the flow and offsets it from that place. absolute removes it from the flow and positions it relative to the nearest positioned ancestor. fixed removes it from the flow and positions it relative to the viewport. sticky behaves as relative until the element reaches a scroll threshold such as top: 0, then stays there while its container is in view.
Why might z-index not work?
Show answer
z-index only applies to positioned elements, meaning those with a position other than static, and to flex and grid items. It also works only within a stacking context: an element cannot rise above something outside its own stacking context, whatever its value. Properties such as opacity below 1, transform and position: fixed create a new stacking context, which is the usual cause of the surprise.
Key Points to Remember
- static is the default and ignores offset properties; relative stays in flow but can be nudged visually.
- absolute removes an element from flow and positions it relative to the nearest positioned ancestor.
- fixed removes an element from flow and positions it relative to the viewport, staying in place during scroll.
- sticky toggles between relative and fixed-like behavior based on scroll position and a defined threshold.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering