Skip to content
A template built entirely with keel — including the one category keel's own components can't cover alone: page-local vanilla JS. Every effect here respects reduced-motion. All templates

Motion technique study

Four ways to move a pixel with JavaScript.

keel ships zero JavaScript by design — its animations are CSS, and every one of them is gated behind prefers-reduced-motion. This page is the deliberate exception: a reference for the handful of effects that need a script, built the same way you'd build them in a real project — small, page-local, and contained to one section each.

Every technique below checks matchMedia("(prefers-reduced-motion: reduce)") before it starts. If your system asks for reduced motion, each section falls back to a static, fully readable state instead of running its animation loop — try it in your OS accessibility settings and reload.

01 · Pointer tracking

Cursor follower

A dot eases toward the pointer instead of snapping to it: each frame it moves a fraction of the remaining distance (linear interpolation, or "lerp"), driven by requestAnimationFrame. The pointermove listener is bound to this box alone, not document, so it never reads mouse movement anywhere else on the page.

Move your pointer inside this box

02 · Scroll progress

Scroll-driven scene

This box scrolls internally (overflow-y: auto) so the effect stays put instead of hijacking the page. A scroll listener on the box itself computes progress and fades/rises a card into place — pure scroll-position math, no library.

Scroll down inside this box ↓

Frame 04

The card resolves as you scroll

Opacity and position are set from the box's own scroll offset — reload with reduced motion on and this card just stays fully visible, no listener attached.

03 · Layered depth

Parallax on scroll

Three circles move at three different rates as the page scrolls this section into and out of view — the classic depth illusion. One shared requestAnimationFrame loop reads this section's getBoundingClientRect() each frame and stops updating once the section leaves the viewport, so it never runs unnecessarily.

scroll the page

04 · Pointer-relative rotation

Tilt-on-hover card

The card rotates on two axes based on where your pointer sits inside it — rotateX from vertical position, rotateY from horizontal — applied with a plain CSS transform, reset on pointerleave. No animation loop needed; the browser's own compositor handles the easing via a CSS transition.

Hover to tilt

Depth without a library

Twelve lines of JS, one CSS transition, and a perspective on the wrapper.

That's the whole toolbox

Four techniques, four small functions, one reduced-motion check apiece. Copy the <script> at the bottom of this file's source — it's the entire dependency.

All templates