Motion Specimens

Candidate effects for a motion wave. Scroll, and judge.

There are two different mechanisms on this page and the difference decides what a wave costs. Scroll-driven ties an animation’s progress to scroll position through animation-timeline: pure CSS, free to author, and absent in browsers that have not shipped it — where the property is dropped and the element lands on its finished state immediately. Scroll-triggered uses an observer to add a class and then runs an ordinary timed animation: it works everywhere, runs once and stays put, and needs a runtime behavior in the renderer because templates ship no JavaScript.

Where an effect can be built both ways, both are shown. The panel below reports which mechanisms this browser actually has, and each specimen carries a live diagnostic line.

Your browser, right now

01 — Reveal — the two mechanisms side by side

animation-timeline: view()  vs  IntersectionObserver + transition

The same fade-and-lift built both ways. The first row is scroll-driven: pure CSS, free to author, and it reverses as you scroll back. The second is scroll-triggered: an observer adds a class and an ordinary timed transition runs, once, and stays. If only the second row moves for you, this browser has no scroll-driven animation — and the triggered route is the one a wave would have to use.

Scroll-driven — pure CSS, no code cost

Hero Feature

Full-bleed picture, copy over it.

Statement Figure

One number, one line, full width.

CTA Band

Gradient band, one dominant action.

Scroll-triggered, then timed — needs a runtime behavior

Hero Feature

Full-bleed picture, copy over it.

Statement Figure

One number, one line, full width.

CTA Band

Gradient band, one dominant action.

waiting…

Applies to all 91 designs. The triggered version is the one that runs everywhere.

02 — Staggered reveal

animation-range  vs  transition-delay on a trigger

A stagger is not a second effect — it is how the children of one reveal are timed. Scroll-driven, the offset has to be a shifted animation-range, because a time delay means nothing on a progress-based timeline. Triggered, it is an ordinary transition-delay, which is both simpler and the thing most people expect a stagger to be.

Scroll-driven, offset by animation-range

Solo

£19 a month

Crew

£39 a month

Firm

£89 a month

Bespoke

Talk to us

Triggered, offset by transition-delay

Solo

£19 a month

Crew

£39 a month

Firm

£89 a month

Bespoke

Talk to us

waiting…

49 designs have a collection to stagger.

03 — Drifting ground

keyframes, infinite alternate — back to 26s

Back to the original pace, since the speed was never the problem: the first version drifted a gradient, and a gradient has no features to judge movement by. On a photograph the same 26 seconds reads clearly.

Get a fixed price before anyone climbs a ladder

The survey fee comes off the cost of any work you go ahead with.

Fits the 4 designs with a picture ground. Plain keyframes, so it runs everywhere.

04 — Drifting gradient

animated background-position — your idea

The version that works without a picture. Nothing moves: the gradient is drawn three times the size of the box and its position travels, so the color under any given point keeps changing. That is the difference from the first attempt, which slid a gradient bodily and looked static because every part of it was identical to every other part.

Twelve years on the same roofs

No photograph, no picture to source, and it costs one keyframe.

Fits every gradient design in the showpiece wave — cta-band, hero-gradient, statement-figure. Free, and it runs everywhere.

05 — Parallax, driven by the page scroll

runtime writes --p, the sheet spends it

The runtime reads the band’s position against the viewport and writes a 0-to-1 progress as --p; the stylesheet spends it, moving the picture one way and the copy the other. The travel is the picture’s own overhang — the height it renders at for the width it is given, less the height of the frame — so --p 0 shows the very top of the image and 1 shows the very bottom, and the whole thing is seen across the crossing rather than a band through its middle. That number cannot live in a stylesheet: a 4:3 photograph has hundreds of pixels to give and a 3:1 banner has almost none, so the runtime measures it. Scroll the page.

Twelve years on the same roofs

The picture travels down, the words travel up, and the gap between them is the depth.

–p 0.500
measuring…

Heroes and full-bleed bands. Costs a behavior in pattern_render.js — the same one that serves any effect wanting scroll progress. The fitness check under the band is the second half of the job: a short, wide picture has nothing to travel through, and the designer has to be told at authoring time rather than the buyer finding out on a live page.

06 — Counters

two techniques, and only one is free

