<p>Scroll down</p>
<div style="--tonum:2000"></div>
<div style="--tonum:1000"></div>
@property --num {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}

p {
  padding: 1rem 2rem;
  margin-bottom: 400px;
}

div {
  counter-reset: num var(--tonum);
  font: 800 40px system-ui;
  margin-top: 200px;
  padding: 2rem;
}
div::after {
  content: counter(num);
}

.zero {
  counter-reset: num var(--num);
}
.animate {
  animation: counter 5s forwards ease-out 0.5s;
}

@keyframes counter {
  from {
    --num: 0;
  }
  to {
    --num: var(--tonum);
  }
}

@media (prefers-reduced-motion: reduce) {
  div {
    animation: none !important;
  }
}
View Compiled
const mediaQuery = window.matchMedia("(prefers-reduced-motion:reduce)");

if (
  "IntersectionObserver" in window &&
  window.CSS &&
  CSS.registerProperty &&
  !mediaQuery.matches
) {
  var targets = document.querySelectorAll("div");

  [].forEach.call(targets, function (target) {
    target.classList.add("zero"); // update to 0
  });

  var options = {
    root: null,
    threshold: [0.5]
  };

  var io = new IntersectionObserver(function (entries, self) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add("animate"); // animate to new number
        self.unobserve(entry.target); // don't animate again
      }
    });
  }, options);

  [].forEach.call(targets, function (target) {
    io.observe(target);
  });
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.