Sticky headers are a very common pattern found on a lot of websites. You see lots of site headers that stick to the top of the page once the user scrolls down to a certain point (usually the height of the header). Since it’s such a common UX pattern, there are lots of libraries, plugins and different ways to build them. But I hate adding libraries to my codebases when just a few lines of custom JavaScript and CSS can do what almost any of the libraries can do. I recently created a super simple sticky header (that animates in as you scroll down) for a client with 17 lines of JavaScript and I’d like to show you how!

Markup

There is no special markup needed really. Any old element will work. For this example, we’ll just use an empty header tag.

<header class="header"></header>

JavaScript

JavaScript is needed for the sole purpose of adding and removing a class to the element.

const header = document.querySelector('.header');

if (header) {
  const stickyControl = () => {
    if (window.pageYOffset >= 80) {
      header.classList.add('sticky');
    } else {
      header.classList.remove('sticky');
    }
  }

  window.onscroll = () => {
    stickyControl();
  };

  stickyControl();
}

Once the user scrolls down a set amount of pixels (80 in our case), it adds a class of sticky. Otherwise, if the scroll position is above that, it removes the sticky class. Using the onscroll event listener, we’re always keeping tabs on where the scroll position is and calling the stickyControl function to run our check. stickyControl() is called at the bottom to kick off the check immediately on page load.

CSS

The real team player here is the CSS that animates in the header once it gets the sticky class added.

.header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px;
  background: #bada55;
  margin-top: 40px;
  transform: translateY(-40px);
  transition: transform .2s;

  &.sticky {
    position: fixed;
    transform: translateY(0);
    margin-top: 0;
  }
}

The highlight is the transform property. The margin-top: 40px moves the element down and the transform: translateY(-40px) moves it back upwards to make an even 0 – flush with the top of the page. The reason for this is so we can animate the transform property to make it appear to slide down once the sticky class gets added. Without two properties controlling the positioning, an animation wouldn’t be possible.

And to keep the element fixed over the top of the rest of the page, we switch from position: absolute to position: fixed.

You can control how fast the header slides down and up by modifying the transition value.

Demo

See the Pen Simple animated sticky header with plain JavaScript and CSS by Trevan Hetzel (@trevanhetzel) on CodePen.

What about position: sticky?

The sticky value on the position property is meant for cases like this (minus the animation) and could be used in place of position: fixed, but browser support isn’t 100% there, it’d only save a few lines of CSS, and we wouldn’t get the desired “slide down once the user has scrolled to the end of the header” effect. Try it out if you’d like though, it’s also a great solution!