CSS Positioning: Static, Relative, Absolute, Fixed, Sticky
CSS positioning controls how an element participates in normal flow and which box supplies the coordinates for its offsets. Think of normal flow as assigned seating: every element receives a place in order. The position value decides whether an element stays in that seat, shifts from it, leaves the row, follows the viewport, or stops moving at a scroll threshold.
That seating model makes the five values easier to predict. It also explains why an absolute badge can jump to a page corner and why a sticky heading can stop at the edge of its container.
If you learn by doing, jump to Build a Positioned Card Step by Step first.
What CSS Positioning Changes
The position property works with inset properties such as top, right, bottom, and left. Flow-relative inset forms include inset-block-start and inset-inline-end for components that must follow writing direction. An inset describes an offset from an edge, but the position value determines which edge matters.
static, relative, and sticky keep a place in normal flow. absolute and fixed remove the element from flow, so nearby content lays out without reserving space for it. This distinction causes most visible positioning differences.
Position Static Keeps Normal Flow
static is the default for every element. The browser places the box in its assigned seat, and physical inset properties plus z-index have no effect on an ordinary static box.
.notice {
position: static;
top: 2rem;
} The notice appears exactly where normal flow placed it. The top: 2rem declaration does not move the box because static elements are not positioned elements.
Position Relative Offsets an Element
relative keeps the assigned seat but lets the element slide away from it. Other elements still behave as though the box remained in its original location.
.notice {
position: relative;
top: 0.5rem;
left: 1rem;
} A containing block is the box that supplies the coordinates the child measures its offsets from.
The notice moves down by 0.5rem and right by 1rem. Its original space remains reserved, which can leave a visible gap. A relatively positioned box also becomes a containing block for an absolutely positioned descendant.
Position Absolute Uses a Containing Block
absolute makes the box leave its assigned seat. Its containing block usually comes from the padding box of the nearest ancestor whose position is not static.
.card {
position: relative;
}
.card-badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
} The card becomes the marked row that supplies coordinates, and the badge sits 0.75rem from its top-right corner. The card content does not reserve room for the badge, so padding may be needed to prevent an overlap.
Position Fixed Follows the Viewport
fixed also removes the box from normal flow. In ordinary cases, its containing block is the viewport, so the element remains at the same screen position while the document scrolls.
.back-to-top {
position: fixed;
right: 1rem;
bottom: 1rem;
} The button stays one rem from the viewport's right and bottom edges. Check fixed controls at narrow widths and high zoom because they can cover links, form fields, or text underneath.
Position Sticky Stops at a Threshold
sticky keeps its assigned seat until scrolling reaches a specified inset. The element then stays at that threshold within its nearest scrolling ancestor and containing block.
.section-heading {
position: sticky;
top: 0;
background: #eef3f0;
} The heading scrolls normally until it touches the top threshold, then remains visible while its containing section continues past it. The background prevents scrolling content from showing through the sticky box.
Build a Positioned Card Step by Step
This sequence changes one layout rule at a time. Begin with normal flow, remove the badge from its assigned seat, define its local coordinate system, then add a sticky heading that keeps its original space.
Start with Static Flow
<section class="product-section">
<h2 class="section-heading">New arrivals</h2>
<article class="product-card">
<span class="card-badge">New</span>
<h3>Desk lamp</h3>
<p>Warm light with three brightness levels.</p>
</article>
</section> With no positioning rules, the heading, badge, title, and description appear in source order. Every element has a place in flow, so nothing overlaps.
Take the Badge Out of Flow
.card-badge {
position: absolute;
top: 0.75rem;
right: 0.75rem;
} The badge leaves the document flow, but the card has not supplied a positioning context yet. The browser searches farther up the ancestor chain for a containing block, so the badge may appear near the page corner.
Add a Positioned Ancestor
.product-card {
position: relative;
padding: 1.25rem;
padding-top: 3rem;
} The card now supplies the badge's coordinates, so the same offsets pin it to the card's top-right corner. Extra top padding reserves visual room even though the badge itself occupies no flow space.
Make the Section Heading Sticky
.section-heading {
position: sticky;
top: 0;
z-index: 1;
background: #eef3f0;
} The heading keeps its flow space and scrolls until it reaches the viewport's top edge. It then stays visible within the section. The small z-index keeps card content below the opaque heading in the same stacking context, the group of layers a browser composites together.
Common Pitfalls & Debugging
Absolute Element Uses the Wrong Ancestor
Symptom: the absolute element appears at a page edge instead of inside its card. Cause: no intended ancestor establishes its containing block. Fix: add position: relative to the specific parent that should supply the coordinates.
Z-index Does Not Change the Layer
Symptom: raising the element's z-index value produces no visible change. Cause: the element is an ordinary static box, or it sits inside a lower stacking context. Fix: position the element and inspect ancestor stacking contexts. Flex and grid items are the main exception because their z-index can work without a non-static position.
Sticky Does Not Stick
Symptom: the sticky box scrolls away or sticks inside a much smaller area. Cause: an ancestor with overflow: hidden, auto, or scroll became the nearest scrolling ancestor. Fix: remove the unnecessary overflow rule or place the sticky element inside the intended scroll container.
Top or Left Does Nothing
Symptom: an inset declaration has no visible effect. Cause: the element still uses position: static as part of normal flow. Fix: choose relative, absolute, fixed, or sticky according to the layout behavior you need.
Positioning Best Practices
- Keep document structure in normal flow, flexbox, or grid so content can grow without unexpected overlaps.
- Give an absolute element a deliberate containing block and reserve visual space when the overlay could cover text.
- Use the smallest practical
z-indexwithin a known stacking context instead of escalating to arbitrary large numbers. - Test fixed and sticky elements with zoom, long content, narrow screens, and keyboard focus before shipping them.
Frequently Asked Questions
Does sticky positioning need a scrolling ancestor to be declared?
Not explicitly. Sticky uses the nearest ancestor that can scroll, which is often the document itself, so most sticky headers need no extra setup. It fails silently when an ancestor has overflow hidden.
Can flexbox or grid replace CSS positioning?
Flexbox and grid should handle most structural layout because they keep items coordinated with surrounding content. Positioning remains useful for overlays, corner badges, fixed controls, and sticky headings. These systems often work together inside the same component.
Should you use physical or logical inset properties?
Physical properties such as top and right are clear for viewport-based placement. Logical properties such as inset-block-start and inset-inline-end adapt to different writing modes and suit components intended for multilingual layouts.
Does z-index affect where an absolute element is positioned?
No. z-index only orders elements within a stacking context. Which ancestor supplies the coordinates for an absolutely positioned descendant is decided entirely by position, and changing z-index will not move it.
MDN Resources
- The CSS
positionproperty - CSS positioning tutorial
- Layout and the containing block
- Understanding
z-index
Related CSS Layout Guides
- Return to the CSS layout guide for the category overview.
- Read CSS display and flow to understand the normal flow that positioning modifies.
- Use CSS flexbox for one-dimensional alignment that stays in flow.
- Use CSS grid for coordinated rows and columns.
Sources
Read Next
How CSS selectors target elements: type, class, and id selectors, combinators, pseudo-classes, and how specificity decides which rule wins.
How CSS arranges boxes on the page: normal flow, flexbox for one axis, grid for two, and positioning to lift elements out of flow.
Learn CSS typography and color through color formats, length units, font and text properties, custom properties, and shadows.