Paper Layers Scene
A scene of 3–5 torn-edge paper layers with soft cut-paper shadows that parallax on scroll or cursor, for storytelling heroes and editorial layouts.
Stack 3 to 5 torn-edge paper sheets from back to front into a single scene: each sheet has its own colour, an irregular torn edge generated deterministically from a seed, a soft paper shadow that follows the torn shape, and optional paper-fibre grain. As the page scrolls or the cursor moves, sheets closer to the front shift further, giving a layered cut-paper parallax. Each layer can hold your own SVG drawings or text, and the top-most children slot is a normally readable heading area — a good fit for storytelling heroes and editorial layouts.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add paper-layers-scene.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<PaperLayersScene/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/paper-layers-scene.en.jsonOr, once registries are configured in components.json, install it as @webberui/paper-layers-scene.
Usage
import { PaperLayersScene } from "@/components/ui/paper-layers-scene";
<PaperLayersScene
mode="both"
layers={[
{ id: "far", color: "#efe7da", height: "74%" },
{ id: "mid", color: "#e2d4bd", height: "56%" },
{ id: "near", color: "#c2ab8a", height: "36%", tornEdge: "both" },
{ id: "front", color: "#97805c", height: "18%", content: <MyGrassSvg /> },
]}
>
<h2 className="pt-10 text-center text-3xl font-semibold">A Slow Reading Weekend</h2>
</PaperLayersScene>Inside a nested scroll container
By default the component tracks window scroll. When it sits inside a fixed-height overflow-y-auto container, pass that container's ref to container. When mode includes scroll, the layer parallax follows how far the scene has scrolled inside the container.
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[400px] overflow-y-auto">
<PaperLayersScene container={scrollerRef} />
</div>When the window itself is the scroll container, container can be omitted and behavior is unchanged.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
layers | PaperLayer[] | four default warm papers | Paper layers stacked back to front; 3 to 5 is recommended |
mode | "scroll" | "pointer" | "both" | "both" | What drives the parallax; on touch devices pointer is disabled and only scroll remains |
intensity | number | 18 | Cursor parallax strength (px): the max shift of a depth-1 layer when the cursor reaches the edge |
range | number | 120 | Scroll parallax distance (px): total travel of a depth-1 layer across the whole scroll |
stiffness | number | 120 | Cursor spring stiffness; higher follows more tightly |
damping | number | 20 | Cursor spring damping; lower leaves more wobble when settling |
seed | number | 3 | Torn-edge random seed; the same seed always yields the same edge |
grain | boolean | true | Overlay a paper-fibre texture (multiply 6%) |
aspectRatio | string | "16/9" | Scene aspect ratio (CSS aspect-ratio) |
tilt | number | 0 | Max scene tilt following the cursor (degrees), capped at 3; 0 turns it off |
children | ReactNode | — | Text area stacked on top; read normally by screen readers |
container | RefObject<HTMLElement> | — | Ref of the nested scroll container; omit to use the window as the scroll container |
className | string | — | Passed to the outermost container |
PaperLayer
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique layer identifier |
color | string | warm paper by layer order | Paper colour (#f5efe6, #e9dfcf, #d9c9b0, #c2ab8a, #a88d68) |
tornEdge | "top" | "bottom" | "both" | "top" | Which edge is torn |
depth | number | 0.2 → 1 by layer order | Depth factor; larger moves more |
height | string | decreasing back to front | Share of the scene height as a percentage string, e.g. "45%" |
content | ReactNode | — | Content on the sheet (your own SVG or text), clipped by the torn edge too |
How it works
- The torn edge is a
clip-path: polygon(...): a deterministic RNG seeded byseedand the layer index places 24 to 48 points along the edge, mixing a low-frequency undulation with small high-frequency jitter. NoMath.random, so the shape matches between SSR and the client. - The shadow is two stacked
drop-shadows on the outer element while the clip-path sits on the inner one, so the shadow follows the torn shape. Override the whole shadow string with the--wb-paper-layers-shadowCSS variable. - Scroll parallax tracks the scene itself with
useScroll: layers rest at zero when the scene is centred in the viewport, and near layers rise faster than far ones as you scroll down. - Cursor parallax uses the scene centre as its origin and is spring-smoothed; when the cursor leaves, layers ease back to centre over
--wb-paper-layers-ease-duration(default600ms). - Every sheet bleeds past the scene on the left, right and bottom, so even large offsets never reveal a sheet's edge.
Accessibility
- All paper layers are decorative and the whole group is
aria-hidden; headings and text inchildrenare unaffected and read normally by screen readers - Touch devices (no hover, coarse pointer) automatically disable cursor parallax and keep only scroll parallax
- When the system "Reduce motion" setting is on, all sheets stay still while keeping the paper texture, torn edges and shadows
Scroll Fade List
A React scrolling list whose top and bottom fades grow and shrink with the remaining scroll distance, disappearing entirely at either end, with optional auto-scroll that pauses on hover, focus and touch.
Paginated Wave Grid
A paginated grid layout — on a page change the outgoing items exit in distance-based waves toward the turn direction while the new page's items flood in from the opposite side, with the page number and total rolling and morphing in sync.