Interactive Dot Grid
An interactive dot grid background — a full-bleed regular grid where dots near the cursor scale up and get pushed away, then ease smoothly back.
npx shadcn@latest add https://webberui.com/r/interactive-dot-grid.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<InteractiveDotGrid />
Installation
npx shadcn@latest add https://webberui.com/r/interactive-dot-grid.jsonOr, once registries are configured in components.json, install it as @webberui/interactive-dot-grid.
Usage
The component fills a sized parent, with foreground content absolutely positioned on top; give that overlay pointer-events-none so cursor events reach the dot grid:
import { InteractiveDotGrid } from "@/components/ui/interactive-dot-grid";
<div className="relative h-[400px] overflow-hidden rounded-2xl">
<InteractiveDotGrid />
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<h2 className="text-2xl font-semibold">Move the cursor to see the grid react</h2>
</div>
</div>Adjusting density, influence radius, and color:
<InteractiveDotGrid gap={36} dotRadius={2} influence={160} color="#38bdf8" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
gap | number | 28 | Spacing between dots (px), which also determines their density |
dotRadius | number | 1.5 | Radius of each dot at rest (px) |
influence | number | 120 | Cursor influence radius (px): only dots inside it scale up and get pushed away |
color | string | neutral-400 in light / neutral-600 in dark | Dot color; follows the theme when unspecified |
className | string | — | Appended to the outermost container's className |
How it works
- Drawn on a single canvas. The entire grid is drawn on one
<canvas>; aResizeObserverreads the container size and sizes the backing buffer bydevicePixelRatio(capped at 2×) to avoid blurriness on high-resolution displays - Repulsion grows with proximity. Every frame, each dot's distance to the cursor is computed; when it falls inside
influence,1 - dist / influencebecomes the strength, which pushes the dot along the opposite direction (by at most about 35% ofinfluence) and scales up its radius - Smooth return. Both the cursor coordinates and the overall influence strength interpolate toward their target, so once the cursor leaves the container the strength decays back to 0 and the grid eases naturally back to its baseline positions
- Idle power saving. Once the animation is at rest and the grid is back at baseline, the rAF loop keeps only the lightweight interpolation math and skips drawing, waking up again the moment the cursor re-enters
- Theme-aware color. When
coloris unspecified, the canvas takes its color fromcurrentColor(text-neutral-400 dark:text-neutral-600) and uses aMutationObserveron the<html>element's class to redraw immediately when the light/dark theme is switched - Only mouse pointers (
pointerType === "mouse") trigger the interaction; touch has no effect
Accessibility
- The dot grid is a purely decorative background, and the outermost element carries
aria-hidden, so it does not interfere with assistive technology announcing the foreground content - When the user has "reduce motion" enabled at the system level, only a static grid is drawn — the animation loop never starts and the cursor gets no response
- On unmount the component cleans up its rAF,
ResizeObserver, andMutationObserver, leaving no background timers behind
Grain Overlay
A canvas grain overlay that flickers frame by frame, in fine / coarse / vintage textures, adding film-like noise to gradients and images.
Shader Waves
A fluid wave background — a per-pixel multi-layer sine wave field (a software shader) computes a flowing gradient, with optional mouse tracking, grain, and several palettes.