The first row is scrubbed by scroll position: pure CSS, but it runs backwards when you scroll up and never settles. The second is triggered then timed — it runs once and stays, which is what a figure on a page should do.

Scrubbed by scroll — pure CSS, free

Designs
With collections
Worth animating

Triggered, then timed — needs a runtime behavior

Jobs completed
First-visit fixes %
Saved for clients k
waiting…

3 designs carry a stat module, plus statement-figure.

07 — Ribbon

keyframes, infinite linear — seam fixed

Three things were wrong. The halves differed by one gap, so -50% landed short; the track was translated in 2d, so the browser snapped it to whole pixels and ticked at the seam; and six names meant the duplicate copy was on screen beside the original, which reads as a jump whatever the seam is doing. Now: equal halves each carrying their own trailing gap, a compositor-friendly translate3d, and sixteen names so the second copy is always off screen. It also pauses on hover so a name can be read.

Kestrel RoofingHarbor FoodsKestrel CourtBromley EstatesForest Hill TerraceMarlow & SonsPennington EstatesLambourne FacilitiesAshgrove Managed HomesVerity PropertySouthwark LettingsChalfont GroupRenshaw & DoyleOakfield TrustBramley CourtHighgate Residential

One design: logo-strip. Still not user-proof: it works because sixteen names happen to overflow this container. A buyer with four would see the seam again, which is why the duplication has to be the module’s job rather than the author’s.

08 — Scroll progress

scroll(root), with a listener where that is unsupported

The rule pinned at the top of the window. Where scroll() exists it is pure CSS; where it does not, the bar was showing full green because the dropped property left the animation on its end state. It now falls back to a scroll listener, and the panel above says which route is running.

waiting…

A new design. Free in Chrome, a runtime behavior everywhere else.

09 — Sticky stack

position: sticky, no animation at all

Each card pins under the one before it and the next slides over the top. Nothing here is animated, which is why it behaves identically in every browser. The stepping you saw is your wheel scrolling by lines rather than pixels — drag the scrollbar or use a trackpad and it is smooth. That is the scroller, not the design, and it would do the same to any sticky layout.

Stage one

You tell us what happened

A phone call, or a photograph sent to the office. Nine times out of ten that is enough to know what we are coming to look at.

Stage two

We survey and put a price on it

Two hours on site including the loft, a drone pass over anything we cannot safely walk, and a fixed written quote inside five working days.

Stage three

We do the work on the day we said

Same crew who quoted it, in most cases. If the weather moves us we call you the day before.

A new design, and the cheapest here.

10 — Ribbon that fills itself

runtime repeats the list until one cycle clears the container

Specimen 07 loops smoothly only because sixteen names happen to overflow the strip — it is not fixed, it is lucky. Here the runtime takes whatever list it is handed, repeats it until one cycle is at least as wide as the container, lays two cycles end to end and translates by exactly one, so the seam is always off screen at any count. The speed is set from the cycle width rather than fixed, so a short list does not crawl and a long one does not race. Nothing here depends on how many logos the buyer has. Tested at three, six and fifteen items — all three filled correctly and ran at the same 55px per second.

Kestrel RoofingHarbor FoodsKestrel CourtBromley EstatesForest Hill TerraceMarlow & SonsPennington EstatesLambourne FacilitiesAshgrove Managed HomesVerity PropertySouthwark LettingsChalfont GroupRenshaw & DoyleOakfield TrustBramley Court

This is the shape the ribbon module would take. The duplication is the runtime’s job, not the author’s — which is the whole difference between a marquee that survives a buyer and one that does not.

What a motion wave would actually cost

EffectMechanismDesignsRuns everywhereCode cost
RevealTriggered91YesRuntime behavior
Staggered revealTriggered49YesSame behavior
Drifting groundKeyframes4YesNone
Drifting gradientKeyframes3+YesNone
ParallaxPerspective4YesNone, but needs its own scroller
CounterTriggered4YesSame behavior
RibbonKeyframes1YesNone
Scroll progressscroll() or listener+1With fallbackRuntime behavior
Sticky stackposition: sticky+1YesNone

One runtime behavior — an observer that adds a class when an element comes into view — covers reveal, stagger and the counters, which is the bulk of the wave. Everything else is authoring. Nothing here is decided.