<label id="motionToggle">
 <input type="checkbox" id="reducemotion" name="reducemotion">
 Reduce motion
</label>

<section>
 <div class="box css">CSS</div>
 <div class="box js">JS</div>
</section>
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@300;400&display=swap");

body {
 display: flex;
 align-items: center;
 justify-content: center;
 flex-direction: column;
 min-height: 100vh;
 padding: 0;
 margin: 0;
 font-family: "Nunito", sans-serif;
 font-weight: 400;
}

section {
 display: flex;
 align-items: center;
 justify-content: center;
 flex-wrap: wrap;
 margin-top: 2rem;
}

.box {
 width: 100px;
 height: 100px;
 border-radius: 20px;
 margin: 2rem;
 color: white;
 display: flex;
 align-items: center;
 justify-content: center;
}

.css {
 background-color: #654cb7;
}

.js {
 background-color: #23b195;
}

.css {
 animation: spin 4s infinite linear;
}

@keyframes spin {
 from {
  transform: rotate(0deg);
 }
 to {
  transform: rotate(360deg);
 }
}

@media (prefers-reduced-motion: reduce) {
 *,
 *::before,
 *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
 }

 #motionToggle {
  display: none;
 }
}

.reducedMotion *,
.reducedMotion *::before,
.reducedMotion *::after {
 animation-duration: 0.01ms !important;
 animation-iteration-count: 1 !important;
 transition-duration: 0.01ms !important;
 scroll-behavior: auto !important;
}
const toggle = document.querySelector("#reducemotion");
let motionQuery = matchMedia("(prefers-reduced-motion)");

// our GSAP animation
let tween = gsap.to(".js", {
 rotate: 360,
 duration: 4,
 repeat: -1,
 ease: "none",
 paused: true
});

const handleReduceMotion = () => {
 if (motionQuery.matches || toggle.checked) {
  tween.progress(1).pause();
  document.body.classList.add("reducedMotion");
 } else {
  tween.play();
  document.body.classList.remove("reducedMotion");
 }
};

// update if the OS level setting or toggle changes
motionQuery.addListener(handleReduceMotion);

toggle.addEventListener("click", () => {
 handleReduceMotion();
});

// call it up front to set the initial state of the animation
handleReduceMotion();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.co/gsap@3/dist/gsap.min.js