Search code examples
htmlcssscroll-snap

CSS scroll-snap to top only when children larger than parent container


I'm using scroll-snap-type: y to achieve vertical scrolling with snap points in a container. My goal is for the top of each child element to snap to the top of the parent container.

However, I'm facing an issue where the child also snaps to the bottom of the parent container. I want to ensure that only the top of each child element snaps to the top of the parent, even if the child elements are larger than the parent.

In my specific case, I have a parent container with a height of 100vh. Inside this container, there are two child elements. The first child is 120vh tall, and the second is 100vh tall. When scrolling, I want to avoid any snap point where the top of the second child aligns with the bottom of the parent element.

Is there a way to configure CSS scroll snapping to only snap the top of these larger child elements to the top of the parent container?

.wrapper {
  height: 100vh;
  overflow: auto;
  scroll-snap-type: y mandatory;
}

.panel {
  width: 100vw;
  scroll-snap-align: start;
  height: 120vh;
  background: red;
  display: flex;
  justify-content: center;
  color: white;
  align-items: center;
}

.panel2 {
  width: 100vw;
  height: 100vh;
  scroll-snap-align: end;
  background: pink;
  display: flex;
  justify-content: center;
  color: black;
  align-items: center;
}
<div class="wrapper">
  <div class="panel">PANEL ONE</div>
  <div class="panel2">PANEL TWO</div>
</div>

EDIT

I would like to clarify the desired scrolling behaviour for both scrolling down and scrolling back up:

Scrolling Down:

When a user scrolls down, the top of each child element should snap to the top of the parent container. For example, as the user scrolls down from Panel One to Panel Two, I want the top of Panel Two to align with the top of the wrapper, without any additional snap points in between.

Scrolling Up:

As the user scrolls up from Panel Two to Panel One, the top of Panel One should align with the top of the wrapper.

The key point is to avoid any snap point where the bottom of a child element (like the bottom of Panel One) aligns with the bottom of the wrapper.

The snapping should only occur at the top of the child elements aligning with the top of the parent container, and not at any other point of the child elements.


Solution

  • From what I can tell in your question, the below code snippet should give you the behavior you're looking for. I've added a div with a class of scroll-container with the height: fit-content; property to ensure the containers height adapts to its content.

    .wrapper {
      height: 100vh;
      overflow: auto;
      scroll-snap-type: y mandatory;
    }
    
    .panels {
      width: 100vw;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .panel {
      height: 120vh;
      scroll-snap-align: start;
      background: red;
      color: white;
    }
    
    .panel2 {
      height: 100vh;
      scroll-snap-align: end;
      background: pink;
      color: black;
    }
    
    .scroll-container {
      scroll-snap-type: y mandatory;
      height: fit-content; /* Ensure the container's height adapts to content */
    }
    <div class="wrapper">
      <div class="scroll-container">
        <div class="panels panel">PANEL ONE</div>
        <div class="panels panel2">PANEL TWO</div>
      </div>
    </div>

    If this isn't what you're looking for, would you be able to provide a bit more clarity on the specific result you're intending to create? What do you want to occur when someone scrolls down and when someone scrolls back up?