Skip to content

Fade-in elements on scroll

Problem. Reveal sections as they enter the viewport, but only animate for users without reduced-motion.

Solution. Use inView and respect prefersReducedMotion.

ts
import { inView, animate } from '@bquery/bquery/motion';
import { prefersReducedMotion } from '@bquery/bquery/a11y';

const reducedMotion = prefersReducedMotion();

document.querySelectorAll<HTMLElement>('.reveal').forEach((el) => {
  inView(
    el,
    () => {
      if (reducedMotion.value) {
        el.style.opacity = '1';
        return;
      }
      animate(
        el,
        { opacity: [0, 1], transform: ['translateY(20px)', 'translateY(0)'] },
        { duration: 300 }
      );
    },
    { once: true }
  );
});

Why it works. inView uses IntersectionObserver and only fires once per element when { once: true }; reduced-motion users skip the keyframe animation entirely.

Released under the MIT License.