Scroll Blinds Reveal — Free GSAP Slat Flip Snippet

Scroll Blinds Reveal · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

3D slat flips
rotationX around each slat's top edge.
Generated slats
One SLATS constant controls the count.
Cord-pull stagger
A 0.12 stagger sweeps the reveal downward.
True perspective
900px perspective adds real foreshortening.
Edge-on cleanup
Opacity and backface-visibility hide slivers.
Arriving content
Headline and copy rise on the same timeline.
Pinned + scrubbed
The reveal maps to 150% of scroll.
Reversible
Scrolling up closes the blinds.

About this UI Snippet

Scroll Blinds Reveal — Tilt Slats Open Like Venetian Blinds on Scroll

Screenshot of the Scroll Blinds Reveal snippet rendered live

The scroll blinds reveal covers a section with a stack of horizontal slats, then tilts each one open in 3D as you scroll — like pulling the cord on venetian blinds — so the content behind appears through widening gaps that sweep from top to bottom. Scrolling up tilts them shut again. This snippet builds it with GSAP ScrollTrigger (from a CDN), generated slat elements, and one staggered rotationX tween.

Slats are generated, not hand-written

A small loop builds SLATS (7) equal-height divs into the cover layer, each flex: 1 of a full-viewport column. Generating them means the slat count is a single constant: bump it to 12 for finer, faster-feeling blinds or drop to 4 for chunky panels — the CSS and the tween need no edits because both target the shared .sbr-slat class.

rotationX around the top edge is what sells "blinds"

Each slat has transform-origin: 50% 0%, so it rotates around its own top edge, exactly like a physical slat hanging from its rail. The tween takes every slat to rotationX: -92° — just past parallel-to-the-viewer, at which point the slat is edge-on and effectively invisible. The parent's perspective: 900px gives the rotation real foreshortening; without it, rotateX just squashes the slat vertically with no depth cue.

A stagger turns seven flips into one cord-pull

The single tween uses stagger: 0.12, offsetting each slat's start down the stack. On a scrubbed timeline that reads as a wave: the top slat is fully open while the bottom is just starting, and the diagonal front of "openness" travels down the screen like light spilling through blinds. One tween plus a stagger replaces seven hand-placed tweens and stays trivially retunable.

Opacity assists the edge-on illusion

Real slats vanish at edge-on angles, but an anti-aliased 1px line can linger on screen. Tweening opacity: 0 alongside the rotation guarantees each slat fully disappears at the end of its flip, and backface-visibility: hidden prevents the mirror image from flashing if a slat overshoots −90°.

The revealed content arrives, it doesn't just sit there

On the same timeline, the headline rises from y: 50 starting at position 0.25 and the paragraph follows at 0.45 — timed so the content is settling into place while the middle slats are opening. Sharing one scrubbed timeline keeps the cover's exit and the content's entrance locked together at every scroll position.

Pinned so the reveal owns its distance

The stage pins for +=150%, mapping the full cord-pull onto 1.5 viewport-heights of scrolling with scrub: 0.4 smoothing. Transform-and-opacity-only animation keeps the whole reveal compositor-friendly even with seven layers moving.

Slats stay geometrically simple, which is why the effect scales

Each slat is a plain flex child with no absolute positioning or manual sizing — flex: 1 on equal siblings guarantees even heights at any viewport size, and the generator loop means adding a slat is changing one number, not hand-placing a new element. That simplicity is what makes the 3D perspective cheap: seven (or seventy) identical transform origins mean the browser can batch the compositor work identically per layer, so raising SLATS costs GPU time linearly rather than triggering new layout paths.

Customizing it

Rotate slats to +92° to open downward, switch to vertical slats with flex-direction: row and rotationY, or map an image across the slats with a shared background-image and per-slat background-position. Related reveals: the sliding scroll curtain reveal, the parting scroll split panels, and the clip-path scroll image mask.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to reconstruct the hinge geometry by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why each slat rotates to -92 degrees instead of a clean -90, and why transform-origin is set to the slat's top edge rather than its center. The same assistant is useful for optimizing it — ask whether raising the SLATS constant from 7 to 40 for finer blinds would meaningfully change compositor cost given every slat shares an identical transform-origin and property set, or whether the stagger value should shrink as slat count grows to keep the total sweep duration constant. It is just as useful for extending the effect — ask it to print a background image across the slats so it reads whole when closed and shreds open as they tilt, reverse the sweep direction to bottom-up, or swap rotationX for rotationY and a row layout for vertical blinds instead of horizontal. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "venetian blinds" scroll reveal in plain HTML, CSS, and JavaScript using GSAP and its ScrollTrigger plugin (load both from a CDN, no build step) plus CSS 3D transforms.

