Search code examples
htmlcsssticky

Sticky child element in display: flex parent


I want my .sidebar flex element to be sticky when I'm scrolling but position: sticky; top: 0; doesn't work.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 100vh;
}

header {
  width: 100%;
  height: 100px;
  background-color: blue;
  opacity: 0.5;
  position: sticky;
  z-index: 100;
  top: 0;
}

.container {
  display: flex;
  gap: 2rem;
  position: relative;
}

.sidebar {
  position: sticky;
  top: 0;
}

.main {
  display: flex;
  gap: 2rem;
  flex-direction: column;
}

.main div {
  width: 300px;
  aspect-ratio: 1 / 1;
  background-color: red;
}
<div class="root">
  <header></header>
  <div class="container">
    <div class="sidebar">
      <p>Sidebar</p>
      <button>Upload image</button>
    </div>
    <div class="main">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
  </div>
</div>


Solution

  • The issue is that the default property of align-items for flex elements is normal (acts like stretch in this context) therefore the .sidebar element takes all the available height and can't have the "sticky effect".
    You can achieve the the sticky efect by changing the align-items property on the .container element to flex-start like this :

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      height: 100vh;
    }
    
    header {
      width: 100%;
      height: 100px;
      background-color: blue;
      opacity: 0.5;
      position: sticky;
      z-index: 100;
      top: 0;
    }
    
    .container {
      display: flex;
      align-items: flex-start; /* <- ADD THIS */
      gap: 2rem;
      position: relative;
    }
    
    .sidebar {
      position: sticky;
      top: 100px;
    }
    
    .main {
      display: flex;
      gap: 2rem;
      flex-direction: column;
    }
    
    .main div {
      width: 300px;
      aspect-ratio: 1 / 1;
      background-color: red;
    }
    <div class="root">
      <header></header>
      <div class="container">
        <div class="sidebar">
          <p>Sidebar</p>
          <button>Upload image</button>
        </div>
        <div class="main">
          <div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
        </div>
      </div>
    </div>

    (Note that I also changed the top property on the .sidebar element so it doesn't hide under the header)