My position: sticky element isn't sticky when using flexbox
Asked Answered
L

9

273

I was stuck on this for a little bit and thought I'd share this position: sticky + flexbox gotcha:

My sticky div was working fine until I switched my view to a flex box container, and suddenly the div wasn't sticky when it was wrapped in a flexbox parent.

.flexbox-wrapper {
  display: flex;
  height: 600px;
}
.regular {
  background-color: blue;
}
.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: red;
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>

JSFiddle showing the problem

Littles answered 8/6, 2017 at 22:42 Comment(3)
Does this behavior persist across all browsers that support position: sticky or just one/some?Hardpressed
@Hardpressed I believe it's all browsers. It functioned like this in both Chrome and Safari.Littles
Thanks. I can confirm the problem and solution occur in Firefox, too.Hardpressed
L
538

Since flex box elements default to stretch, all the elements are the same height, which can't be scrolled against.

Adding align-self: flex-start to the sticky element set the height to auto, which allowed scrolling, and fixed it.

Currently this is supported in all major browsers, but Safari is still behind a -webkit- prefix, and other browsers except for Firefox have some issues with position: sticky tables.

.flexbox-wrapper {
  display: flex;
  overflow: auto;
  height: 200px;          /* Not necessary -- for example only */
}
.regular {
  background-color: blue; /* Not necessary -- for example only */
  height: 600px;          /* Not necessary -- for example only */
}
.sticky {
  position: -webkit-sticky; /* for Safari */
  position: sticky;
  top: 0;
  align-self: flex-start; /* <-- this is the fix */
  background-color: red;  /* Not necessary -- for example only */
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>

JSFiddle showing the solution

Littles answered 8/6, 2017 at 22:42 Comment(8)
this only works when scrolling within the containing flex element -- when scrolling the entire window it does NOT stick (at least in Firefox) -- so for those who are experiencing contradictory behavior it is more likely you're experiencing contradictory expectations (like i was)Spinach
@Spinach Do you know of a way to get it to stick when scrolling the whole page?Longerich
@Douglas the question is misleading because it suggests the problem is with flexbox but the problem has more to do with containing the sticky element. look at this fiddle: jsfiddle.net/9y87zL5c where the SECOND red box works as expected but the FIRST red box not stick -- so... this is not related to flexbox (thus contradictory expectations)... ALSO... i added a bunch of jabberwocky to see results in a more accurate manner.Spinach
I have no problem using it in Firefox 87. Works Correctly.Signora
But when used align-self: flex-start, for example, top: 100px; is not working. How can I do that?Charin
this worked a lot to meBailey
@Ms. Siri, the answer works in Edge using both top: 0 and top: 100px. My Flexbox test does not use either overflow or height. You absolutely need top and align-self as the answer shows.Gratian
Thank the heavens for this answer. This answer has been bothering me for days.Presumably
G
94

In my case, one of the parent containers had overflow-x: hidden; applied to it, which will break position: sticky functionality. You'll need to remove that rule.

No parent element should have the above CSS rule applied to it. This condition applies to all parents up to (but not including) the 'body' element.

Gambol answered 8/10, 2019 at 23:57 Comment(12)
You also can't have overflow-x:hidden on the html element.Dysarthria
Thanks a lot. I ALWAYS struggle with position sticky and I didn't knew about this condition until reading your reply. :)Underbelly
@SamuelLiew, yes you can have overflow-x: hidden; on the HTML element.Gambol
@RaphaelAleixo I'm glad I could help :)Gambol
This is what was blocking me from having a sticky working correctly. Thanks a lot!Probative
OMG, this worked! In my case, the overflow hidden was from a parent three levels up from the sticky column. I have used the "section" layout helper from Bulma which apparently has overflow:hidden applied. Though, from my case both overflow-x and overflow-y should not be hidden, not just overflow-x.Gyrus
Thanks was stuck , your comment helpedJacobsen
I just tested it: You can have overflow-x: hidden; on the body tag, but you can't have overflow: hidden; on any other parent element.Rosalbarosalee
for me it was overflow-x: auto; on an ancestor divZhukov
What if you need the overflow? In my case overflow-y: auto; and right: 0; breaks and positions the element to the left.Physiotherapy
Same case for me - had ".card" extra style 'overflow:hidden'Afterburning
Somehow, in my case setting to overflow: unset on an ancestor div fixed this, whereas overflow: auto did not.Allspice
G
36

If you are using flex in the parent element use align-self: flex-start for the element which you want to make sticky.

position: sticky;
align-self: flex-start;
top: 0;
overflow-y: auto;
Gratification answered 5/7, 2021 at 21:7 Comment(1)
But when used align-self: flex-start, for example, top: 100px; is not working. How can I do that?Charin
S
19

You can also try adding a child div to the flex item with the contents inside and assign position: sticky; top: 0; to that.

That worked for me for a two column layout where the contents of the first column needed to be sticky and the second column appear scrollable.

Subjection answered 7/9, 2019 at 5:10 Comment(1)
Totally agree, this is the most simple and reliable option I could find. This way you will also be able to control when to stop sticking by controlling the height of the container.Puseyism
J
9

For my situation, the align-self: flex-start (or justify-self: flex-start) solution does not work. I need to keep overflow-x: hidden as well since some containers swipe horizontally.

My solution required nested display: flex with overflow-y: auto to get the desired behaviors:

  • header can adjust height dynamically, which prevents playing with position: absolute or position: fixed
  • content scrolls vertically, constrained horizontally to the view width
  • sticky element can be anywhere vertically, sticking to the bottom of the header
  • other elements can slide horizontally
    • looks like the SO snippet tool can't render width on child elements to properly to demonstrate the horizontal slide, or maybe there's some other setting on my actual layout that makes it work...
    • note that a wrapper element that does nothing else is required to allow overflow-x: auto to work correctly in elements under a parent with overflow-x: hidden

body {
  height: 100vh;
  width: 100vw;
  display: flex;
  flex-direction: column;
}

body>header {
  background-color: red;
  color: white;
  padding: 1em;
}

.content {
  overflow-x: hidden;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

article {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.horizontal_slide {
  display: flex;
  overflow-x: auto;
  background-color: lightblue;
  padding: .5em;
}

.horizontal_slide>* {
  width: 1000px;
}

.toolbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: lightgray;
  padding: .5em;
  display: flex;
}
<header>Fancy header with height adjusting to variable content</header>
<div class="content">
  <article class="card">
    <h1>One of several cards that flips visibility</h1>
    <div class="overflow_x_wrapper">
      <div class="horizontal_slide">
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
        <div>Reason why `overflow-x: hidden` on the parent is required
        </div>
      </div>
      <div class="toolbar">Sticky toolbar part-way down the content</div>
      <p>Rest of vertically scrollable container with variable number of child containers and other elements</p>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
        dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </article>
  </div>
Journalist answered 14/10, 2020 at 6:21 Comment(0)
O
9

Make sure flex-wrapper has assigned a height, and set the overflow value to auto. Then add "align-self: flex-start;" to the sticky element.

.flexbox-wrapper {
  display: flex;
  height: 600px;  //<- nessary,and don't assign with %
  overflow:auto;  //<-fix
}
.regular {
  background-color: blue;
}
.sticky {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: red;
  align-self: flex-start; // <-fix
}
<div class="flexbox-wrapper">
  <div class="regular">
    This is the regular box
  </div>
  <div class="sticky">
    This is the sticky box
  </div>
</div>
Olenta answered 13/7, 2022 at 13:45 Comment(1)
What if setting a height to the wrapper is not an option? Doing that breaks my layout completelyOnstage
S
1

I made a makeshift flexbox table and had this problem. To solve it, I simply put the sticky header row outside of the flexbox, just before it.

Snowslide answered 13/8, 2020 at 19:51 Comment(5)
What if that is not an option?Onstage
Your options are: Make it an option, quit using whatever won't let you make it an option, take it up with whomever won't let you quit using whatever won't let you make it an option, use JavaScript to rearrange stuff on the page, try the other options e.g. those in the other answers, give in and accept the bad design, redesign your app to not need it (or leave the browser world altogether), give up and quit and/or find another profession. Lots of options, good luck.Snowslide
Sometimes you get a design that has been approved by a client. And sometimes that design does not make it an option. Making it an option, is not an option.Onstage
I highly, highly doubt that your client actually cares how you put things on the page, and suspect that they really only care about how it looks and functions.Snowslide
In other words, put your own flexbox and div's in, or use JS, or complain to your client that you do not have sufficient control to do what they want. Or maybe one of the other options here will work. Or give up, or quit.Snowslide
T
1

Global solution/rule from my experience:

Do not put structures with sticky elements directly inside { display : flex } containers.

Instead (for flex layouts) put your tables/divs with sticky elements inside a flex sub-container (e.g. with { flex: 1 1 auto }, but without { display : flex } ), then everything should work as intended.

Tunic answered 25/3, 2021 at 14:50 Comment(1)
can you give an example ?Capitulum
R
0

This worked for me but i have no idea if it's the issue you have. And my issue was that the centered content in flexbox was hidden when the viewport was too small

/*this is the wrapper of centered content*/
.section {
    overflow-y: auto;
    max-height: none;
    height: 100%;
    min-height: 100vh; /*optional, can be changed to anything*/
}
<div class="section">
   <div class="content">
      Some long content that is centered....
   </div>
</div>
Realism answered 14/4, 2023 at 14:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.