Origin is out. See it

Motion & accessibility

Edit on GitHub

Blocks animate with motion/react and honour the system reduced-motion setting.

Motion is part of the craft, never a gimmick. Every block animates with intent — numbers roll, lines draw, rows slide in — and every one of those animations respects the visitor who prefers less motion.

The motion library

All animation is done with motion/react, the library behind Framer Motion. Blocks import a small set of primitives — motion, useInView, useReducedMotion and, where needed, animate and AnimatePresence. Nothing else is used for animation.

How blocks animate

  • Entrance plays once, when the block scrolls into view, and never repeats.
  • Easing is a soft ease-out curve like [0.22, 1, 0.36, 1], with durations between 0.5 and 1.2 seconds and small, subtle staggers.
  • No bounce, no elastic, no infinite attention-seeking loops.
  • Blocks animate transform, opacity and SVG path length, not layout properties, so motion stays smooth.

A block's entrance looks like this:

"use client";

import { useRef } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";

export function Example() {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const inView = useInView(ref, { once: true, margin: "-10% 0px" });

  return (
    <motion.div
      ref={ref}
      initial={reduce ? false : { opacity: 0, y: 16 }}
      animate={inView ? { opacity: 1, y: 0 } : undefined}
      transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
    >
      {/* your content */}
    </motion.div>
  );
}

Reduced motion

Every animation is gated behind useReducedMotion(). When a visitor has enabled reduced motion in their system settings, the block renders its final state immediately — fully visible, with no movement.

Reduced motion is not a smaller animation, it is no animation: content is complete and readable the moment it appears.

Keyboard

Anything interactive is a real, focusable element:

  • Buttons for every control, with a visible focus-visible:ring-2 ring.
  • Tooltips also appear on keyboard focus, not just hover.
  • Charts are not keyboard traps — they expose their meaning through text, not only colour.

Screen readers

  • Charts use role="img" and an aria-label that summarises the data.
  • Live regions announce changes (for example the live feed) politely with aria-live.
  • Text meets AA contrast, and colour is never the only signal for a value.

Building your own blocks? Follow the same rules and they will feel like part of the set. See the contributing guide for the full list.