A crisp square-grid background in the border color with optional dashed lines, filled cells, softly lighting random cells and an edge fade, drawn as one SVG pattern.
Structure, quietly
A grid that fades out before it gets in the way.
import { GridPattern } from "@/components/ballmac/grid-pattern"
export default function GridPatternDemo() {
return (
<div className="relative flex h-72 w-full max-w-2xl items-center justify-center overflow-hidden rounded-2xl border bg-background">
<GridPattern
cell={36}
squares={[
[4, 2],
[6, 4],
[9, 1],
[11, 5],
[2, 5],
]}
fade="radial"
/>
<div className="relative z-10 text-center">
<h3 className="text-3xl font-semibold tracking-tight">Structure, quietly</h3>
<p className="mt-1 text-sm text-muted-foreground">A grid that fades out before it gets in the way.</p>
</div>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/grid-patternInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/grid-pattern.tsx// Ballmac UI: Grid Pattern. https://ui.ballmac.com/components/grid-pattern "use client" import * as React from "react" import { motion, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type GridPatternProps = Omit<React.ComponentProps<"svg">, "children"> & { /** Side of one cell in pixels. */ cell?: number /** Horizontal shift of the pattern in pixels. */ x?: number /** Vertical shift of the pattern in pixels. */ y?: number /** Dash pattern for the lines, such as "4 3". "0" is solid. */ strokeDasharray?: string /** Cells to fill, as [column, row] pairs counted from the top left. */ squares?: [number, number][] /** How many extra random cells softly light up and fade. They are picked after mount. */ flicker?: number /** Fade the pattern toward the edges. */ fade?: "radial" | "top" | "bottom" | "none" } const MASK = { radial: "radial-gradient(ellipse 70% 70% at 50% 50%, black 25%, transparent 85%)", top: "linear-gradient(to bottom, black, transparent 90%)", bottom: "linear-gradient(to top, black, transparent 90%)", none: undefined, } /** A square grid background. Lines and filled cells use the border and foreground tokens. Decorative. */ function GridPattern({ cell = 40, x = -1, y = -1, strokeDasharray = "0", squares, flicker = 0, fade = "radial", className, style, ...props }: GridPatternProps) { const id = React.useId().replace(/:/g, "") const reduce = useReducedMotion() const ref = React.useRef<SVGSVGElement>(null) const [lit, setLit] = React.useState<[number, number][]>([]) // Random cells are chosen after mount, so the server and browser markup stay identical. React.useEffect(() => { const el = ref.current if (!el || !flicker || reduce) return setLit([]) const pick = () => { const cols = Math.max(1, Math.floor(el.clientWidth / cell)) const rows = Math.max(1, Math.floor(el.clientHeight / cell)) setLit(Array.from({ length: flicker }, () => [Math.floor(Math.random() * cols), Math.floor(Math.random() * rows)] as [number, number])) } pick() const timer = setInterval(pick, 3200) return () => clearInterval(timer) }, [flicker, cell, reduce]) return ( <svg ref={ref} data-slot="grid-pattern" aria-hidden="true" className={cn("pointer-events-none absolute inset-0 size-full text-foreground", className)} style={{ maskImage: MASK[fade], WebkitMaskImage: MASK[fade], ...style }} {...props} > <defs> <pattern id={id} width={cell} height={cell} patternUnits="userSpaceOnUse" x={x} y={y}> <path d={`M${cell} 0 H0 V${cell}`} fill="none" stroke="var(--border)" strokeWidth="1" strokeDasharray={strokeDasharray} /> </pattern> </defs> <rect width="100%" height="100%" fill={`url(#${id})`} /> {squares?.map(([col, row]) => ( <rect key={`s-${col}-${row}`} x={col * cell + x + 1} y={row * cell + y + 1} width={cell - 1} height={cell - 1} fill="currentColor" fillOpacity={0.07} /> ))} {lit.map(([col, row], i) => ( <motion.rect key={`${col}-${row}-${i}-${lit.length}`} x={col * cell + x + 1} y={row * cell + y + 1} width={cell - 1} height={cell - 1} fill="currentColor" initial={{ fillOpacity: 0 }} animate={{ fillOpacity: [0, 0.1, 0] }} transition={{ duration: 3, delay: i * 0.2, ease: "easeInOut" }} /> ))} </svg> ) } export { GridPattern, type GridPatternProps }Update the import paths to match your project setup.
Usage
import { GridPattern } from "@/components/ballmac/grid-pattern"The full example is in the Code tab above.
Examples
Dashed and flickering
import { GridPattern } from "@/components/ballmac/grid-pattern"
export default function GridPatternVariants() {
return (
<div className="grid w-full max-w-xl gap-3 sm:grid-cols-2">
<div className="relative h-40 overflow-hidden rounded-xl border bg-card">
<GridPattern cell={24} strokeDasharray="3 3" fade="top" />
<span className="absolute bottom-3 start-3 text-xs font-medium">Dashed, fading down</span>
</div>
<div className="relative h-40 overflow-hidden rounded-xl border bg-card">
<GridPattern cell={28} flicker={6} fade="radial" />
<span className="absolute bottom-3 start-3 text-xs font-medium">Cells that glow now and then</span>
</div>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
cellSide of one cell in pixels. | number | 40 |
xHorizontal shift of the pattern in pixels. | number | -1 |
yVertical shift of the pattern in pixels. | number | -1 |
strokeDasharrayDash pattern for the lines, such as "4 3". "0" is solid. | string | "0" |
squaresCells to fill, as [column, row] pairs counted from the top left. | [number, number][] | — |
flickerHow many extra random cells softly light up and fade. They are picked after mount. | number | 0 |
fadeFade the pattern toward the edges. | "radial" | "top" | "bottom" | "none" | "radial" |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Screen readers | Decorative: aria-hidden |
| Reduced motion | No random cells light up |
Use with AI
<GridPattern cell strokeDasharray squares={[[col,row]]} flicker={n} fade='radial|top|bottom|none' /> in a relative parent. Random cells are picked after mount so server and browser agree. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Grid Pattern (@ballmac/grid-pattern) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Section and hero backgrounds that need structure
- Behind cards and empty states
Not for
- Dots (dot-pattern)
- A pointer-reactive grid (interactive-grid)
Registry JSON: https://ui.ballmac.com/r/grid-pattern.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Dot Pattern
A lightweight SVG dot grid background with an optional radial fade and a few softly pulsing glow dots. One pattern element, token colors, hydration-safe, still under reduced motion.
Interactive Grid
A canvas grid whose cells light up under the pointer and fade out behind it, leaving a glowing trail, drawn only while something is moving and listening on its parent so content above never blocks it.
Animated Grid
A decorative hairline grid background where a few random cells softly light up and fade, like instrument lights. SVG, hydration-safe, static under reduced motion.
Aurora Background
Soft, slowly drifting aurora light made of blurred token-colored glows with fine vertical curtains and an optional radial fade. Pure CSS layers, paused off-screen, still under reduced motion.