Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Drapery Shader for React

Champagne satin drawn on the GPU: heavy cloth in deep diagonal folds that breathe slowly, with long highlights along every ridge. For React with Base UI, when a fashion, jewelry or luxury page wants a rich, quiet hero.

Framework

Drapery Shader drapes heavy champagne satin across its box in deep diagonal folds. The folds fan out down their length, as if the cloth were gathered just above the frame, wander gently, and breathe slowly, as if air moved underneath. Long, sharp highlights run along each ridge with the stretched sheen of woven silk, the slopes turned away from the light glow softly, and the valleys sink into rich shadow. A WGSL fragment shader on WebGPU builds the cloth from a few rounded ridges that meet in soft creases and lights it like fabric, for under half a millisecond of GPU time.

Use it as the hero of a fashion, jewelry, beauty or luxury page, or behind a product shot. For sheer, translucent layers instead of heavy satin, use Chiffon Shader; for soft silk color without the geometry, in plain CSS, the Silk background. It runs only while it's on screen, holds a still frame with reduced motion, and shows a CSS gradient of the same folds in browsers without WebGPU.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { DraperyShader } from "@/components/ui/drapery-shader";
<DraperyShader className="flex min-h-[32rem] items-center justify-center">
  <div className="rounded-xl bg-black/60 px-8 py-6">
    <h1 className="text-5xl font-semibold text-white">The evening edit</h1>
  </div>
</DraperyShader>

Drapery Shader is a container: your content sits on top of the cloth, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The satin runs from deep shadow to bright highlights, so no text color reads on it bare everywhere: put copy on a panel, like the bg-black/60 one above, which keeps white text above 5.5:1 everywhere.

Demos

Black satin

color sets the cloth where the light falls, highlightColor the highlights and the sheen, and shadowColor the valleys. Here the satin is near-black with cool, blue-white highlights, for a night or tech look.

Preview width

In a card

folds={3} makes fewer, broader folds that read at card size, and angle={-60} turns them steeper, to run down a tall box. It keeps full quality: it's cheap, and the highlights are thin lines that half resolution would soften.

Preview width

Performance

About 0.4 ms of GPU time a frame at 1280×720 on an M2 Max laptop: light for a shader, since the folds are a few formulas per pixel, with no tracing or noise. folds={8} costs a little more. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu (about 50 KB gzipped) loads only when the first one mounts.

Browser support

The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a diagonal CSS gradient of the same folds, in your colors, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.

Accessibility

The cloth is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame and stops. Nothing flashes: the folds breathe over several seconds and the highlights slide along them. On the bare satin, white text reads in the shadows but drops below 2.4:1 on the brightest twentieth of the frame, and black text below 1.6:1 in the valleys; a bg-black/60 panel keeps white text above 5.5:1 everywhere.

FAQ

How is the cloth drawn?

Each fold is a rounded ridge whose center line wanders and fans out down its length. Where two folds meet, a smooth maximum built on a soft absolute value pinches them into a crease. The slope of that surface gives a normal at every pixel, and the thread runs along the fold, so the shading can treat it as woven fabric.

Why do the highlights look like silk?

Satin reflects light differently along its threads than across them. The highlights use Ward's anisotropic model: sharp across the thread and long along it, so each ridge gets a long, thin line of light. A Charlie sheen lobe, made for cloth, adds the soft glow on the slopes turned away from you, and the valleys darken by how far below the ridges they sit.

What do folds, depth, angle and sheen do?

folds sets how many folds cross the frame, from 2 to 8. depth sets how deep and steep they are, from 0 (nearly flat) to 1. angle turns them, in degrees from horizontal, from -90 to 90. sheen sets the soft glow on the slopes, from 0 to 1, and intensity the strength of the highlights along the ridges, from 0 to 2.

Can I change the colors?

Yes: color is the cloth in the light, shadowColor the valleys and highlightColor the highlights and sheen. They take any CSS color, var(--primary) included, and the CSS fallback follows them too. Keep the shadow darker than the cloth, or the folds lose their depth.

Will it slow my page down?

It costs about 0.4 ms of GPU time a frame at 1280×720 on a laptop, and the CPU only sets a few numbers each frame. It stops when it's off screen. You rarely need a lower quality, and half resolution would soften the thin highlights.

What happens in browsers without WebGPU?

The box keeps its CSS gradient, diagonal bands of shadow, cloth and highlight in your colors, so the page still looks finished. Nothing errors: the root's data-state reads unsupported.

API Reference

DraperyShader

data-slot="drapery-shader"

PropType
angle?number

The direction the folds run, in degrees from horizontal, from -90 to 90. @default -30.

Default
-30
color?string

The satin's colour where the light falls on it. Any CSS colour. @default "#d9c3a0".

Default
"#d9c3a0"
depth?number

How deep the folds are, from 0 (nearly flat) to 1 (deep, steep folds). @default 0.6.

Default
0.6
fallback?string

What shows until the first frame and in browsers without WebGPU: a CSS background value (gradients, sizes, a colour) that looks like the shader. It can use the colour props' --shader-<field> variables.

folds?number

How many folds cross the frame, from 2 to 8. @default 5.

Default
5
highlightColor?string

The highlights along the ridges and the sheen at grazing angles. Any CSS colour. @default "#fff6e5".

Default
"#fff6e5"
intensity?number

How strong the highlights along the ridges are, from 0 to 2. @default 1.

Default
1
interactive?boolean

Whether u.pointer follows the pointer. @default false.

paused?boolean

Holds the current frame. @default false.

quality?number

Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.

shadowColor?string

The shadow in the valleys. Any CSS colour. @default "#1a1410".

Default
"#1a1410"
sheen?number

The soft silky glow on the slopes turned away from you, from 0 to 1. @default 0.7.

Default
0.7
speed?number

Animation speed: 1 is normal, 0 holds the frame. @default 1.

time?number

A fixed moment, in seconds: draws it and doesn't animate.

render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

All 101 shaders