Built for Motion
Log in

WebGL Velvet Curtain Page Reveal Transition

proOn LoadOn ScrollOn Click
Loading preview

Overview

A velvet curtain for React and Next.js that parts on load, on scroll or on command, gathering to the sides to reveal your real page content underneath. The folds are built in the vertex shader, with velvet shading and a thin gold fringe.

The cloth is a mesh, so the folds are real shape: they wander on the way down, pinch at the rail and swing wide enough to scallop the leading edge. The top of the curtain moves first and the hem follows, so the edge leans as it goes, and the pleats tighten as the cloth bunches up at the sides. It is lit like velvet, dark where the pile faces you and bright on the slopes of the folds.

Details

Category
Transitions
Interaction
On Load + On Scroll + On Click
Trigger
On Load
Languages
React, TypeScript, Tailwind

(01)Keep exploring

All 118 components

Built with the same techniques

(02)FAQ

WebGL Velvet Curtain Page Reveal Transition — FAQ

01How do I open and close it?

Set trigger to load, scroll or manual. For manual control, either pass a ref and call open(), close() or toggle(), or pass the open prop from your own state. onOpenChange tells you when it starts moving.

02Can someone get stuck behind it?

No. Clicking a closed curtain opens it, and so does tabbing to a link inside it. If WebGL fails or its context is lost, the curtain is removed and the content shows.

03Can I make it leave the frame completely?

Yes. Set gather to 0 and the cloth slides right out. The default of 0.16 leaves a gathered column at each side, which covers about 8% of the width on each side, so leave some side padding in your content.

04What does it need from my page?

React and Tailwind CSS v4. It has no npm dependencies: no Three.js, no GSAP, and no image files to load. Give it a height with className, such as h-dvh, because the curtain fills whatever it wraps.

05What happens with reduced motion?

The curtain doesn't appear at all and your content is visible straight away. open(), close() and toggle() do nothing, and isOpen stays true.