Feature Playground

Scroll-driven · 1.18

Scroll-swept before/after

A squeegee pulled across a fogged window by the scrollbar. The work happens as you pass it.

Two unaltered frames under one straight seam; scroll drives the seam across while the pair is fully on screen, and any drag takes the seam and keeps it.

3 knobs

How it actually works

The before/after is the entire sales argument of every trade that cleans, lights, or restores something. A drag slider makes the visitor do the work; a triggered crossfade does it without them. Scrubbing the seam from the scrollbar keeps the causality — you move, the work happens — while the drag override keeps the visitor in charge the moment they want to compare a detail.

The AFTER frame is the top layer, clipped with inset(0 calc(100% - seam) 0 0) over the BEFORE frame; both render at the pair's own intrinsic aspect, uncropped, so the seam only chooses visibility, never pixels. Progress is the frame's travel WHILE FULLY VISIBLE: p = 0 the moment its bottom edge has fully entered (top == vh - h), p = 1 the moment its top edge is about to leave (top == 0), clamped so a visitor can always see both extremes whole — the first build ran it over the whole viewport transit and neither end could ever be seen; that was the one fix the client asked for. The seam maps start + p*(end - start). A real <input type=range> covers the frame for keyboard and AT; any manual input sets a flag the scroll job respects, so drag is an override, not a fight.

The knobs, named

Start seam, end seam, and the travel window. Start and end are the 4/5 marks the client specified by eye; the window decides how much of the fully-visible travel the sweep occupies.

KnobSourceWhat it teaches
Start seam sourced Where the seam rests as the pair finishes entering. 20 is the client's "4/5 left".
End seam sourced Where it finishes as the pair starts to leave. 80 is the "4/5 right".
Travel window ours Fraction of the fully-visible travel the sweep uses, centred. Below 1 the sweep runs faster in the middle of the pass.

sourced means the source names this parameter. ours means the source names none and the knob is our design against the mechanism. No knob here is invented and passed off as sourced.

Evidence

VERIFIED (ours, shipped)

clients/harrison-handyman assets/app.js (v3 rebuild, 2026-07-26). Andrew's verdict same day: "The before vs after left-to-right reveal playground feature is excellent. Save that for the future." The fully-visible window is his one requested fix, applied.

Seen on
harrison-handyman v3 (ours), all three real photo pairs. The manual-drag ancestor was the standard twenty-twenty slider seen everywhere.
Dependencies
vanilla: clip-path + one shared rAF + <input type=range>
Difficulty
easy
Performance
One custom property write per frame per visible pair; clip-path on a composited layer; no filters, no layout writes.
Accessibility and the floor
The range input is the control surface: keyboard-operable, aria-valuetext in words. Floor (no JS / reduced motion): seam rests mid-frame, both frames and both labels visible. Nothing is hidden behind the motion.

Notes

Composability. Pairs with a night/dark band for a lights-coming-on read. Do not stack two swept pairs in one viewport; each needs its own full-visibility window to complete.

The one bug that matters: driving the seam over the pair's whole viewport transit means the start state exists only below the fold and the end state only above it — the two photographs the section exists to show are the two things nobody can look at. Compute the window from (vh - height) and clamp; frames taller than the viewport get a forced mid-transit window because full visibility never happens.