The state of your sticky element

27 oktober 2020
Frontend ontwikkelaar

When styling your page and working out the perfect UI/UX for your users, you often find yourself in the situation that you want to apply specific styling to an element under certain specified conditions.
A common example would be an input field that is clicked and ready to be filled with input, we apply styling to the focused state like so:

input {
  width: 100px;
  height: 20px;
  border: 1px solid grey;

input::focus {
  border: 1px solid blue;

It's that simple, we have a grey border on default, but when the input is focused the input gets a blue border.

Well, let's do the same for a sticky element, shall we?

.element__with-input {
  width: 100px;
  height: 20px;
  position: sticky;

.element__with-input::stuck {
  background-color: gooeygreen;

Hate to break it to you, but this is not going to work, there is no pseudo-element called '::stuck'... There is no native way to see when your element is stuck or not.

So, our situation right now is that we have a working sticky element but no way to apply styling to it when it's in a stuck state.

We need to build something ourselves, so, what do we need?

  • A way to observe whether the element that has position:sticky has passed some sort of boundary.
  • Apply or toggle a second class to the element that indicates when that boundary has been crossed ( got stuck ).
  • We also need to figure out when the element is out of its sticky state and is 'unstuck' to un-toggle the specific styling.

Let's get to work,

we need a way to observe... since we are using Typescript here at YTEC, I made a sticky-element-handler.ts and within that file, I made use of an Intersection Observer
This will give me the ability to asynchronously observe whether my element has been intersected.

I added -2 pixels to the bottom of the sticky element and when the Y-offset of the viewport is lower than 1 intersected pixel, the code will trigger the --stuck class
giving the sticky element the following classes: element__with-input element__with-input--stuck.

Awesome, now we can add styling in the CSS or in my case the SCSS file by targeting the element__with-input--stuck class which will overwrite the element__with-input class.

All in all, we made an observer to see a certain threshold has been set in order to toggle a stuck specific class to the existing element for us to style!
In a perfect world we would have a ::stuck pseudo-element like hover, focus, before, after, etc.

By viewing the code you will get a better understanding of the observer at work:

export class StickyChecker {
  constructor() {
    const stickyElement = document.querySelector(".event-detail__actions");
    const observer = new IntersectionObserver(
      ([e]) =>
          e.intersectionRatio < 1
        threshold: [1],