Warp Grid
Show a line grid that sags toward the pointer like mass bending space.
import { WarpGrid } from '@youj/motion/backgrounds'
Installation & usage
npm i @youj/motionFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use it where the idea is weight, gravity or structure: infrastructure, finance, anything that should feel like a surface under load. It is the one grid that never goes fully still, because with nobody there the well rests in the middle and breathes slowly. Keep it to one per page.
The grid fills its nearest positioned ancestor and paints behind your content, so make the container relative and isolate.
import { WarpGrid } from '@youj/motion/backgrounds'
export function WeightedHero() {
return (
<section className="relative isolate min-h-[560px]">
<WarpGrid />
<p className="relative text-5xl font-semibold">Space bends around what matters</p>
</section>
)
}The well follows the pointer with the same smoothing as the other backgrounds. When the pointer leaves, it glides back to the middle of the field and shallows to 40% of its depth. A press deepens it for a moment, which is what makes it respond on touch screens.
Description
A hero section over the grid; move the pointer across it and the whole grid leans toward it. Leave, and the well eases back to the middle.
colors takes two CSS colors: the grid lines, then the color the lines take close to the well. The defaults are --color-fg-subtle and --color-accent. Token references follow the theme, and the grid rereads them when the theme changes.
A switch between a shallow, a default and a deep well.
depth scales how far points slide toward the well, and radius how far out the pull reaches. Points move with an inverse-square falloff, so lines bunch and curve close to the well and stay where they are far away: the edges of the field never move. A deep well pinches the lines almost together at the center.
Code
warp-grid-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propscolors, intensity, speed… (8)
Props
| Prop | Type | |
|---|---|---|
colors | [lines: string, well: string][lines, well] CSS colors. Default: | – |
intensity | numberLine opacity, 0–1.5. default 1 | 1 |
speed | numberClock multiplier for the idle breathing and the pulse a press leaves. default 1 | 1 |
spacing | numberGrid pitch in CSS px. default 26 | 26 |
depth | numberHow far the well pulls the grid, 0–2. default 1 | 1 |
radius | numberReach of the well in CSS px. default 240 | 240 |
interactive | booleanPointer reactivity; a press deepens the well for a moment. default true | true |
className | string | – |
PerformanceCanvas 2D; one path plus a few for the lit lines
Performance
It's drawn with Canvas 2D, not WebGL. Every line is sampled every 8 pixels and each sample is one inverse-square evaluation, so the whole grid is the base path plus five alpha-bucketed paths for the lit segments. The canvas renders at a device-pixel ratio capped at 2. On the low motion-budget tier the lines are sampled 60% more coarsely and the loop runs at 30 fps. The loop pauses when the grid is offscreen or the tab is hidden, and it stops and cleans up on unmount. If the browser can't create a 2D context, it falls back to a static CSS wash of the accent color.
MotionWell position, breathing, press pulse · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Well positionblends from the middle to the pointer by pointer strength | no token smoothed per frame | smoothed per frame | |
| Idle breathingthe well deepens and eases about 6% while nobody is there | no token speed ÷ time scale | speed ÷ time scale | |
| Press pulsethe well deepens by half, then decays | no token about 0.3 s half-life | about 0.3 s half-life |
Reduced motion
Draws one still grid sagging toward the middle: the same composition every time. There's no breathing and no pointer response, and it redraws only when the size, the props or the theme change.
Reduced motion
The grid sags toward the middle at its resting depth, so it's the same on every visit and in every screenshot. The lines and the lit accent keep their colors. Only the breathing and the pointer response are gone. Switch on Reduced motion in any preview above to compare.
AccessibilityARIA
Accessibility
- It's decorative: the element is
aria-hiddenand ignores pointer events. - Lines cross whatever sits above them. Keep a quiet pool of canvas behind the copy, as the hero preview does, or lower
intensity, and check your text's contrast there. - The idle breathing is slow and stops offscreen. Under reduced motion the grid doesn't move at all.