The AutoTouch product page needed a hero that shows the app working: tap points being placed, timing being set, a countdown, and a run in progress. Screenshots could show one of those moments at a time, at one resolution, in one language. So the hero draws the app itself, in HTML and CSS.
One component, four scenes
AutoTouchDemo renders the overlay as ordinary DOM inside a CSS phone chassis called DeviceFrame.
There is no image asset anywhere in the frame.
The component takes a mode, and each mode is a different moment from the same routine.
type AutoTouchDemoMode = "scrub" | "place" | "timing" | "run" | "static";place, timing and run are fixed frames used inline further down the page.
scrub is the hero, where the scene advances as you scroll.
Scroll drives the story
The scrubbed hero maps scroll progress onto each element of the scene. Markers fade and rise into place first, then the timing panel, then the countdown, then the running state.
const markerOpacity = useTransform(progress, [0.1, 0.24], [0, 1]);
const timingOpacity = useTransform(progress, [0.34, 0.43, 0.58, 0.64], [0, 1, 1, 0]);
const countdownOpacity = useTransform(progress, [0.62, 0.7, 0.77, 0.82], [0, 1, 1, 0]);
const runningOpacity = useTransform(progress, [0.8, 0.9], [0, 1]);Each range is a window of the scroll, so the whole sequence is one tall sticky section rather than a timeline with its own clock. Scrolling back up plays it in reverse for free.
Small screens and reduced motion
Below the md breakpoint the scrubbed section is hidden and a single static frame is shown instead.
The same swap happens for anyone who has asked their system to reduce motion, and it happens in CSS:
@media (prefers-reduced-motion: reduce) {
.autotouch-demo-scrub {
display: none !important;
}
.autotouch-demo-static {
display: block !important;
}
}Doing this in JavaScript would mean rendering the animated version on the server, then correcting it after hydration. That correction is itself a visible motion, which is the one thing the setting asks us not to do.
What it costs
The demo has to be kept in step with the real app by hand. When the overlay changes, the component changes with it. In exchange, the hero stays sharp at any size, follows the page's light and dark tones, and is described to screen readers as a single image with a plain-language label.