SEO-friendly, JS-triggered CSS animations

Published Sun Jan 08 2023 12:00:00 GMT+0000 (Coordinated Universal Time)
Tech
CSS allows us to create all sorts of transitions. For example, content fading in as we scroll it into view, as often seen on Apple product pages
.
The styling is simple. The element has a class applied which sets opacity: 0. When the element is scrolled into view, JavaScript removes this class, causing the opacity to revert to 1. Throw in a simple transition: 0.5s opacity ease-in-out and you have yourself a visually pleasing transition.
<div class="animated hidden">Content</div>
HTML
.animated {
  transition: .5s opacity ease-in-out;
}
.hidden {
  opacity: 0;
}
CSS
setTimeout(()=>{
  const elements = [...document.getElementsByClassName('hidden')]
  elements.forEach(e=>{
    element.classList.remove("hidden")
  })
}, 500)
JavaScript

Is that it?

For the most part, yes. But we're not done yet. If we load any of Apple's fancy landing pages with JavaScript disabled, the content is immediately visible. It doesn't fade in nicely, but it's there. For people with old browsers, JS disabled, or for search engine crawlers, it's there.
My initial thought was that they are rendering both animated and non-animated versions of the content alongside each other using noscript tags, but this isn't what they are doing. Probably because noscript tags can cause unintended SEO consequences.
Instead, the way they achieve this is by nesting class selectors. The page's HTML tag has a class name indicating whether JS is available or not.
<html class="js">
  …
  <div class="animated hidden">Content</div>
  …
</html>
HTML
The hidden CSS class only affects the content when an ancestor element has the js class. This is achieved in CSS as follows:
.animated {
  background: red;
  height: 5rem;
  width: 5rem;
  margin-bottom: 2rem;
  transition: .5s opacity ease-in-out, .2s margin ease-in-out;
}
.js .hidden {
  margin-top: 8rem;
  opacity: 0;
}
CSS
When the HTML initially comes from the server, it has the no-js class. When the page is loaded in the browser, a lightweight script immediately changes this to js to enable animations. (This only happens if JS is enabled.)
The exact JS code that does this on apple.com is in head.built.js
:
this._target.classList.remove("no-js")
this._target.classList.add("js")
JavaScript

Summary

By using nested CSS, we can ensure that hidden effects are only applied if JS is enabled. They avoid common pitfalls of noscript. By leveraging nested CSS selectors and using a lightweight script to enable hidden styles via CSS, we can avoid the initial flash of content that a lot of sites show when they try to implement SEO-friendly enter animations,

Copyright

Most of my photos are licensed under Creative Commons BY-SA 3.0
.
If you are unsure about your right to use them please contact me.