In the last 2 posts, I wrote about best practises around handling scroll events and then how to combine a sticky nav with smooth scrolling. Both solutions required JavaScript, but there's solutions right around the corner that allows us to do away with all the JavaScript and let the browser do all the work with a few directions from CSS.

Firstly, I've replicated the ffconf2016 site and stripped out all the JavaScript that ran the smooth scrolling and sticky nav.

Then I turn to two CSS properties:

  • position: sticky - currently supported in Firefox, Chrome and Safari
  • scroll-behavior: smooth - currently supported in Firefox only

The effect is exactly what I want and the final code is extremely light compared to the original JavaScript version.

Implementation details

The scroll-behavior is applied directly on the html element so that it applies to whole window scrolling.

The position: sticky always catches me out. First I applied it to the navigation element, but this doesn't work because it's sticky to it's parent, and the parent doesn't have an inner scroll. The html element is the element that's scrolling, so the stickiness is applied to the whole header element on the page (that includes the navigation bar).

Since the sticky is applied to the entire header block (which is the height of the page), the top position for the sticky element, needs to be the height of the header element (100%) less the height of the navigation (in my case 100px). So I'm going to make use of the excellent CSS calc value (which is very well supported): top: calc(-100% + 100px).

The final code is shown below. I've only applied it when the navigation is full present, in my case at > 768px wide, and I've had to tweak the header component to use flex box to keep it set to the height of the window (note that this in only specific to my case, it's likely you wouldn't need this).

@media all and (min-width: 768px) {
  html {
    scroll-behavior: smooth;
  }

  #masthead {
    position: sticky;
    top: calc(-100% + 100px);
    /* make sure stick above images */
    z-index: 1;

    /* tweaks to the ffconf design
       to keep the height right */
    display: flex;
    flex-direction: column;
  }

  .logo-wrapper {
    flex-basis: 85vh;
  }
}

Full working demo here - currently only Firefox supports both position: sticky and scroll-behavior so it's best viewed there. But you can also see it working nicely (without enhancements) in Chrome and other browsers.