Adam
Adam

Reputation: 108

Position sticky does not remain centrally aligned upon screen resize

I've noticed that centrally aligning (vertically) an element with position sticky starts to break down after a certain reduction of screen width, depending on that elements width. Unlike position: fixed, the sticky element eventually gets stuck and loses its central alignment.

Any idea why this is happening and a workaround?

div {
  height: 100px;
  width: 500px;
  background-color: red;
  position: sticky;
  left: 50%;
  transform: translateX(-50%);
}
<div>
</div>

Upvotes: 2

Views: 835

Answers (2)

Adam
Adam

Reputation: 108

I came back to this question after some time. PIzmAR's answer is a good workaround but does not explain why this is happening as fy data suggested and there is a better solution without wrapping HTML elements.

The reason this happens is actually simple. Sticky applies stickiness to both Y and X axes.

Since it also keeps its position/size in the normal DOM flow, its untransformed state will (depending on the sticky elements width) eventually hit the far-right most size of the viewport and create overflow. At this point, sticky elements just behave in the same way they do on the Y axis and STICK in position. That's it! It's the left: 50% attribute that's the culprit, in the same way the top: attribute works when using sticky in a Y direction.

This isn't what I needed at the time and I doubt it's what most people need when using left to centrally align a sticky element. So the best solution is to treat the sticky element as relative and use margins to center it on the X axis. Like so:

#box-2 {
  top:0;
  left: 0;
  transform: translate(0);
  margin-left: auto;
  margin-right: auto;
}

Run the code snippet in fullscreen to see some examples:

        function updateBoxClasses(boxId, highlighted) {
            const box = document.getElementById(boxId);
            if (highlighted) {
                box.classList.add('highlighted');
                box.classList.remove('box');
            } else {
                box.classList.add('box');
                box.classList.remove('highlighted');
            }
        }

        function updateClasses() {
            const viewportWidth = window.innerWidth;
            if (viewportWidth <= 1000) {
                updateBoxClasses('box-ghost', true);
                updateBoxClasses('box', true);
                updateBoxClasses('box-2', true);
            } else {
                updateBoxClasses('box-ghost', false);
                updateBoxClasses('box', false);
                updateBoxClasses('box-2', false);
            }
        }

        updateClasses();
        window.addEventListener('resize', updateClasses);
* {
  box-sizing: border-box;
}

body {
  width: 100%;
  padding: 0;
  margin: 0;
}

div#container {
  height: 2000px;
  width: 100%;
  position: relative;
}

div#heading {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 64px;
  height: 200px;
  border: solid black 2px;
}

#box, #box-2 {
  position: sticky;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 32px;
  color: white;
  opacity: 50%;
  top: 100px;
}

#box-2 {
  top: 210px;
  margin-top: 10px;
  left: 0;
  transform: translate(0);
  margin-left: auto;
  margin-right: auto;
}



span {
  font-size: 12px;
  position: absolute;
  top: 5px;
  left: 10px;
}

#box-ghost {
  position: absolute;
  top: 0;
  left: 50%;
  background-color: transparent;
  border-style: dashed;
  border-width: 10px;
  opacity: 70%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 32px;
}

/* div#half-width {
  position: fixed;
  left: 0;
  right: 0;
  width: 250px;
  height: 100px;
  background-color: grey;
  display: block;
  color: lightgray;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 32px;
} */

.highlighted {
  border-color: blue;
  color: blue;
  background-color: blue;
   width: 500px;
  height: 100px;
}

.box {
  color: red;
  background-color: red;
  border-color: red;
   width: 500px;
  height: 100px;
}
<div id="heading"><span>position: relative</span>Resize viewport to see demo</div>
<div id="container">
  <div class="box" id="box"><span>position: sticky (left: 50%, transform: translateX(-50%))</span>500px</div>
  <div class="box-ghost" id="box-ghost"><span>position: absolute (left: 50%)</span>500px (not transformed)</div>
  <div class="box" id="box-2"><span>position: sticky (margin-left: auto, margin-right: auto)</span>500px (solution)</div>
</div>

Upvotes: 0

PIzmAR
PIzmAR

Reputation: 78

You can do it with using flex on parent class and using justify-content

Example: https://jsfiddle.net/9cp6borj/

<body>
  <div class="div__item">
    <div class="div__sticky">
    </div>
  </div>
</body>
<style>
.div__item {
  display:flex;
  justify-content:center;
}
.div__sticky {
  height: 100px;
  width: 500px;
  background-color: red;
  position: -webkit-sticky;
  position: sticky;
  top: 20px;
}
</style>

Upvotes: 2

Related Questions