CSSFixed Positioning Deep Dive

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

CSS
/* 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.

CSS
<!-- 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.

CSS
/* 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

CSS
/* 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

CSS
<!-- 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

CSS
<!-- 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

CSS
<!-- 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. */
Note
Sticky positioning only works within its parent container. If the parent scrolls out of view, the sticky element scrolls away with it. For truly fixed elements that always stay visible, use `position: fixed`.
Fixed positioning can interfere with page layout
Fixed elements don't take up space, so content can be hidden beneath them. Always account for fixed headers/footers by adding padding or margin to the body or main content. Also consider mobile viewports where fixed elements take up significant space.
Sticky has limited browser support on older devices
`position: sticky` requires modern browsers. For better compatibility with older devices, consider JavaScript-based sticky solutions or just use `position: fixed` with careful padding.
Next
Controlling stacking order with z-index: [z-index & stacking context](/css/z-index).