You can build a premium 3D slider in Webflow using nothing but the native Slider component, a few lines of CSS, and a single Finsweet attribute set — no heavy libraries, no third-party slider plugin. It starts as a flat, static row of images and ends as an immersive, reflective, depth-rich experience that you can point at a hardcoded set of slides or a CMS collection with the exact same interaction.
What makes this worth the time isn’t the finished look — it’s understanding why each piece works. The native slider hides a handful of defaults that trip people up, depth turns out to be four simple properties stacked together, and moving the whole thing onto the CMS is three attributes once you know the one gotcha. Get the mental model and you can reuse this on portfolios, case-study galleries, or any featured-content section.
How it works
The native slider, demystified. The slider is Webflow’s own component, but a few of its defaults work against you. Even when the designer shows the height as “auto”, Webflow secretly applies a fixed 300px height — so you set it to auto manually. The real trick for showing multiple slides at once lives on the mask, not the slides: the distance a slider travels per click is set by the mask width, not the slide width. Shrink the mask (to 40%) and switch its overflow from hidden to visible, and you see neighbouring slides while still advancing one at a time. Resizing the slides instead would look identical in the designer but jump several slides per click. To keep it clean you set children perspective: 700px on the slide (the foundation for the 3D rotation), make the content wrapper inline-block with centered text so it hugs the image, and cap images at max-height: 70vh. Webflow also assigns hidden z-indexes behind the scenes — mask 1, nav wrapper 2, arrows 3 and 4 — which is why a z-index: 1 overlay can sit above the slides yet still leave the arrows clickable.
Depth is four properties, not one. A non-active slide isn’t styled by any single 3D switch — it’s the sum of four things: a Y-axis rotation (made three-dimensional by that children perspective), a scale just under 1, reduced opacity, and lowered saturation. Together, against a near-black background, they push the neighbours back into a faded, greyish haze and let the active slide read as vivid and forward. A separate absolutely-positioned overlay with a horizontal linear gradient (opaque at the edges, transparent through the middle) fades the row into the background at the left and right for a natural light falloff — with pointer-events: none so it never blocks the hover later.
The reflection is pure CSS. The mirrored-on-glass look comes from -webkit-box-reflect — a WebKit-only property that renders a reflection below the element, offset by a small gap, masked by a gradient so it fades out realistically. The clever part is targeting: instead of a class, the CSS matches a data-reflect attribute, so the same effect drops onto any image regardless of what classes it carries. Two things make or break it. The embed holding the CSS should sit at the very top of the page (given a global-styles class) so it loads immediately, and the reflection — which behaves like an absolutely-positioned element and adds no height of its own — gets clipped by the main container’s overflow: hidden. The fix is to give the content wrapper real height (100vh) so there’s room for the reflection to show beneath a 70vh image.
The Slider Change interaction, then the CMS swap. Motion comes from Webflow’s Slider Change trigger, which only appears in the interactions panel once a slide is selected. It gives you two animations: slide in view (becoming active) and slide out of view (leaving active). You animate the content wrapper from the “non-active” values (rotate 15°, scale 85%, opacity 60%, saturation 50%) to the active values (rotate 0, scale 1, opacity 100%, saturation 100%) with an 800ms ease-in-out expo — the same easing set in the slider settings, so everything feels of a piece. The out animation is just the reverse. The catch that makes it look broken at first: set the trigger to Class, not Element, or only the one slide you built it on animates. Moving to the CMS with Finsweet List Slider reuses all of this — you inject collection items into a single empty slide via two fs-cmsslider-element attributes, and because the slides now share the same class, your interaction already applies. The one gotcha: CMS items load after the interaction binds, so you add fs-cmsslider-reset="true" to make Webflow reapply the interaction to the injected slides. (Side effect worth knowing: after a reset the first slide isn’t animated as “active” on load until you interact once.)
How to use it
-
Build the structure. Section → container (
main-container, 48px horizontal / 64px vertical padding, max-width 1440px) → content wrapper → Slider. On the slider, set height to auto and background to transparent. In its settings, choose ease-in-out expo at 800ms, disable infinite/repeat slides (it fights a multi-slide viewport), and turn off the rounded and number nav labels. -
Shrink the mask, match the slides. Give the mask a class, set its width to 40%,
margin: 0 autoto center the first slide, and (while building) overflow visible so you can see the neighbours. Give the slides a shared class, setchildren perspective: 700px, and align them vertically to middle. Make the slide content wrapperdisplay: inline-blockwith the slide’s text aligned center so it hugs the image. Set the imagemax-height: 70vhand add adata-reflectattribute (name only — the value is ignored). -
Add the falloff overlay. Set the main container to
position: relativeand add an absolutely-positioned, full-sizeslider-overlaydiv with a 90° linear gradient: opaque background at 0% and 100%, transparent at 10% and 90%. Give itz-index: 1andpointer-events: none. Putoverflow: hiddenback on the main container to kill horizontal scroll. -
Drop in the reflection CSS. Add an Embed containing a
<style>block with the-webkit-box-reflectrule targeting[data-reflect](grab the exact CSS from the cloneable). Give the embed aglobal-stylesclass and move it to the very top of the page. Then give the section content wrapperheight: 100vhso the reflection has room to render beneath the images. -
Build the Slider Change interaction. Select a slide, add a Slider Change interaction. In slide in view, animate the content wrapper’s rotate (Y), scale, opacity, and saturation from the non-active values (15°, 85%, 60%, 50%) to active (0°, 100%, 100%, 100%) at 800ms ease-in-out expo, using “set as initial state” for the start values. Duplicate and reverse it for slide out of view. Crucially, set the trigger to Class so every slide animates.
-
Add the hover interaction (optional). On the slide content wrapper, add a Mouse hover interaction that animates blur (0 → 3px) and saturation (100% → 85%) on hover in, reversed on hover out — same easing, so it matches the rest.
-
Make it CMS-driven with Finsweet. Rebuild the same structure with a single empty slide, plus a Collection List (hidden with a global class) bound to your images collection. Add Finsweet’s List Slider script to the page head, then set the attributes from the reference table above:
fs-cmsslider-element="slider"on the slider,fs-cmsslider-element="list"on the collection list, andfs-cmsslider-reset="true"on the slider so the interaction reapplies after the items load. See the Finsweet List Slider docs for the current script and syntax. -
Handle responsiveness. At tablet, widen the mask to ~60% and drop the container’s horizontal padding; at portrait, set the mask to ~75% and shrink the arrow icons (e.g. 32px → 24px).