How does the “position: sticky;” property work?

Attack this Q from other direction.

Imagine this is a game Find the nearest scrolling ancestor.

<!-- sticky not working -->
<h1 style="position: sticky; top:0;">Hello World</h1>

Questions:

  • 1/3: The sticky node? <h1>.
  • 2/3: The ancestor? <body>.
  • 3/3: <body> scrolling ? FALSE => “No effect”.

Fix: “Sticky is working” (<body> scrolling ? TRUE).

body{
  min-height: 300vh;
}
<!-- sticky working -->
<h1 style="position: sticky; top: 0;">Hello World</h1>

With this in mind – here are some “hello world” “famous” scenarios of “not working” sticky 🙂 Most cases relate to one or many of these cases.

Case 1: Missing “top” (Easy to fix):

Not working:

/* not working example */
aside{
  position: sticky;
  background: lightgray;
}

main{
  height: 200vh;
}
<aside>
  <h2>sticky Aside</h2>
</aside>

<main>
  <h1>Article</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
  </p>
</main>

Fix (Add top):

aside{
  position: sticky;
  top: 0;
}

main{
  height: 200vh;
}
<aside>
  <h2>sticky Aside</h2>
</aside>

<main>
  <h1>Article</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
  </p>
</main>

Case 2: Sticky node & overflow (Easy to fix):

I “destroy” the sticky by adding #extra-wrapper with overflow setting auto -or- hidden -or- visible – but without any clipped content.

“The problem” now the nearest scrolling ancestor (#extra-wrapper) “without” any scrolling (No scrollbar dragging option == “no scrolling ancestor”).

Not working:

/* not working example */
#overflow-wrapper{
  overflow: scroll;
}

aside{
  position: sticky;
  background: lightgray;
  top: 0px;
}

main{
  height: 200vh;
}
<div id="overflow-wrapper">
  <aside>
    <h2>sticky Aside</h2>
  </aside>

  <main>
    <h1>Article</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
    </p>
  </main>
</div>

Fix – Clip the content (Now their is “nearest scrolling ancestor”).

Working:

/* not working example */
#overflow-wrapper{
  overflow: scroll;
  max-height: 60vh; /* clip the content */
}

aside{
  position: sticky;
  background: lightgray;
  top: 0px;
}

main{
  height: 200vh;
}
<div id="overflow-wrapper">
  <aside>
    <h2>sticky Aside</h2>
  </aside>

  <main>
    <h1>Article</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
    </p>
  </main>
</div>

Case 3: Sticky related to “wrong / not scrolling” node (Tricky to fix)

Again, Sticky offset relative to its nearest scrolling ancestor.

I “destroy” the sticky by adding #extra-wrapper to the sticky element. Why it is not working? Now the height of #extra-wrapper == height aside content (box model) == “no scrolling ancestor” == “no effect”.

Not working:

/* not working example */
aside{
  position: sticky;
  top: 0;
  background: lightgray;
}

main{
  height: 200vh;
}
<div id="extra-wrapper">
  <aside>
    <h2>sticky Aside</h2>
  </aside>
</div>

<main>
  <h1>Article</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
  </p>
</main>

This is what really “happens” (I added height to #extra-wrapper):

#extra-wrapper{
  background: lightgray;
  height: 40vh;
}
aside{
  position: sticky;
  top: 0;
}

main{
  height: 200vh;
}
<div id="extra-wrapper">
  <aside>
    <h2>sticky Aside</h2>
  </aside>
</div>

<main>
  <h1>Article</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
  </p>
</main>

FIX:
change the sticky node:

#extra-wrapper{
  position: sticky;
  top: 0;
}
aside{

}

#layout{
  displ
}
main{
height: 200vh;
}
<div id="extra-wrapper">
  <aside>
    <h2>sticky Aside</h2>
  </aside>
</div>

<main>
  <h1>Article</h1>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
  </p>
</main>

Case 4: display: flexbox/Grid layout – even cols by deafult (Tricky to fix)

You create flex/grid layout & set one of the cols to be sticky. By default the cols height is even = The height of the “nearest ancestor” (wrapper) == Cols height = no scroll effect.

Not working:

#extra-wrapper{
  position: sticky;
  top: 0;
  border: 1px solid red;
}
aside{

}

#layout{
  display: flex;
}
main{
height: 200vh;
}
<div id="layout">
  <div id="extra-wrapper">
    <aside>
      <h2>sticky Aside</h2>
    </aside>
  </div>

  <main>
    <h1>Article</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
    </p>
  </main>
</div>

FIX: Set the sticky aside max-height to be 90vh for example (Now the cols height is not even).

Working:

#extra-wrapper{
  position: sticky;
  top: 0;
  border: 1px solid red;
  max-height: 90vh;
}
aside{

}

#layout{
  display: flex;
}
main{
height: 200vh;
}
<div id="layout">
  <div id="extra-wrapper">
    <aside>
      <h2>sticky Aside</h2>
    </aside>
  </div>

  <main>
    <h1>Article</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui dicta minus molestiae vel beatae natus eveniet ratione temporibus aperiam harum alias officiis assumenda officia quibusdam deleniti eos cupiditate dolore doloribus!
    </p>
  </main>
</div>