Foil Surface
A foil finish that wraps any content: five gradient layers with blend modes shift their sheen with scroll progress and pointer position, without any tilt.
This is a WebberUI Pro component
The install command below needs a licence to run; without one it returns 401.
Turn any container into a flowing hot-foil or holographic-sticker surface: badges, membership cards, tickets, headline blocks. The component stacks five gradient layers over your content (colored foil, film blobs, a pearl base, a diagonal shine band and a pointer glare) and blends them in with blend modes. The sheen is driven by scroll progress and pointer position; CSS variables are written in a batched requestAnimationFrame, never through React state and with no transitions. It only handles the surface and never tilts — put it inside tilt-card when you want 3D tilt, or use holographic-card for a ready-made card + tilt + hologram.
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 "https://webberui.com/r/foil-surface.en.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<FoilSurface/>
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/foil-surface.en.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/foil-surface.
Usage
import { FoilSurface } from "@/components/ui/foil-surface";
<FoilSurface className="rounded-full" intensity={0.8}>
<div className="flex size-40 items-center justify-center rounded-full bg-neutral-100">
Limited Member 2026
</div>
</FoilSurface>
// Driven by whole-page scroll progress, ignoring the pointer
<FoilSurface scrollProgressMode="document" interactive={false} className="rounded-xl">
<MemberCard />
</FoilSurface>Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Any content to wrap; the five foil layers sit on top of it |
scrollProgressMode | "element" | "document" | "element" | element: progress of the element through the viewport; document: whole-page scroll progress |
intensity | number | 0.8 | Sheen strength (0–1), multiplied into every layer's opacity |
shineSpeed | number | 1 | How fast the sheen flows while scrolling, multiplied into the scroll coefficients (0.82 and 80°) |
interactive | boolean | true | Whether the glare follows the pointer; when false only scroll drives the sheen |
as | "div" | "span" | "div" | Root element tag; use span for inline content (rendered as inline-block) |
layerClassName | string | — | Class applied to every foil layer |
className | string | — | Passed to the root element; set the radius here, the five layers inherit it via rounded-[inherit] |
How it works
- Update loop: listens to window scroll (in the capture phase, so nested scroll containers are caught too), resize, and the element's pointermove / pointerleave, all passive. Events only record the latest values and queue a single rAF, so several events in one frame collapse into one write
- Scroll progress:
elementmode is(viewport height − rect.top) ÷ (viewport height + rect.height), clamped to 0–1;documentmode isscrollTop ÷ (scrollHeight − clientHeight).elementmode always measures against the browser viewport, so inside a short nested scroll container the change is smaller — raiseshineSpeedto compensate - Pointer: 0–1 coordinates relative to the element, clamped to 0.08–0.92; on leave it returns to the center
- CSS variables written:
--foil-shift,--foil-y-shift,--glare-x,--glare-y,--shine-angle,--shine-opacity, set directly on the root element's style so children or a customlayerClassNamecan use them - Five layers: foil (seven-color linear gradient at 300% size panned by the variables, boosted saturate / contrast), film (four colored blobs,
screen), pearl (pearly white,soft-light), shine (diagonal band whose angle turns with scroll,screen) and glare (round pointer highlight). The root isisolate, so blend modes only mix with this component's content - Paused off-screen: when IntersectionObserver reports the element is out of view, rAF writes stop; one write is made when it comes back
- Versus holographic-card: holographic-card is a card + 3D tilt; foil-surface is a surface finish for any container, scroll-driven and never tilted. Put foil-surface inside tilt-card to combine them
Accessibility
- All five foil layers are
aria-hiddenandpointer-events-none, so they never affect reading order, clicks or focus - The foil is purely visual and carries no information; its strength is capped by
intensity, but you still need to ensure your text has enough contrast - When the user has enabled reduce motion, no scroll or pointer listeners are attached and the sheen is pinned to a static middle state (scroll 0.5, pointer centered)
Postage Stamp Frame
A postage-stamp frame React component whose scalloped perforations are computed with a pure CSS clip-path; wraps an image or any content, with a caption row and an optional SVG postmark overlay that fades in on hover.
Pixel UI Kit
A pixel-art UI trio — a stepped-border card, a pixel button that sinks on press, and a block-by-block progress bar, all drawn with box-shadow and CSS.