Backgrounds

Grid Dots

A pure-CSS grid, dot or cross pattern container with a radial fade — a zero-JS decorative backdrop for hero and section surfaces.

Preview in your theme

Loading preview…

import * as React from "react"
import { cn } from "@/lib/utils"

/**
 * Pattern inks derive from --foreground via color-mix, so light/dark themes
 * adapt for free. Sparser marks get more alpha to read at equal weight.
 */
const GRID_LINE = "color-mix(in oklab, var(--foreground) 10%, transparent)"
const CROSS_STROKE = "color-mix(in oklab, var(--foreground) 16%, transparent)"
const DOT_INK = "color-mix(in oklab, var(--foreground) 22%, transparent)"

/** Alpha-only mask — keyword colors here, nothing is a rendered color. */
const FADE_MASK = "radial-gradient(ellipse at center, black, transparent 75%)"

Installation

npx shadcn@latest add https://ui.zyeon.ai/r/grid-dots.json

Prompt

The prompt behind this component — paste it into your AI assistant to recreate or adapt it.

Build a React + TypeScript + Tailwind "GridDots" component — a decorative
grid / dot-matrix / crosshair pattern container. Pure CSS, no dependencies
beyond the cn() class merger.

Contract
- export function GridDots — props extend React.HTMLAttributes<HTMLDivElement>
  (root spreads the rest), plus:
  - variant?: "grid" | "dots" | "cross" (default "grid")
  - cell?: number — tile spacing in px (default 32)
  - fade?: boolean — radial mask dissolving the pattern toward the edges
    (default true)
  - children render in a content layer above the pattern; className merges
    into the root via cn().
- No hooks, no events, no browser APIs — do NOT add "use client". It stays a
  React Server Component and ships zero client JS.

Behavior
- Root is cn("relative isolate overflow-hidden", className): isolate scopes
  the internal z-index, overflow-hidden clips the pattern to consumer-rounded
  corners. The component sets no size or padding — the consumer's className
  owns dimensions.
- Two layers inside: an absolute inset-0 pattern layer (aria-hidden,
  pointer-events-none) and a relative z-10 wrapper around children.
- Each pattern is a background-image recipe tiled at `cell × cell` px via
  background-size:
  - grid: two 1px line gradients — linear-gradient(90deg, line 1px,
    transparent 1px) plus linear-gradient(0deg, same).
  - dots: radial-gradient(circle, ink 1.5px, transparent 1.5px).
  - cross: the conic-gradient quadrant trick — conic-gradient(at 1px Npx,
    transparent 75%, stroke 0) paints an exact 1px × N rectangle (hard stop
    at 270°, axis-aligned), a second layer paints N × 1px, and per-layer
    background-position centers both in the tile. Arm length
    N = max(4, round(cell / 4)) so the mark scales with density.
- fade=true sets mask-image and -webkit-mask-image to
  radial-gradient(ellipse at center, black, transparent 75%); fade=false lets
  the pattern run edge to edge.
- Static by design: nothing animates, so there is nothing to gate behind
  prefers-reduced-motion.

Rendering & styling
- Never hardcode pattern colors. Every ink derives from the theme:
  color-mix(in oklab, var(--foreground) N%, transparent) with N = 10 for grid
  lines, 16 for cross strokes, 22 for dots — sparser marks get more alpha so
  the three variants read at equal visual weight. Dark mode adapts for free
  because --foreground flips with the theme.
- The mask gradient uses only the black / transparent keywords — a mask reads
  the alpha channel, it is not a rendered color.
- The content wrapper is plain relative z-10 and imposes no layout, so
  consumer flex/grid classes on the root position children as usual.

Customization levers
- Contrast: the three color-mix percentages are the only ink knobs; stay in
  the 6–25% band or the ornament starts competing with content.
- Ink token: swap var(--foreground) for var(--primary) (brand-tinted paper)
  or var(--border) (even quieter); the recipe stays identical.
- Density: cell is the single rhythm knob (24–80px is the useful range);
  the cross arm length follows it automatically.
- Mask shape: replace the radial mask with
  linear-gradient(to bottom, black, transparent) for a top-anchored fade
  under a navbar, or move the ellipse center (e.g. "at 50% 0%") to spotlight
  a hero headline.
- Hero pairing: give the root the section's min-height plus centering flex
  classes via className, stack headline + CTA as children, and keep fade=true
  so the pattern stays clean against adjacent sections.
- Line weight: the 1px / 1.5px stops can grow to 2px for a bolder blueprint
  look — raise cell at the same time to keep the surface calm.

Concepts

  • Layered decoration — the pattern lives in an absolute, aria-hidden, pointer-events-none layer beneath a relative z-10 content wrapper, so assistive tech and pointer events only ever meet the content.
  • Zero-JS background — no hooks, events, or browser APIs means the component stays a React Server Component: the ornament is pure paint and adds nothing to the client bundle.
  • Token-derived contrast — every mark is color-mix(in oklab, var(--foreground) N%, transparent); one source token yields correct low contrast in both color schemes, and re-theming is a token swap, not a rewrite.
  • Tile-density lever — a single cell number feeds background-size for every variant, so pattern rhythm scales without touching the gradient recipes.
  • Radial mask fademask-image cuts alpha, not color: the pattern dissolves toward the edges so the centered content stays focal and section boundaries stay clean.
  • Conic quadrant trick — with axis-aligned hard stops, a conic-gradient paints an exact rectangle, letting two layers compose a crosshair per tile with no SVG asset.

On This Page