Search code examples
htmlcssdynamiclayoutsticky

Achieve Dynamic Layout of a sticky shrinking div when scrolling


I have a problem with a simple HTML CSS structure. In a #maincontainer are container1 and container2 displayed in columns (one below the other). My content plans to exceed the height of the window. I'd like to take advantage of this to create an effect in which container1, with an initial width of 100%, could be "pushed" by container2, but still sticky at the top of the main container, and whose height and width would shrink as one scrolls in maincontainer, down to a minimum width of 30%. In other words, container 1 shrinks instead of disappearing at the top of the screen to make way for the contents of container2. Is it possible to achieve this effect without JS scripting?

If so, I'd be happy to hear your ideas or tips for achieving this effect. I hope everything is clear and thank you in advance for your help. Paul

PS : I plan to use an image instead of that #subcontainer, that is the main element that should stay at the top of the page. But I don't know which div should be sticky and which element should define a minimum width. (???)

Here is what I tried: https://codepen.io/PaulPaturel/pen/WNmYyxy

  <body>
  <div id="main_container">
  <!-- content that should shrink and stay at the top -->
  <div id="container1"><div id="subcontainer" alt="should be an image"></div></div>

  <!-- overflowing content -->
  <div id="container2">
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
    <p>AAAAAAAAAAAAAAAAAAA</p>
  </div>
  </div>
  </body>
 #main_container {
    width: 100vw;
    height: 100vh;
    padding: 15px;
    position: relative; 
    box-sizing: border-box;
    background-color: grey;
    display: flex;
    flex-direction: column;
    overflow-y: scroll;
}

#container1 {
    position: sticky;
    top:  0;
    padding:  5px;
    height:  100%; 
    width:  50%;
    min-width: 30%;
    background-color: rgb(149,  149,  255);
    box-sizing: border-box;
    flex:  1;
}


#subcontainer {
    display: flex;
    height: 600px;
    width: 100%;
    background-color: blueviolet;
    box-sizing: border-box;
}

#container2 {
    height: auto;
    background-color: rgb(255, 0, 0);
    font-size: 15px; 
    box-sizing: border-box;
    position: relative; 
    opacity: 0.5;
}


Solution

  • You can achieve this surprisingly easily using animation-timeline: scroll(). You can further specify by adding any relevant stuff between the parentheses. But setting the right middle percentage for the keyframes should be enough.

    #main_container {
      width: 100vw;
      height: 100vh;
      background: grey;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      position: relative;
      overflow-y: scroll;
    }
    
    #container1 {
      position: sticky;
      flex-shrink: 0;
      top: 0;
      height: 600px;
      width: 100vw;
    }
    
    #subcontainer {
      height: 100%;
      background: lightblue;
      animation: shrinking forwards linear;
      animation-timeline: scroll();
    }
    
    #container2 {
      background: yellow;
    }
    
    @keyframes shrinking {
      0% {
        height: 100%;
      }
    
      33.3% {
        height: 30%; /* set this keyframe percentage to specify the animation speed */
      }
    
      100% {
        height: 30%;
      }
    }
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="stylesheet" href="style.css" />
        <title>Admin Dashboard</title>
      </head>
      <body>
        <div id="main_container">
          <!-- content that should shrink and stay at the top -->
          <div id="container1">
            <div id="subcontainer" alt="should be an image"></div>
          </div>
    
          <!-- overflowing content -->
          <div id="container2">
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
            <p>AAAAAAAAAAAAAAAAAAA</p>
          </div>
        </div>
      </body>
    </html>

    Though I still believe using JS's addeventlistener is much better. But this should work.