GridScan
grid-scanProjects an infinite perspective grid with a traveling luminous scan pulse.
Usage
Basic usage
Default round-trip scan; children layered on top of perspective grid via relative z-10.
<div className="relative h-64 overflow-hidden rounded-xl bg-neutral-950">
<GridScan>
<div className="flex h-full items-center justify-center text-white/80">
Content layer
</div>
</GridScan>
</div>Dashed grid · Scan forward
lineStyle switches among solid, dashed, and dotted lines; scanDirection controls the scan direction.
<GridScan lineStyle="dashed" scanDirection="forward" scanOpacity={0.6} />Dotted line grid + custom scan color
gridScale The smaller the grid, the denser it is. scanColor changes the scanning pulse luminous color.
<GridScan lineStyle="dotted" gridScale={0.07} scanColor="var(--color-chart-4)" />Sparse slow scan (wallpaper level)
Large gridScale + long scanDuration + high scanSoftness to get a leisurely wide light band.
<GridScan
gridScale={0.18}
scanDuration={4}
scanDelay={1}
scanSoftness={3}
scanColor="var(--color-chart-1)"
/>When to Use
Use GridScan behind a technology hero or dashboard when the grid should recede in perspective and a scan band should move from far to near, near to far, or in both directions. Choose GridPattern for a lightweight static grid, or RetroGrid for a CSS perspective horizon with a retro aesthetic.
Import
import { GridScan } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| linesColor | string | var(--color-border) | Grid-line color; accepts any CSS color and defaults to the theme-aware border token |
| scanColor | string | var(--color-primary) | Color of the luminous scan band; accepts any CSS color and defaults to the primary token |
| gridScale | number | 0.1 | Grid-cell scale; lower values create a denser grid, with 0.05–0.3 recommended |
| lineThickness | number | 1 | Grid-line thickness in screen pixels |
| lineStyle | "solid" | "dashed" | "dotted" | "solid" | Pattern applied to both grid axes |
| scanOpacity | number | 0.45 | Scan-band opacity from 0 to 1; 0 leaves only the grid |
| scanDirection | "forward" | "backward" | "pingpong" | "pingpong" | forward moves far to near, backward near to far, and pingpong alternates directions |
| scanDuration | number | 2 | Duration of one scan in seconds |
| scanDelay | number | 2 | Delay in seconds before each one-way scan; in pingpong mode it delays only the initial movement |
| scanSoftness | number | 2 | Scan-band softness; higher values make the band wider and softer |
| noiseIntensity | number | 0.01 | Fine screen-space grain; 0 produces a clean image |
| parallax | boolean | true | Tilt the camera slightly toward pointer movement that reaches the root; ignored by the reduced-motion fallback |
| className | string | — | Class name forwarded to the live or fallback root |
Slots
| Slot | Type | Description |
|---|---|---|
| children | ReactNode | Purely decorative content wrapped in a full-size relative z-10 layer; it remains inside the root's aria-hidden subtree |
| fallback | ReactNode | Purely decorative content rendered inside the static token-colored grid when reduced motion is enabled |
Usage Guidelines
- GridScan is an
absolute inset-0 z-0decorative layer. Put it in arelativecontainer with an explicit height andoverflow-hidden, then place necessary, accessible foreground content in a sibling atrelative z-10or above. - Both
childrenandfallbackremain inside GridScan'saria-hiddenroot. Use them only for decoration that does not need to enter the accessibility tree. - OGL/WebGL starts on the client. During SSR, the live root has no canvas. After hydration, a canvas is appended before OGL import and scene setup; if either step fails, that uninitialized or blank canvas remains and the component does not switch to the reduced-motion fallback. Reduced motion instead renders the static grid plus decorative
childrenandfallback. - The live root defaults to
pointer-events-none. Addpointer-events-autothroughclassNameif parallax is required, and make sure covering siblings do not take those pointer hits. - An opaque parent in a separate stacking context can cover a full-size background layer. If the canvas exists but is invisible, inspect stacking and background paint; see [[webgl-canvas-rendered-but-invisible-negative-zindex-covered]].
- CSS variables for
scanColorandlinesColorrequire full token names such asvar(--color-primary). Bare values such asvar(--primary)do not resolve; see [[hulian-token-color-var-needs-color-prefix]].
Related
DotPattern · GridPattern · StripedPattern · Spotlight · RetroGrid · Ripple
Playground
<div className="relative h-64 overflow-hidden rounded-xl"
style={{ background: "oklch(0.13 0.02 265)" }}>
<GridScan
gridScale={0.1}
lineThickness={1}
lineStyle="solid"
scanDirection="pingpong"
scanOpacity={0.45}
scanDuration={2}
/>
</div>