cssgoogle-chromefirefoxmarginsticky

How to position a sticky div to stick to the edge within a scrollable div?


In Firefox, an element styled with position: sticky and a negative margin-inline-end doesn't stick at the expected position within its parent when scrolling. This behavior deviates from Chrome, where the positioning works as expected. The bug is detailed in this Bugzilla entry: Mozilla Bugzilla #1895739

Code to Reproduce:

Codepen Link

.parent {
  background: lightblue;
  width: 800px;
  height: 50px;
  overflow: scroll;
}

.scroller {
  width: 150%;
  height: 40px;
  background: blue;
  border: 1px solid red;
}

.button {
  position: sticky;
  width: 50px;
  height: 20px;
  background: green;
  border: 1px solid cyan;
}

.back {
  inset-inline-start: 1em;
}

.forward {
  inset-inline-start: 100%;
  translate: calc(-100% - 1em);
  margin-inline-end: -100%;
}
<div class="parent">
  <div class="scroller">
    <div class="button back"></div>
    <div class="button forward"></div>
  </div>
</div>

Expected Behavior: The .forward div should appear at the right edge of the .parent.

I've reported this issue in Bugzilla but am looking for alternative solutions or workarounds that could help solve this problem while it's being addressed.

Btw, it also works correctly in Firefox, if I put margin-inline-end: -300px on .forward. -300px is an arbitrary value here, the value just needs to be bigger than the width of the .forward element + the (-)1em translateX. However, in my actual usecase, I can't set arbitrary values like this.


Solution

  • You need to set the coordonates where the element will stick.

    example (my browser is FF 135.01 at this time):

    .parent {
      background: lightblue;
      width: clamp(450px,50vw, 90vw);
      height:clamp(150px,80vh, 600px);
      overflow: scroll;
    }
    
    .scroller {
      width: 300%;
      height:300%;
      background: linear-gradient(30deg,blue,lightblue);
      border: 1px solid red;
    }
    
    .button {
      position: sticky;
      width: 50px;
      height: 20px;
      background: green;
      border: 1px solid cyan;
    }
    
    .back {
      background: yellow;
      float: inline-start;/* to push towards inline-start */
      inset:calc(100% - 3em) auto 1em 1em;
    }
    
    .forward {
      float: inline-end;/* to push towards inline-end */
      inset: 1em 1em auto auto;
      top: 1em;
    }
    <div class="parent">
      <div class="scroller">
        <div class="button back"></div>
        <div class="button forward"></div>
      </div>
    </div>

    resources :