Sticky showcase
The features section of a product page, written as plain markup: one block per step, a picture or a video and a block of words, any number of steps.
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialTwo things belong to the page rather than to the component. No parent of the block may cut its overflow withoverflow: hidden, which stops the media from pinning: useoverflow-x: clipwhere a parent has to cut. And the block lays itself out: the Embed reads--sticky-showcase-share(the width of the media column),--sticky-showcase-inset-xand--sticky-showcase-inset-y(the air around the media),--sticky-showcase-step(the scroll a step takes),--sticky-showcase-top(the height of a fixed header),--sticky-showcase-stackand--sticky-showcase-ground(the height of the pinned media and the colour behind it on a phone). Set them in a rule of your own on the block itself: a value set on a parent is not inherited. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-sticky-showcase Marks the block that holds the steps. It takes no value: leave the value field empty. That block becomes the grid of two columns, the words in one and the pinned media in the other, and it carries the settings below. data-sticky-showcase-step On each step, a direct child of the block. It takes no value. A step holds two blocks, its media and its words. Duplicate one to add a step, delete one to remove it. With fewer than two the block is left alone and the engine says so in the console. [ Required ] data-sticky-showcase-media On the box of a step that holds its picture or its video, and nothing else. It takes no value. Every media is pinned in the same place, and only the one of the step being read is seen. A step without it is skipped. [ Required ] data-sticky-showcase-rail On a direct child of the block: a block pinned over the media, for a counter. It takes no value. Without it nothing is pinned but the media. [ Optional ] data-sticky-showcase-current On a text inside the block: it receives the number of the step on the reading line, 02 for the second. It takes no value. [ Optional ] data-sticky-showcase-total On a text inside the block: it receives how many steps there are, 05 for five. It takes no value. [ Optional ] data-sticky-showcase-state Written by the engine on the block: live, or still under reduced motion. While the fins turn the block also carries data-sticky-showcase-turning, and the step on the reading line data-sticky-showcase-active. Never written by hand; the stylesheet reads them, and so can a rule of yours. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-sticky-showcase-accent #ffab1a The colour of the edge of a fin, seen for the instant the fin is edge on. Options: any CSS colour. The amber of the preview by default; the colour of the page's ground to show no edge at all. data-sticky-showcase-slats 14 How many upright fins the media is cut into, from 2 to 48. data-sticky-showcase-wave 0.65 How far the fins turn one after another rather than together, from 0 to 1. data-sticky-showcase-hold 0.45 The share of the travel between two texts during which the media stays still, half at the start and half at the end, from 0 to 0.9. data-sticky-showcase-line 0.5 Where the reading line is, down the window, from 0.1 near the top to 0.9 near the foot: the text on it is the step whose media is shown. On a narrow screen the line is the middle of what the pinned media leaves, whatever this says. data-sticky-showcase-smooth 0.12 The time the turn takes to catch up with the scroll, in seconds, from 0 to 2. data-sticky-showcase-dim 0.55 The opacity of the texts that are not on the reading line, from 0 to 1. data-sticky-showcase-fit own What the frame of the media does with pictures of different ratios. Options: own, the frame takes the ratio of each picture in turn, as large as its column allows, and goes from one ratio to the next during the turn; cover, the frame fills its column and every picture covers it, cropped around its object-position. A narrow screen always covers. data-sticky-showcase-side right The column the pinned media is in. Options: right, the words on the left and the media on the right; left, the other way round, and the fins then turn from the side nearest the words all the same.
Two files and their pictures (5), no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
StickyShowcase.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-sticky-showcase-accent #ffab1a The colour of the edge of a fin, seen for the instant the fin is edge on. Options: any CSS colour. The amber of the preview by default; the colour of the page's ground to show no edge at all. data-sticky-showcase-slats 14 How many upright fins the media is cut into, from 2 to 48. Fewer and wider fins, a heavier and slower-looking turn; under 6 it reads as doors more than as louvres Finer fins and a tighter ripple; over 24 each fin is a sliver on a phone and the pictures are hard to read in flight data-sticky-showcase-wave 0.65 How far the fins turn one after another rather than together, from 0 to 1. The fins turn more in step; at 0 they all turn at once, as one shutter The turn travels across the media as a wave; at 1 a fin has hardly started when the first has finished data-sticky-showcase-hold 0.45 The share of the travel between two texts during which the media stays still, half at the start and half at the end, from 0 to 0.9. The fins start turning as soon as a text leaves the reading line, and the turn is spread over more scroll, so it is slower under the finger; at 0 the media is never at rest between two steps The picture stays whole longer around each text and the turn is played over a shorter scroll, so it is quicker; over 0.7 it starts to look triggered data-sticky-showcase-line 0.5 Where the reading line is, down the window, from 0.1 near the top to 0.9 near the foot: the text on it is the step whose media is shown. On a narrow screen the line is the middle of what the pinned media leaves, whatever this says. The media changes later, when the next text is higher in the window The media changes sooner, when the next text has only just come in data-sticky-showcase-smooth 0.12 The time the turn takes to catch up with the scroll, in seconds, from 0 to 2. The fins follow the wheel more exactly; at 0 each notch of a mouse wheel is a small jump The turn glides on after the scroll stops; over 0.3 it feels late data-sticky-showcase-dim 0.55 The opacity of the texts that are not on the reading line, from 0 to 1. The other texts fade further back; at 0 only the text being read is seen The other texts stay readable; at 1 nothing fades data-sticky-showcase-fit own What the frame of the media does with pictures of different ratios. Options: own, the frame takes the ratio of each picture in turn, as large as its column allows, and goes from one ratio to the next during the turn; cover, the frame fills its column and every picture covers it, cropped around its object-position. A narrow screen always covers. data-sticky-showcase-side right The column the pinned media is in. Options: right, the words on the left and the media on the right; left, the other way round, and the fins then turn from the side nearest the words all the same. -
Drive it from JavaScript
When a script follows which step is being read, scrolls to one, or changes the steps after load.const element = document.querySelector('[data-sticky-showcase]'); element.addEventListener('stickyshowcase:change', (e) => { console.log(e.detail); }); // Follow the step on the reading line: a menu that marks it, an analytics call. element.addEventListener('stickyshowcase:change', (event) => { console.log(event.detail.index, event.detail.step); }); const showcase = StickyShowcase.mount(element); // the block auto() mounted, or a new one showcase.goTo(2); // scrolls to the third step showcase.progress(); // 0 to 1, the place among the steps showcase.refresh(); // after adding a step or swapping a picture // When the section leaves the page: StickyShowcase.destroy(element); // removes the fins and gives the markup back as it was
-
Fit it to your page
Steps
- One block per step, a direct child of the block marked
data-sticky-showcase. In it, two blocks: the box markeddata-sticky-showcase-media, and the words, which hold anything, a label, a heading, paragraphs, a link, a list. - To add a step, copy one; to remove one, delete it; to reorder, move it. In Webflow, duplicate a step in the Navigator. Two is the least the engine takes; three to eight read well.
- **The media is one
<img>, one<picture>or one<video>.** Give an image itswidth, itsheightand analt; give a videomuted,playsinline,loop,preload="auto"and aposter. The engine plays the video of the step on the reading line and pauses the others. - Ratios may be mixed. With
fitatowneach picture is shown whole, as large as its column allows, and the frame changes shape from one to the next. Withcoverevery picture fills the same frame. - Texts may be of any length. Each takes at least the height
--sticky-showcase-step, 90% of the window, and more when it is longer. - Several showcases on a page each work on their own.
The layout
The mechanics lay the block out themselves, and read nine custom properties, set at zero specificity on the block. Give them your own values in a rule on the block itself, whatever its selector; a value set on a parent is not inherited, since the block declares its own:
--sticky-showcase-share: the width of the media column,56%.--sticky-showcase-inset-xand--sticky-showcase-inset-y: the air around the media inside its column, across and down.--sticky-showcase-step: the least height of the words of a step, which is the scroll a step takes.--sticky-showcase-top: where the media is pinned from the top of the window,0px. The height of a fixed header goes here.--sticky-showcase-stack: the height of the pinned media on a narrow screen,52svh.--sticky-showcase-ground: the colour behind the fins on a narrow screen, where the words pass under the media. The ground of the page.--sticky-showcase-accent: the edge of a fin. Also the settingaccent.--sticky-showcase-shade: how dark a fin gets as it turns away,0.55.
The type, the colours of the texts and their padding are the page's own: in the preview they are the
sticky-showcase-*classes.On a narrow screen
Under 768 px there is one column. The media is pinned across the top of the window, edge to edge,
--sticky-showcase-stacktall, and always covers its frame; the words are read in what is left of the screen and pass under the media as they leave. The reading line is the middle of that space.From JavaScript
StickyShowcase.mount(element, overrides)returns the instance of a block, mounting it if it was not:{ element, settings, state(), index(), count(), progress(), goTo(index), refresh(), destroy() }.goTo(2)scrolls the page until the text of the third step is on the reading line, the one of a narrow screen included.StickyShowcase.auto(root)mounts every marked block insideroot, and runs once on its own;StickyShowcase.destroy(element)gives the markup back.- One event, bubbling from the block:
stickyshowcase:change, on the first frame after the mount, then each time another step comes to the reading line, in both directions, with{ element, index, previous, step }indetail. - The state is in the markup:
data-sticky-showcase-stateon the block,liveorstill;data-sticky-showcase-turningon the block while the fins are in flight;data-sticky-showcase-activeon the step on the reading line;--sticky-showcase-nearon each step, from 0 to 1. - **Under
prefers-reduced-motion: reduce** there are no fins: the media of the step on the line fades in over the last one, and the words fade the same way. Videos are not started. - Without JavaScript, and on the canvas of the Webflow Designer, each step is its picture, then its words, one under the other.
- The words and the photographs of the demo are the demo's: on a real page every text and every picture is the user's own, or asked for.
- One block per step, a direct child of the block marked
-
Avoid the pitfalls
- No ancestor may cut its overflow.
overflow: hidden,autoorscrollon a parent of the block makes that parent the scroll container of the pinned media, and it no longer pins. Useoverflow-x: clipwhere a parent has to cut. - The steps are direct children of the block, and a step holds exactly two blocks. The stylesheet gives each step
display: contents, so a background, a border or a padding on the step itself shows nothing: put them on the words. - The block takes its own layout:
display: gridand its columns come from the mechanics. Change the columns with--sticky-showcase-share, not with adisplayof your own on the block. - The fins carry copies of the pictures. A picture swapped by a script after the mount needs
refresh(). Withsrcset, the copy takes the source the browser had chosen at the mount. - A video is drawn on the fins from its current frame, so a video that has not loaded yet leaves its fins empty during a turn: give it
preload="auto". Itsposteris what the pinned media shows at rest until then; the fins do not draw it. - Pictures of very different ratios make a frame that changes a lot. It is the point of
own; for a calmer page usecover. - The pinned media is one window tall. A fixed header over it hides its top: give
--sticky-showcase-topthe height of the header. - In React or any framework that owns the DOM, mount in an effect and call
destroy()in its cleanup: the engine adds one element to the block, the stage of the fins, and writes attributes and custom properties on the block and on the steps.
- No ancestor may cut its overflow.
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Sticky showcase is a scroll animation for Webflow and vanilla JavaScript, with no library. Also called change image on scroll sticky section, sticky scroll section or scrollytelling sticky image.
The features section of a product page, written as plain markup: one block per step, a picture or a video and a block of words, any number of steps. The words scroll down one column; the media of the step being read is pinned in the other. Between two steps the media is cut into upright fins that each carry the picture that leaves on one side and the picture that comes on the other, and they turn half a turn about their own middle, one after another, like the sun louvres of a facade. The turn is tied to the scroll: it advances, stops and runs back with the finger, and nothing is ever triggered. A fin seen edge on shows its edge in the accent, and loses its light as it turns away. The frame takes the ratio of each picture in turn and goes from one to the next while the fins turn, so portrait, landscape and square pictures mix without a crop or a letterbox. The words off the reading line fade back, by the distance they are from it. On a narrow screen the media is pinned edge to edge across the top and the words pass under it. The pin is CSS sticky positioning. Under reduced motion the pictures fade from one to the next; without JavaScript each step is its picture, then its words. No library. Pattern type sticky scroll section, scrollytelling, features section.
Updated
Questions
Mark a block data-sticky-showcase, each step inside it data-sticky-showcase-step, and in each step the box holding the picture data-sticky-showcase-media; the other block of the step is its text. The stylesheet pins every picture in the same place and the engine shows the one of the text being read. Duplicate a step in the Navigator to add one.
No. The pin is CSS sticky positioning on one grid cell, with no spacer and no scroll trigger, and the turn of the fins is read from the layout at every frame the scroll moves. No library is loaded, and Webflow.destroy() has nothing to take away.
Yes. With data-sticky-showcase-fit at own, the default, the frame takes the ratio of each picture in turn and goes from one to the next while the fins turn, so portrait and landscape mix with no crop; cover fills one frame instead. A step may hold a <video> with muted, playsinline and loop: the engine plays the one of the step being read.
Under 768 px there is one column: the picture is pinned edge to edge across the top of the window, --sticky-showcase-stack tall, 52% of the screen by default, and the texts are read below it and pass under it as they leave. The pictures always cover their frame there.
An ancestor with overflow: hidden, auto or scroll becomes the scroll container of the sticky picture, and a container that does not scroll has nowhere to pin. Use overflow-x: clip where a parent has to cut. Under a fixed header, give --sticky-showcase-top the height of that header.