Positioning: Static, Relative, Absolute, Fixed, Sticky
Introduction to CSS Positioning
CSS positioning is one of the most fundamental and powerful layout mechanisms available to web developers. While modern tools like Flexbox and Grid handle most layout tasks, the position property remains essential for controlling how individual elements are placed in the document, how they interact with the normal flow, and how they relate to their containing blocks. Whether you are building a fixed navigation bar, a tooltip that appears near a button, a sticky sidebar that follows the user as they scroll, or a modal overlay that covers the entire screen, the position property is your primary tool.
The position property accepts five values: static, relative, absolute, fixed, and sticky. Each value fundamentally changes how the element is placed in the document, whether it remains in the normal flow, what it uses as its reference point for offset properties, and how it interacts with surrounding elements. Understanding each of these values deeply -- including the subtle differences between them -- is critical for building robust, predictable layouts.
In this lesson, we will explore each positioning value in detail, study the offset properties (top, right, bottom, left) that work alongside them, understand the concept of containing blocks, and build practical examples that demonstrate real-world usage patterns.
The Normal Flow and position: static
Before you can understand the other positioning values, you need a solid grasp of the normal document flow. When a browser renders HTML, it places elements on the page according to the normal flow rules. Block-level elements like <div>, <p>, and <h1> stack vertically from top to bottom, each taking up the full available width. Inline elements like <span>, <a>, and <strong> flow horizontally within their parent, wrapping to the next line when they run out of space.
The default value of the position property is static. An element with position: static is said to be "not positioned" -- it participates fully in the normal flow. The offset properties (top, right, bottom, left) have absolutely no effect on a statically positioned element. The z-index property also has no effect on static elements.
Example: Static Positioning (Default Behavior)
<div class="container">
<div class="box box-a">Box A</div>
<div class="box box-b">Box B</div>
<div class="box box-c">Box C</div>
</div>
/* CSS */
.box {
position: static; /* This is the default -- you never need to write this */
top: 50px; /* Has NO effect on a static element */
left: 100px; /* Has NO effect on a static element */
width: 200px;
height: 100px;
margin-bottom: 10px;
background-color: #3498db;
color: white;
padding: 20px;
}
/* Result: All three boxes stack vertically in normal flow.
The top and left declarations are completely ignored. */
position: static explicitly, since it is the default. However, you might use it to reset an element's position back to the default if a media query or class previously set it to something else.position: relative
When you set position: relative on an element, two important things happen. First, the element remains in the normal document flow -- it still occupies its original space, and surrounding elements behave as if the relative element has not moved at all. Second, the element can now be offset from its original position using the top, right, bottom, and left properties. The offset is relative to where the element would have been in the normal flow.
Think of relative positioning as the element leaving a "ghost" in its original spot while the visible element shifts to a new visual position. Other elements do not reflow to fill the gap left behind -- they continue to respect the original space.
Example: Relative Positioning
<div class="container">
<div class="box box-a">Box A</div>
<div class="box box-b">Box B (Relative)</div>
<div class="box box-c">Box C</div>
</div>
/* CSS */
.box {
width: 200px;
height: 80px;
margin-bottom: 10px;
padding: 15px;
background-color: #3498db;
color: white;
}
.box-b {
position: relative;
top: 20px; /* Moves 20px DOWN from its normal position */
left: 40px; /* Moves 40px RIGHT from its normal position */
background-color: #e74c3c;
}
/* Result: Box B visually shifts 20px down and 40px right,
but Box C stays exactly where it would be if Box B had not moved.
There is a visible gap where Box B originally was. */
The offset properties work as follows for relatively positioned elements: top: 20px pushes the element 20 pixels down from its original position, left: 40px pushes it 40 pixels to the right, bottom: 20px would push it 20 pixels up, and right: 40px would push it 40 pixels to the left. If both top and bottom are specified, top wins. If both left and right are specified, the direction depends on the writing mode -- in left-to-right languages, left wins.
position: relative is not to move the element itself at all, but to create a positioning context for absolutely positioned child elements. We will explore this pattern extensively in the absolute positioning section.Creating a Positioning Context
A critically important behavior of position: relative is that it establishes a new positioning context. This means that any descendant element with position: absolute will use this relatively positioned ancestor as its reference point, rather than the viewport or a more distant ancestor. This is the primary reason developers use position: relative in practice.
Example: Relative Parent as Positioning Context
.card {
position: relative; /* Creates positioning context */
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card .badge {
position: absolute; /* Positioned relative to .card */
top: -10px;
right: -10px;
background: #e74c3c;
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
/* The badge appears 10px above and 10px to the right of the card's
top-right corner, creating a notification badge effect. */
position: absolute
Absolute positioning is one of the most powerful and frequently misunderstood positioning values. When you set position: absolute on an element, the element is completely removed from the normal document flow. It no longer occupies any space in the layout, and surrounding elements behave as if it does not exist. The element is then positioned relative to its nearest positioned ancestor -- that is, the closest ancestor that has a position value of relative, absolute, fixed, or sticky. If no positioned ancestor exists, the element is positioned relative to the initial containing block, which is typically the viewport.
Example: Absolute Positioning Basics
<div class="parent">
<div class="sibling">I am a sibling</div>
<div class="absolute-box">I am absolute</div>
<div class="sibling">I am another sibling</div>
</div>
/* CSS */
.parent {
position: relative; /* Positioning context for the absolute child */
width: 400px;
height: 300px;
background: #ecf0f1;
padding: 20px;
}
.sibling {
background: #3498db;
color: white;
padding: 10px;
margin-bottom: 10px;
}
.absolute-box {
position: absolute;
top: 20px;
right: 20px;
width: 150px;
padding: 15px;
background: #e74c3c;
color: white;
}
/* Result: The absolute-box is removed from the flow. The two siblings
stack normally as if the absolute box does not exist. The absolute-box
is placed 20px from the top and 20px from the right edge of .parent. */
position: relative (or another positioning value) on the intended parent element, an absolutely positioned child will "escape" and position itself relative to the viewport or the nearest positioned ancestor further up the DOM tree. This is one of the most common CSS layout bugs. Always make sure you have established the correct positioning context.The Containing Block for Absolute Elements
The containing block for an absolutely positioned element is determined by its nearest positioned ancestor. The element's offset properties (top, right, bottom, left) are calculated relative to the edges of this containing block's padding box. This means that if the parent has padding, the absolute element's offsets are measured from the inner edge of the padding, not the border.
Example: Containing Block Demonstration
.outer {
position: relative;
width: 500px;
height: 400px;
padding: 30px;
border: 3px solid #333;
background: #f0f0f0;
}
.inner-absolute {
position: absolute;
top: 0;
left: 0;
/* This element sits at the top-left corner of .outer's PADDING box,
which is inside the border but at the start of the padding area. */
background: rgba(231, 76, 60, 0.8);
padding: 10px;
}
.inner-absolute-bottom-right {
position: absolute;
bottom: 0;
right: 0;
/* This element sits at the bottom-right corner of .outer's padding box. */
background: rgba(52, 152, 219, 0.8);
padding: 10px;
}
Centering with Absolute Positioning
One of the most classic CSS techniques is centering an element both horizontally and vertically using absolute positioning. While Flexbox and Grid have made centering much easier, this technique remains useful when you need to center an overlay or a specific element within a positioned container without affecting surrounding layout.
Example: Centering with position: absolute
/* Method 1: Using transform */
.centered-transform {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Method 2: Using inset and auto margins */
.centered-inset {
position: absolute;
inset: 0;
margin: auto;
width: 200px; /* Must have explicit width */
height: 100px; /* Must have explicit height */
}
/* Method 3: Using all four offsets with auto margins (older syntax) */
.centered-offsets {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 200px;
height: 100px;
}
/* Practical example: centering a modal */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
padding: 40px;
border-radius: 12px;
max-width: 500px;
width: 90%;
}
Practical Uses of Absolute Positioning
Absolute positioning is ideal for elements that need to be precisely placed without affecting the flow of other content. Common use cases include notification badges, tooltips, dropdown menus, image overlays, decorative elements, and close buttons on modals or alerts.
Example: Tooltip with Absolute Positioning
/* Tooltip component */
.tooltip-wrapper {
position: relative;
display: inline-block;
}
.tooltip-text {
position: absolute;
bottom: calc(100% + 8px); /* 8px gap above the trigger element */
left: 50%;
transform: translateX(-50%);
background: #333;
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
pointer-events: none;
}
/* Arrow on the tooltip */
.tooltip-text::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
}
.tooltip-wrapper:hover .tooltip-text {
opacity: 1;
visibility: visible;
}
/* Image overlay example */
.image-card {
position: relative;
overflow: hidden;
border-radius: 8px;
}
.image-card img {
display: block;
width: 100%;
height: auto;
}
.image-card .overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
display: flex;
align-items: flex-end;
padding: 20px;
color: white;
}
position: fixed
Fixed positioning works similarly to absolute positioning in that the element is removed from the normal document flow. However, instead of being positioned relative to a positioned ancestor, a fixed element is positioned relative to the viewport -- the visible area of the browser window. This means the element stays in the same visual position even when the user scrolls the page. Fixed positioning is commonly used for navigation bars, back-to-top buttons, cookie consent banners, and chat widgets.
Example: Fixed Navigation Bar
/* Fixed navigation bar at the top */
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0; /* Stretch to full width */
height: 64px;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
padding: 0 24px;
z-index: 1000; /* Stay above other content */
}
/* Add padding to body to prevent content from hiding behind the fixed navbar */
body {
padding-top: 64px;
}
/* Fixed back-to-top button */
.back-to-top {
position: fixed;
bottom: 24px;
right: 24px;
width: 48px;
height: 48px;
border-radius: 50%;
background: #3498db;
color: white;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 999;
transition: opacity 0.3s, transform 0.3s;
}
.back-to-top:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
/* Fixed cookie consent banner */
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #2c3e50;
color: white;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
z-index: 2000;
}
position: fixed. If any ancestor of the fixed element has a transform, perspective, or filter property set to anything other than none, the fixed element will be positioned relative to that ancestor instead of the viewport. This can cause confusing behavior where a "fixed" element scrolls with the page. Always check ancestor elements if your fixed positioning is not behaving as expected.Fixed vs Absolute: Key Differences
While both fixed and absolute elements are removed from the normal flow, the critical difference is their reference point. An absolute element is positioned relative to its nearest positioned ancestor, while a fixed element is positioned relative to the viewport (with the caveat about transform ancestors). Additionally, a fixed element does not move when the page is scrolled, whereas an absolute element scrolls with its containing block.
Example: Comparing Fixed and Absolute
.container {
position: relative;
height: 2000px; /* Tall container to enable scrolling */
padding: 20px;
background: #f0f0f0;
}
.absolute-element {
position: absolute;
top: 20px;
right: 20px;
padding: 15px;
background: #e74c3c;
color: white;
/* This element stays 20px from the top-right corner of .container.
When you scroll down, it scrolls away with the container. */
}
.fixed-element {
position: fixed;
top: 20px;
right: 20px;
padding: 15px;
background: #3498db;
color: white;
/* This element stays 20px from the top-right corner of the VIEWPORT.
It remains visible in the same spot no matter how far you scroll. */
}
position: sticky
Sticky positioning is a hybrid between relative and fixed positioning, introduced to solve a common UI pattern that previously required JavaScript. A sticky element behaves like a relatively positioned element until the user scrolls past a specified threshold, at which point it becomes "stuck" and behaves like a fixed element within the bounds of its containing block. Once the containing block scrolls out of view, the sticky element scrolls away with it.
The threshold is defined using the offset properties -- typically top, but bottom, left, and right also work. A top: 0 value means the element becomes sticky when its top edge reaches the top of the viewport. A top: 20px value means it becomes sticky when its top edge is 20 pixels from the top of the viewport.
Example: Sticky Header
/* Sticky section headers */
.section-header {
position: sticky;
top: 0;
background: white;
padding: 16px 24px;
font-size: 18px;
font-weight: bold;
border-bottom: 2px solid #3498db;
z-index: 10;
}
/* HTML structure */
<div class="section">
<h2 class="section-header">Section 1: Introduction</h2>
<p>Content for section 1...</p>
<p>More content...</p>
</div>
<div class="section">
<h2 class="section-header">Section 2: Getting Started</h2>
<p>Content for section 2...</p>
<p>More content...</p>
</div>
<div class="section">
<h2 class="section-header">Section 3: Advanced Topics</h2>
<p>Content for section 3...</p>
<p>More content...</p>
</div>
/* Each header sticks to the top as you scroll through its section,
then gets pushed away when the next section header arrives. */
Requirements for Sticky Positioning
Sticky positioning has several requirements that, if not met, will prevent it from working. Understanding these requirements helps you debug sticky positioning issues:
- At least one offset property must be set: You must specify at least one of
top,right,bottom, orleft. Without an offset, the browser does not know when to trigger the sticky behavior. - The parent must have scrollable content: The parent container must be tall enough that its content overflows the viewport. If the parent is exactly the same height as the sticky element, there is nothing to scroll, so nothing sticks.
- No ancestor with overflow: hidden, auto, or scroll: If any ancestor between the sticky element and the scroll container has
overflow: hidden,overflow: auto, oroverflow: scrollset, the sticky behavior will be constrained to that ancestor's scroll area, which may not produce the expected result. This is the most common reason sticky positioning fails unexpectedly.
Example: Sticky Sidebar
/* Two-column layout with a sticky sidebar */
.page-layout {
display: flex;
gap: 30px;
align-items: flex-start; /* Important: prevents sidebar from stretching */
}
.main-content {
flex: 1;
/* Long content that causes scrolling */
}
.sidebar {
position: sticky;
top: 80px; /* 64px navbar height + 16px gap */
width: 300px;
flex-shrink: 0;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
max-height: calc(100vh - 96px); /* Viewport height minus offset */
overflow-y: auto; /* Allow sidebar scrolling if content is too tall */
}
/* Sticky table header */
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table thead th {
position: sticky;
top: 64px; /* Below a fixed navbar */
background: #2c3e50;
color: white;
padding: 12px 16px;
text-align: left;
z-index: 5;
}
.data-table tbody td {
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
The Offset Properties: top, right, bottom, left
The offset properties determine how far an element is shifted from a specific edge of its containing block. They only take effect on positioned elements (those with a position value other than static). The behavior of these properties varies depending on the position value.
Summary: How Offsets Work with Each Position Value
/* STATIC: Offsets are ignored entirely */
.static-el {
position: static;
top: 50px; /* Ignored */
left: 100px; /* Ignored */
}
/* RELATIVE: Offsets shift the element from its normal position */
.relative-el {
position: relative;
top: 20px; /* Move 20px down from normal position */
left: 30px; /* Move 30px right from normal position */
}
/* ABSOLUTE: Offsets position from the nearest positioned ancestor's padding box */
.absolute-el {
position: absolute;
top: 0; /* Align to the top of the containing block */
right: 0; /* Align to the right of the containing block */
}
/* FIXED: Offsets position from the viewport edges */
.fixed-el {
position: fixed;
bottom: 24px; /* 24px from the bottom of the viewport */
right: 24px; /* 24px from the right of the viewport */
}
/* STICKY: Offsets define the sticking threshold */
.sticky-el {
position: sticky;
top: 0; /* Stick when the top edge reaches the viewport top */
}
The inset Shorthand Property
The inset property is a shorthand for setting top, right, bottom, and left simultaneously. It follows the same shorthand pattern as margin and padding: one value sets all four sides, two values set top/bottom and left/right, three values set top, left/right, and bottom, and four values set each side individually in clockwise order.
Example: Using the inset Shorthand
/* inset: all four sides */
.full-overlay {
position: absolute;
inset: 0;
/* Equivalent to: top: 0; right: 0; bottom: 0; left: 0; */
background: rgba(0, 0, 0, 0.5);
}
/* inset: vertical horizontal */
.padded-overlay {
position: absolute;
inset: 20px 30px;
/* Equivalent to: top: 20px; right: 30px; bottom: 20px; left: 30px; */
}
/* inset: top horizontal bottom */
.custom-overlay {
position: absolute;
inset: 10px 20px 30px;
/* Equivalent to: top: 10px; right: 20px; bottom: 30px; left: 20px; */
}
/* inset: top right bottom left */
.specific-overlay {
position: absolute;
inset: 10px 20px 30px 40px;
/* Equivalent to: top: 10px; right: 20px; bottom: 30px; left: 40px; */
}
/* Practical: Full-screen modal backdrop */
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
}
inset shorthand is particularly useful for creating full-coverage overlays. Instead of writing top: 0; right: 0; bottom: 0; left: 0; every time, you can simply write inset: 0;. It is also useful for centering with the margin: auto technique on absolutely positioned elements.Understanding the Containing Block
The containing block is a fundamental concept in CSS positioning. It determines the reference frame used to calculate the position and dimensions of an element. Different position values produce different containing blocks:
- position: static or relative: The containing block is the content box of the nearest block-level ancestor (or the element that establishes a formatting context).
- position: absolute: The containing block is the padding box of the nearest positioned ancestor (any ancestor with
positionset torelative,absolute,fixed, orsticky). If no positioned ancestor exists, the containing block is the initial containing block (the viewport for continuous media). - position: fixed: The containing block is usually the viewport. However, if an ancestor has
transform,perspective,filter,contain: paint, orwill-changeset to one of those values, the containing block changes to that ancestor's padding box. - position: sticky: The containing block works like relative positioning for layout purposes, but the nearest scrollable ancestor determines the "scroll port" against which stickiness is calculated.
Example: Containing Block Chain
<div class="grandparent">
<div class="parent">
<div class="child">Where am I positioned?</div>
</div>
</div>
/* Scenario 1: Only grandparent is positioned */
.grandparent {
position: relative;
padding: 40px;
background: #eee;
}
.parent {
/* position: static (default) -- NOT a positioned element */
padding: 40px;
background: #ddd;
}
.child {
position: absolute;
top: 0;
left: 0;
/* Positioned relative to .grandparent, because .parent is static
and does not count as a positioned ancestor. */
}
/* Scenario 2: Parent is positioned */
.grandparent {
position: relative;
padding: 40px;
background: #eee;
}
.parent {
position: relative; /* NOW it is a positioned element */
padding: 40px;
background: #ddd;
}
.child {
position: absolute;
top: 0;
left: 0;
/* Now positioned relative to .parent, because .parent is the
NEAREST positioned ancestor. */
}
Common Positioning Patterns
Let us look at some practical, real-world patterns that combine multiple positioning techniques. These patterns appear frequently in modern web development and form the building blocks of common UI components.
Example: Dropdown Menu
/* Dropdown trigger and menu */
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-trigger {
padding: 10px 20px;
background: #3498db;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
.dropdown-menu {
position: absolute;
top: calc(100% + 4px); /* Just below the trigger with a small gap */
left: 0;
min-width: 200px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
padding: 8px 0;
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
z-index: 100;
}
.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.dropdown-menu a {
display: block;
padding: 10px 16px;
color: #333;
text-decoration: none;
}
.dropdown-menu a:hover {
background: #f0f0f0;
}
Example: Sticky Header with Fixed Actions
/* A page layout combining fixed, sticky, and absolute positioning */
/* Fixed navigation bar */
.main-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 64px;
background: white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
display: flex;
align-items: center;
padding: 0 24px;
}
/* Body offset for fixed nav */
body {
padding-top: 64px;
}
/* Sticky sub-navigation (sticks below the fixed nav) */
.sub-nav {
position: sticky;
top: 64px; /* Sticks right below the fixed navbar */
background: #f8f9fa;
padding: 12px 24px;
border-bottom: 1px solid #dee2e6;
z-index: 999;
}
/* Content area with a positioned container for absolute children */
.content-section {
position: relative;
padding: 40px 24px;
}
/* Absolute decorative element */
.content-section .accent-line {
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: linear-gradient(to bottom, #3498db, #2ecc71);
}
/* Fixed floating action button */
.fab {
position: fixed;
bottom: 32px;
right: 32px;
width: 56px;
height: 56px;
border-radius: 50%;
background: #3498db;
color: white;
border: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
cursor: pointer;
z-index: 1001;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
}
Debugging Positioning Issues
Positioning issues are among the most common CSS layout problems. Here are the most frequent issues and how to debug them:
- Absolute element is positioned relative to the wrong ancestor: Open DevTools and inspect the element. Check each ancestor to see which one has a
positionvalue other thanstatic. If the wrong ancestor is positioned, either remove its positioning or addposition: relativeto the intended parent. - Fixed element scrolls with the page: Check if any ancestor has a
transform,perspective,filter, orwill-changeproperty. Any of these on an ancestor will break the fixed behavior relative to the viewport. - Sticky element is not sticking: Verify that you have set an offset property (like
top: 0). Check that no ancestor hasoverflow: hiddenoroverflow: auto. Make sure the parent is tall enough for scrolling to occur. In DevTools, Chrome highlights sticky elements and shows their sticking constraints. - Element disappears when positioned: Check if the element has collapsed to zero width or height. Absolutely positioned elements shrink to fit their content by default, so if there is no content or explicit dimensions, they may be invisible. Also check if the element is behind another element (a z-index issue).
- Overlapping content: Remember that absolute and fixed elements are removed from the flow. Other elements will not make room for them. You may need to add padding or margin to surrounding elements to prevent overlap.
Example: Common Fix for Sticky Not Working
/* BROKEN: Sticky does not work because the parent has overflow: hidden */
.broken-layout {
overflow: hidden; /* This prevents sticky from working! */
}
.broken-layout .sticky-header {
position: sticky;
top: 0;
/* Will NOT stick because the parent has overflow: hidden */
}
/* FIXED: Remove overflow from the parent */
.fixed-layout {
/* No overflow property, or overflow: visible (the default) */
}
.fixed-layout .sticky-header {
position: sticky;
top: 0;
background: white;
z-index: 10;
/* Now sticks correctly! */
}
/* ALTERNATIVE FIX: If you need overflow on an ancestor,
make sure the sticky element is a direct child of the scroll container */
.scroll-container {
overflow-y: auto;
height: 500px;
}
.scroll-container .sticky-header {
position: sticky;
top: 0;
/* Works because the sticky element is inside the scrolling container,
not outside it. */
}
Position Property Summary Table
Here is a comprehensive comparison of all five position values to serve as a quick reference:
Quick Reference: All Position Values
/*
+----------+-------------+------------------+------------------+----------+
| Value | In Flow? | Positioned To | Scrolls With | z-index |
+----------+-------------+------------------+------------------+----------+
| static | Yes | N/A | Page | No |
| relative | Yes | Own normal pos | Page | Yes |
| absolute | No | Nearest pos'd | Containing block | Yes |
| | | ancestor | | |
| fixed | No | Viewport* | Stays in place | Yes |
| sticky | Yes (until | Scroll threshold | Hybrid behavior | Yes |
| | threshold) | | | |
+----------+-------------+------------------+------------------+----------+
* Fixed positioning uses viewport unless an ancestor has
transform, perspective, filter, or will-change set.
*/
/* Complete example combining all position types */
.page {
position: relative; /* Context for absolute children */
}
.page .background-decoration {
position: absolute; /* Decorative element removed from flow */
top: 0;
right: 0;
width: 300px;
height: 300px;
opacity: 0.1;
}
.page .navbar {
position: fixed; /* Always visible at top */
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.page .sidebar-heading {
position: sticky; /* Sticks while scrolling section */
top: 70px;
}
.page .nudged-element {
position: relative; /* Slightly offset from normal position */
top: -5px;
left: 2px;
}
Exercise 1: Build a Product Card with Overlays
Create a product card component that uses multiple positioning techniques. The card should have a product image, and overlaid on the image you need: a "Sale" badge in the top-left corner using absolute positioning, a heart (favorite) icon in the top-right corner using absolute positioning, and a gradient overlay at the bottom of the image showing the product name using absolute positioning. Below the image, add a product description and price. The entire card should have a hover effect where the image scales up slightly (use transform: scale(1.05)) while the card container has overflow: hidden to clip the scaled image. Make sure the overlaid elements stay in place during the hover transition. Finally, wrap the whole component in a CSS Grid or Flexbox layout showing four product cards in a row.
Exercise 2: Multi-Layer Page Layout
Build a complete page layout that demonstrates all five position values working together. Start with a fixed navigation bar at the top of the page (64px tall, full width). Below it, create a two-column layout: a main content area on the left and a sticky sidebar on the right that sticks 80px from the top (to account for the navbar plus some gap). Inside the main content area, create several sections, each with a sticky section header that sticks at 72px from the top (just below the nav). Add a floating action button in the bottom-right corner of the viewport using fixed positioning. Inside one of the sections, create a "before/after" image comparison slider where a divider line is absolutely positioned inside a relative container, and the user can drag it left and right (you can use a simple CSS-only approach with resize: horizontal or describe the JavaScript interaction). Add a tooltip that appears on hover over one of the section headers, using absolute positioning inside the relatively positioned header. Test the entire layout by scrolling and verify that each element behaves correctly: the navbar stays fixed, the sidebar sticks, the section headers stick and replace each other, the FAB stays in the corner, and the tooltip appears correctly.