fixed & sticky positioning
position: fixed positions an element relative to the viewport, so it stays in place while the user scrolls. position: sticky is a hybrid: elements stay in the normal flow until they reach a scroll threshold, then "stick" to a fixed position. Fixed positioning is used for headers, navigation bars, and floating buttons. Sticky positioning is ideal for section headers and table headers.
position: fixed
Property | Fixed element | Regular element |
|---|---|---|
Positioned relative to | Viewport | Normal document flow |
In flow? | No, removed from flow | Yes, part of flow |
Space taken up? | No | Yes |
Scroll behaviour | Stays in place | Scrolls with page |
Use case | Headers, nav, floating buttons | Page content |
/* Fixed positioning — relative to viewport */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
/* Element stays in place while user scrolls */
/* Fixed header */
.sticky-header {
position: fixed;
top: 0;
left: 0;
right: 0; /* or width: 100% */
height: 60px;
background: white;
border-bottom: 1px solid #cccccc;
z-index: 100; /* stay on top of content */
}
/* Account for fixed header with padding */
body {
padding-top: 60px; /* space for header */
}
/* Fixed navigation bar */
.navigation {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background: #333333;
}
/* Account for fixed nav with padding */
body {
padding-bottom: 50px;
}
/* Fixed floating button */
.floating-button {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: #0066cc;
color: white;
}Fixed positioning removes elements from flow
Fixed elements don't take up space, so you must account for them with padding or margin on other content.
<!-- Without accounting for fixed header -->
<header class="fixed">Navigation</header>
<main>
<p>This text is under the header!</p>
</main>
.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
/* Header floats above content */
/* Text starts at top of viewport, hidden under header */
}
<!-- Proper solution: padding on main content -->
<header class="fixed">Navigation</header>
<main>
<p>This text is below the header.</p>
</main>
.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
}
main {
padding-top: 60px; /* match header height */
/* Content now starts below the header */
}position: sticky
Sticky positioning is a hybrid: elements start in normal flow, but when they scroll past a threshold, they "stick" to a fixed position relative to their scroll container.
/* Sticky positioning — hybrid of relative and fixed */
position: sticky;
top: 0; /* stick to top when scrolled past */
/* Sticky header that sticks when scrolled */
.section-header {
position: sticky;
top: 0;
background: white;
border-bottom: 1px solid #cccccc;
padding: 10px;
z-index: 10; /* stay above content */
}
/* Behavior */
<!-- Initially, header is part of normal flow -->
[Section 1]
[Section 1 header] ← in normal flow
[Section 1 content]
<!-- When scrolled past the header -->
[Scrolled up]
━━━━━━━━━━━━━━━
[Section 1 header] ← sticks to top
━━━━━━━━━━━━━━━
[Section 2 content] ← scrolls under sticky header
<!-- When the sticky element's parent scrolls out of view -->
[Section 1 content]
[Section 1 header] ← back to normal, scrolls away with parent
[Section 2 content]Sticky vs fixed: key differences
/* FIXED — always stays in place */
.fixed-header {
position: fixed;
top: 0;
/* Always at top of viewport, never moves */
/* Removed from flow entirely */
}
/* STICKY — sticks within its parent */
.sticky-header {
position: sticky;
top: 0;
/* Sticks to top while its parent is visible */
/* When parent scrolls out of view, header follows */
/* Part of normal flow until scroll threshold */
}
<!-- Practical difference -->
<section>
<header class="sticky">Section A</header>
<content>A content</content>
</section>
<section>
<header class="sticky">Section B</header>
<content>B content</content>
</section>
<!-- With sticky: when you scroll past Section A header, it stays at top -->
<!-- until Section A is completely off-screen, then Section B header takes over -->
<!-- With fixed: headers always stay at top, covering all content -->Sticky table headers
<!-- Sticky table headers while scrolling -->
<table class="scrollable-table">
<thead class="sticky-header">
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<!-- many rows -->
</tbody>
</table>
.scrollable-table {
overflow-y: scroll;
height: 400px;
border: 1px solid #cccccc;
}
.sticky-header {
position: sticky;
top: 0;
background: white;
border-bottom: 1px solid #cccccc;
z-index: 10;
}
.sticky-header th {
padding: 10px;
text-align: left;
border-bottom: 2px solid #0066cc;
}
/* Header stays visible while scrolling through tbody */Practical fixed positioning examples
<!-- Fixed chat widget -->
.chat-widget {
position: fixed;
bottom: 20px;
right: 20px;
width: 350px;
height: 500px;
background: white;
border-radius: 8px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
z-index: 1000;
display: flex;
flex-direction: column;
}
.chat-widget-header {
padding: 15px;
background: #0066cc;
color: white;
border-radius: 8px 8px 0 0;
font-weight: bold;
}
<!-- Fixed notification banner -->
.notification {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #4caf50;
color: white;
padding: 15px 20px;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 1001;
max-width: 500px;
}
<!-- Fixed backdrop behind modal -->
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}Sticky with multiple elements
<!-- Multiple sticky elements -->
<article>
<section>
<h2 class="sticky-heading">Section 1</h2>
<h3 class="sticky-subheading">Subsection 1.1</h3>
<p>Content...</p>
<h3 class="sticky-subheading">Subsection 1.2</h3>
<p>Content...</p>
</section>
<section>
<h2 class="sticky-heading">Section 2</h2>
<p>Content...</p>
</section>
</article>
.sticky-heading {
position: sticky;
top: 0;
background: white;
z-index: 20;
}
.sticky-subheading {
position: sticky;
top: 0; /* sticks at same position as heading */
background: #f5f5f5;
z-index: 10; /* lower z-index so heading appears above */
}
/* Result: as you scroll, headings stick. Subheading sticks under heading. */