https://twitter.com/emilkowalski_/status/1592997196911501312

If you’re like most people, you probably saw this tweet and thought it was ✨ magic ✨ It’s been some time since I wrote some CSS, so I thought I’d give reproducing the effect a go.

https://codepen.io/aarohmankad/pen/eYKrxqm

The Template

The comments gave me a small hint towards how to accomplish this:

https://twitter.com/artur_bien/status/1593021503259672576

So I started with two identical DOM trees, and some pretty basic light/dark mode CSS. (We’ll toggle the classes on the body tag later.)

<body class="lightMode">
  <div class="dark">
    hello
    <button>toggle</button>
  </div>
  <div class="light">
    hello
    <button>toggle</button>
  </div>
</body>
body {
  margin: 0;
  padding: 0;
}

body > div.light {
  background: white;
  color: black;
}

body > div.dark {
  background: black;
  color: white;
}

body > div {
  position: absolute;
  height: 100%;
  width: 100%;
}

Animation

This will render the light mode on top of the dark mode, and I was planning on using the clip-path to transition between the two of them. First, understanding the clip-path API enough to make some animations. From the examples on MDN, I saw that I could use the inset option to set some keyframes. Some experimentation led to this

@keyframes down-enter {
  0% {
    clip-path: inset(0 0 100% 0);
  }
  100% {
    clip-path: inset(0);
  }
}
@keyframes down-leave {
  0% {
    clip-path: inset(0);
  }
  100% {
    clip-path: inset(100% 0 0 0);
  }
}

Controlling States

We’ll add an on click handler to the DOM to let us toggle between the dark and light mode states.

document.querySelectorAll("button").forEach((b) =>
  b.addEventListener("click", () => {
    document.querySelector("body").classList.toggle("darkMode");
    document.querySelector("body").classList.toggle("lightMode");
  })
);

Transitioning between States

Now that we’re toggling all the CSS classes properly, we can add some transitions between the two states

body.darkMode > div.dark {
  animation: 1s down-enter;
}

body.darkMode > div.light {
  animation: 1s down-leave;
}

body.lightMode > div.light {
  animation: 1s down-enter;
}

body.lightMode > div.dark {
  animation: 1s down-leave;
}

However, at this point you’ll notice a small flicker after toggling between states.

Screen Recording 2022-11-25 at 4.56.29 PM.mov