Pixel Scroll Transition
Canvas-based scroll transition that wipes between two colors with a configurable pixel grid, directional patterns, easing, and accent-color bands.
Installation
Pro components require registry authentication. Add your Unlumen UI Pro key as UNLUMEN_LICENSE_KEY in your .env.local file and follow the setup guide.
File Structure
pixel-scroll-transition.tsx
pixel-patterns.ts
Usage
import { PixelScrollTransition } from "@/components/unlumen-ui/pixel-scroll-transition";
export default function Example() {
return (
<main>
<div className="flex h-screen w-full items-center justify-center p-6">
<h1 className="p-1 text-7xl tracking-tighter">Hello, I'm Leo.</h1>
</div>
<PixelScrollTransition
colorA="--background"
colorB="--foreground"
direction="bottom-top"
pattern="random"
scrollInfluence={1.5}
height="900px"
mode="inline"
pixelSize={35}
accentColors={["#eb5e28", "#00A699", "#FFB400", "#FC642D"]}
/>
<div className="flex w-full items-center justify-center bg-foreground p-6 text-2xl text-background">
<h1>Welcome to the revealed section.</h1>
</div>
</main>
);
}Sticky section transition:
<PixelScrollTransition
mode="sticky"
scrollHeight="180vh"
colorA="#252422"
colorB="#fffcf2"
direction="center-out"
pattern="radial"
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
mode? | "sticky" | "inline" | "sticky" | Layout mode. `"sticky"` pins the canvas for a viewport-length transition. `"inline"` animates as the element scrolls through the viewport. |
colorA? | string | "#252422" | Initial canvas color. Accepts any CSS color or a custom property name like `--background`. |
colorB? | string | "#fffcf2" | Final canvas color. Accepts any CSS color or a custom property name like `--foreground`. |
direction? | "top-bottom" | "bottom-top" | "left-right" | "right-left" | "center-out" | "center-in" | "bottom-top" | Base direction used to order the pixel reveal. |
pattern? | "random" | "checker" | "diagonal" | "wave" | "spiral" | "radial" | "random" | Pattern mixed into the directional threshold map. |
patternIntensity? | number | 0.4 | Blend between pure directional reveal and the selected pattern. `0` is directional only, `1` is pattern only. |
easing? | "linear" | "ease-in" | "ease-out" | "ease-in-out" | "expo-out" | "ease-out" | Easing applied to scroll progress before drawing pixels. |
pixelSize? | number | 24 | Size of each pixel cell in pixels. |
gap? | number | 0 | Gap between pixel cells in pixels. |
endAt? | number | 0.85 | Scroll progress at which the transition should be complete. Lower values finish earlier. |
accentShare? | number | 0.15 | Approximate share of pixels that briefly use an accent color during the reveal. |
accentColors? | string[] | ["#eb5e28", "#00A699", "#FFB400", "#FC642D"] | Accent colors used for the temporary colored reveal band. Custom properties are supported. |
scrollContainerRef? | React.RefObject<HTMLElement | null> | - | Ref to the scrollable ancestor that drives the transition. Omit it when the document scroll drives the effect. |
scrollInfluence? | number | 1 | Shapes the scroll progression while preserving the initial and final states. Values above 1 slow the early reveal; values below 1 accelerate it. |
scrollHeight? | string | "200vh" | Extra scroll distance used in `"sticky"` mode. |
height? | string | "600px" | Element height used in `"inline"` mode. |
className? | string | - | Additional classes applied to the root element. |
children? | React.ReactNode | - | Content rendered below the transition canvas. |
Notes
- Use
mode="inline"inside constrained previews, drawers, and scrollable panels. - Pass
scrollContainerRefwhen a nested scrollable element, rather than the page, drives the transition. - Use
mode="sticky"for full-page section transitions between large page blocks. - The canvas is device-pixel-ratio aware and rebuilds its grid on resize.
- Theme changes are observed through
<html>class/style mutations, so CSS variable colors update without remounting.
Credits
Built by leo.
Keep in mind
Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.