Requirements:
- A cover layer generated entirely in JavaScript: loop a SLATS constant number of times, creating one flex-child div per slat inside a container using flex-direction: column, so all slats are always equal height regardless of the constant's value, with a section of real content sitting underneath the cover layer.
- Give the slat container CSS perspective (for example 900px) and give each slat transform-origin: 50% 0% (its own top edge) so a rotationX transform swings it around that hinge like a physical slat hanging from a rail, not around its own center.
- Register one GSAP timeline on a pinned, scrubbed ScrollTrigger, and inside it animate all slats with a single tween (not one tween per slat) that rotates every slat past -90 degrees (for example to -92) while fading opacity to 0, using a stagger so each slat's rotation begins slightly after the one above it — producing a top-to-bottom sweep from one tween plus a stagger value, not manually offset individual tweens.
- Set backface-visibility: hidden on the slats so no mirrored slat face flashes into view if a slat's rotation overshoots past the edge-on angle.
- On the same shared timeline, animate the revealed content's heading and paragraph in from an offset with reduced opacity, timed so they visibly settle into place while the middle slats are still mid-rotation, so the cover's exit and the content's entrance are locked to one timeline rather than driven by separate triggers.
- Confirm scrolling back up correctly reverses the sweep, tilting slats shut from the same stagger pattern, purely from the scrubbed timeline reversing — no separate close-direction logic.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
  2. 2
    Paste HTML, CSS, and JSSeven slats generate and cover the section.
  3. 3
    Scroll into the stageSlats start tilting open from the top down.
  4. 4
    Watch the sweepThe open-wave travels down like a cord pull.
  5. 5
    Scroll back upThe blinds tilt shut — fully scrubbed.
  6. 6
    Tune SLATSChange one constant for finer or chunkier blinds.

Real-world uses

Common Use Cases

Section transitions
A more textured hand-off than a scroll curtain reveal.
Hero reveals
Open onto a scroll zoom hero or product shot.
Photography portfolios
Slat open a full-bleed image, then a photo gallery.
Launch teasers
Tease the announcement through widening gaps; pair with a countdown timer.
Editorial chapters
Punctuate long reads inside a scroll pin story.
Alternative masks
Swap in a scroll image mask where a single-shape reveal fits better.

Got questions?

Frequently Asked Questions

Each slat has transform-origin: 50% 0%, so a rotationX tween swings it around its own top edge — the hinge a physical slat hangs from. The parent's perspective: 900px provides foreshortening so nearer edges grow and farther edges shrink; without perspective, rotateX just squashes the slat flat with no depth.

At exactly −90° a slat is edge-on, but anti-aliasing can leave a shimmering 1px line, and floating-point scrub positions may hover a hair before the end. Overshooting to −92° with a simultaneous opacity fade guarantees clean disappearance, while backface-visibility: hidden stops the slat's mirror side from flashing past −90°.

A single tween targets all slats with stagger: 0.12, offsetting each one's start time down the stack. Under a scrubbed timeline that offset becomes spatial: at any scroll position the top slats are further through their flip than the lower ones, so the front of openness travels downward like a cord being pulled.

Yes — put anything in .sbr-behind; it's just a layered section under the slat stack. You can also print an image on the blinds themselves: give every slat the same background-image with background-size: 100% 700% and per-slat background-position offsets so the picture reads whole when closed and shreds open as they tilt.

Minimally — every slat is an identical flex child with the same transform-origin and rotationX property, so the compositor batches them uniformly rather than computing distinct layout paths per slat. Cost scales roughly linearly with count on the GPU, not the CPU, since the only JavaScript work is building the initial DOM and running one staggered tween; the scroll-driven updates themselves are pure compositor transforms.

Render the slats from an array (v-for / map / *ngFor) instead of the DOM loop, then create the timeline in a mount effect — useEffect, onMounted, or ngAfterViewInit — inside gsap.context scoped to the stage ref, reverting it on cleanup so the pin is removed on unmount. Slat styles are simple enough to express as Tailwind utilities on the mapped element.