Volumetric Light
A pointer-following light drawn with WebGL behind your content. Each element you mark blocks it, so soft shadows stream out behind it and light pours through the gaps, while text gets a matching text-shadow. Made for light and dark pages; without WebGL it falls back to CSS shadows.
Included with membership
Members install Volumetric Light with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.
When to use it
A hero or feature section built from separate pieces, such as cards, tiles and a headline, where you want the page itself to feel lit. The effect lives in the gaps: light pours between the pieces you mark and their shadows stream away from the pointer, so it needs a few solid, separate shapes to work with. Over one block of text or a full-bleed image there is little for it to cast.
- Mark separate, solid pieces such as cards and tiles, and leave gaps between them; the gaps are where the light comes through.
- Set ground to match the page behind: light for pale pages, dark for dark ones. The container paints no background, so the page colour stays yours.
- Use restPosition to choose where the light waits before the visitor moves, such as a corner of the page or the gap between two cards.
- Mark headlines too. Text elements (h1 to h6, p, span and a) don't block the light; they get a text-shadow that leans away from it.
Avoid:
- Don't mark more than 24 blocking elements. Only the first 24 in page order block the light, and the rest cast nothing, with no warning.
- Don't put the class on a wrapper. A wrapper blocks as one big box, so the gaps between its children go dark.
- Don't expect see-through cards to let light through. Every marked element blocks as a solid rounded box, whatever its opacity.
- Don't nest one inside another. Both lights claim the same marked elements and fight over their shadows.
Props
A light that follows the pointer, drawn with WebGL behind your content. Every element you mark blocks it, so soft shadows stream out behind each one and light pours through the gaps between them, while marked text gets a matching text-shadow. It has one look for light pages and one for dark pages, and falls back to CSS shadows where WebGL2 isn't available.
<VolumetricLight />
Wraps a region and lights it. Everything inside that matches `selector` takes part: text gets a text-shadow, and every other match blocks the light.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | The lit region. |
selector | string | ".shadow-cast" | Which descendants take part. Text elements get a text-shadow; any other match blocks the light and catches a rim of it on the edge that faces it. |
ground | "light" | "dark" | "light" | What the page behind is. Light warms what the light reaches and lays a cool shade where it is blocked; dark adds glowing light. |
beams | boolean | true | Draws the light with WebGL behind the content, so shadows stream out behind each marked element and light pours through the gaps. Off, the plain glow and CSS shadows stand in. |
beamColor | string | "#FFB25E" on light, "#FFE3BC" on dark | Colour of the light, as hex or rgb(). Alpha is ignored. |
shadeColor | string | "#3B4A6B" | Colour of the shade on a light ground, as hex or rgb(). |
beamStrength | number | 1 | Brightness of the light. |
beamReach | number | 1200 | How far the light carries, in pixels. |
rim | boolean | true | The edge of each marked element that faces the light catches it. |
restPosition | [number, number] | string | [0.5, 0.35] | Where the light waits before the pointer arrives: fractions of the container's width and height, or a selector for an element inside it. Resting on an element, the light drifts only within that element's box. |
idle | boolean | true | Until the visitor moves, the light drifts a little around its resting place. Always off with reduced motion. |
damping | number | 0.08 | How far the light trails the pointer, from 0 to 0.95. Higher trails further. |
intensity | number | 0.08 | How far a text-shadow reaches for each pixel of distance from the light. |
maxShadowLength | number | 100 | Longest a text-shadow gets, in pixels. |
shadowColor | string | "rgba(0, 0, 0, 0.45)" | Colour of the text-shadows and of the CSS shadows. Its alpha sets how dark they get. |
blurFactor | number | 1 | Multiplier on shadow blur. Lowered automatically on constrained devices. |
showLight | boolean | true | Draws the plain glow wherever the WebGL light can't be drawn. |
lightSize | number | 240 | Diameter of the plain glow, in pixels. Also sets how soft the edges of the shadows are. |
lightColor | string | "rgba(255, 240, 200, 0.55)" | Colour at the centre of the plain glow. |
lightBlur | number | 40 | Blur on the plain glow, in pixels. |
disableAdaptiveQuality | boolean | false | Keeps full shadow quality on constrained devices. By default the most constrained tier drops to a single shadow layer at 40% blur; set this and every device gets the three-layer shadow. |
className | string | "" | Applied to the container. Set the page colour here. |
style | React.CSSProperties | — | Inline styles merged onto the container. |