Creating a fixed or sticky element is a little dangerous when you're not sure on the height of the final element. If it runs too long, then some parts of it will get cut off and you cannot scroll down to see them. Definitely not a good user experience. I also didn't want the sidebar to be there on a smaller screen, otherwise it would be really squashed and pretty much un-readable. Luckily, in this case, I had complete control over the HTML, so I was more or less free to do whatever was needed to make this work.

Here's the basic HTML I wanted to use:

<main class="with-side-nav">
  <nav>
    <ul>
      <li></li>
      ...
    </ul>
  </nav>

  <article>
    Main content goes here.
  </article>
</main>

In my setup, there were 17 items in the nav list that linked to sections on the page. However, there was a chance more would need to be added later on.

So, I tried to use things like floating the nav to the right, but that was a bit messy; flex-box seemed OK, but still didn't feel right. Eventually, CSS Grid came out as the simplest way to do this.

.with-side-nav {
  display: grid:
  grid-template-columns: 20vw auto;
}

With just that CSS, the nav had its own column on the left, and the main content was on the right. Gotta love that Grid magic!

Next step was to make the nav sticky when scrolling down. Note that I am using SASS here - just split out nav to .with-side-nav nav if you're using vanilla CSS.

.with-side-nav {
  display: grid;
  grid-template-columns: 20vw auto;

  nav {
    position: sticky;
    top: 50px;
    height: 100vh;
  }
}

The top and height are REQUIRED for the nav to be sticky. Without either of those being set, the nav does not stick.

If you look at this on a smaller screen, the nav gets super squished and if your height of the browser window is not tall enough, you will lose some of those list items to the void. Time for some media queries.

Let's only activate the grid on larger screens - in my case, min-width: 980px was a good width. For the height, there is actually a min-height media query. I honestly did not know that until doing this. For my setup, 560px was the shortest I could go. Here's what the code looks like with those two media queries added:

.with-side-nav {
  @media screen and (min-width: 980px) {
    display: grid;
    grid-template-columns: 20vw auto;

    nav {
      @media screen and (min-height: 560px) {
        position: sticky;
        top: 50px;
        height: 100vh;
      }
    }
  }
}

That's pretty much it. For any screen below 980px wide, you'll have the nav sitting at the top of the page, scrolling along with the page. Wider than 980px, the nav pops into the left column, content in the right. If we're above 560px of screen height, the nav will be sticky and stay in place as you scroll down the page, otherwise, it will stay at the top of the column and scroll with the reset of the page.

I added in some extra CSS so that the long list splits into two columns on smaller screen, but that doesn't affect anything in the above code.