EmpireUI
Get Pro
← Blog8 min read#grid pattern#css#background

CSS Grid Pattern Background: Dot Matrix, Lines and Cross Grids

Build dot matrix, line grid, and cross grid CSS backgrounds from scratch — pure CSS, no images, full control over size, color, and opacity.

abstract CSS grid dot matrix pattern on dark background

Why Grid Backgrounds Are Having a Moment Again

Grid pattern backgrounds are everywhere right now — Vercel, Linear, Supabase, Raycast. They project a kind of quiet confidence: structured, technical, unmistakably developer-built. And the funny thing is, you don't need a single image file or an SVG sprite to pull them off. Pure CSS does the job.

The technique is older than you'd think. Developers were doing this in 2014 with repeated PNG tiles. In 2026 the approach is cleaner: CSS background-image with radial-gradient or linear-gradient, repeated at a fixed background-size. No HTTP request, no blurry scaling at odd display densities, and you can change color or spacing with a single CSS variable.

Honestly, the real power here isn't the static pattern itself — it's what you layer on top. A dark grid behind a glassmorphism components card looks immediately polished. The same grid under a neobrutalism layout reads as a sketchpad. Context transforms the exact same 24px tile into something completely different.

This guide covers the three most-requested variants: the dot matrix, the line grid (both single-axis and full crosshatch), and the cross/plus grid. You'll get copy-paste CSS for each, React component wrappers, and tips for animating them without tanking scroll performance.

The Dot Matrix Grid

The dot matrix is a single radial-gradient that paints a tiny circle and then tiles it. The trick is making the gradient transparent everywhere except the dot itself — which means the first color stop controls the dot size and the second stop snaps to transparent at the same radius, leaving no bleed.

.dot-grid {
  background-color: #0a0a0a;
  background-image: radial-gradient(
    circle,
    rgba(255, 255, 255, 0.25) 1px,
    transparent 1px
  );
  background-size: 24px 24px;
}

That 1px controls dot diameter. 24px 24px is the cell size — the distance between dot centers. Worth noting: on HiDPI screens a 1 px dot can look hairline-thin. Bump it to 1.5px for Retina displays or use a CSS media query targeting min-resolution: 2dppx. The background-size stays at 24 px either way.

You can make dots feel more alive by layering two dot grids offset by half a cell. The visual result is something between a halftone and a blueprint — dense enough to read as texture, sparse enough not to compete with content.

.dot-grid-layered {
  background-color: #0d1117;
  background-image:
    radial-gradient(circle, rgba(99,102,241,0.3) 1px, transparent 1px),
    radial-gradient(circle, rgba(99,102,241,0.1) 1px, transparent 1px);
  background-size: 24px 24px, 12px 12px;
}

Line Grids: Single-Axis and Full Crosshatch

Line grids use linear-gradient instead of radial-gradient. A single-axis horizontal grid is just one gradient repeated vertically. Single-axis vertical is the same idea rotated 90 degrees. Stack both and you get a crosshatch — the classic engineering-paper look.

/* Horizontal lines only */
.h-line-grid {
  background-color: #ffffff;
  background-image: linear-gradient(
    rgba(0, 0, 0, 0.08) 1px,
    transparent 1px
  );
  background-size: 100% 32px;
}

/* Full crosshatch */
.crosshatch-grid {
  background-color: #ffffff;
  background-image:
    linear-gradient(rgba(0,0,0,0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,0.06) 1px, transparent 1px);
  background-size: 32px 32px;
}

In practice, 32 px is a sweet spot for line grids on body-level backgrounds. It's large enough to let content breathe but tight enough to feel structured. Go above 64 px and the grid starts to look like a mistake rather than a choice. Go below 16 px and it reads as noise on small screens.

One more thing — the line width. That 1px in the gradient is the rendered line weight. On a standard display that's genuinely one CSS pixel. But if you want a hairline effect on Retina, use 0.5px. Some browsers round it to 1 px anyway, but Chrome and Safari on macOS render sub-pixel widths correctly, giving you a noticeably lighter touch.

The crosshatch pairs especially well with neobrutalism — that style's bold borders and flat fills read clearly over a fine grid. It also works as a subtle depth cue under lighter UI patterns like the aurora style where the grid shows through the animated glow.

The Cross Grid (Plus Pattern)

The cross grid is the one that trips people up. You want a plus/cross shape repeating across the canvas — not a full grid line, just a small cross centered in each cell. That's actually two overlapping linear gradients positioned to cover only the center of each tile.

.cross-grid {
  background-color: #0f0f0f;
  --cross-size: 40px;
  --cross-weight: 1px;
  --cross-color: rgba(255, 255, 255, 0.15);
  --arm-length: 8px; /* length of each arm from center */

  background-image:
    /* vertical arm */
    linear-gradient(
      var(--cross-color) var(--cross-weight),
      transparent var(--cross-weight)
    ),
    /* horizontal arm */
    linear-gradient(
      90deg,
      var(--cross-color) var(--cross-weight),
      transparent var(--cross-weight)
    );

  background-size: var(--cross-size) var(--cross-size);
  background-position:
    calc(var(--cross-size) / 2) calc(var(--cross-size) / 2 - var(--arm-length)),
    calc(var(--cross-size) / 2 - var(--arm-length)) calc(var(--cross-size) / 2);
  background-repeat: repeat;
}

Look, this one's trickier than it looks. The background-position offset is what clips the gradient line to just the arm length. Without it, you'd end up with a full crosshatch. The math: position each gradient so it starts at (cell/2 - arm) and the gradient itself only has color for 1px before going transparent — so the filled portion spans from (cell/2 - arm) to (cell/2 - arm + 1px). That's your arm. The whole cross is 16 px wide (2 × 8 px arms) inside a 40 px cell.

Quick aside: if you want the cross to feel more like a target reticle (longer arms), just increase --arm-length. The background-size cell stays the same and the arms will eventually merge with adjacent crosses — at that point you're back to a crosshatch, which is a useful continuum to understand.

The cross pattern is popular in cyberpunk and sci-fi UI aesthetics. It reads as a targeting system, a calibration grid, or a satellite map. Combined with a neon color scheme and a vignette fade at the edges, it's one of those touches that makes a landing page feel like a product rather than a template. You can prototype color combos fast using the gradient generator to nail the background tone before dialing in the grid opacity.

Wrapping Patterns as React Components

Inline styles in JSX work fine for one-offs but get messy at scale. A thin wrapper component that accepts the pattern type and color as props keeps things tidy across a design system.

// GridBackground.tsx
type GridVariant = 'dots' | 'lines' | 'crosshatch' | 'cross';

interface GridBackgroundProps {
  variant?: GridVariant;
  color?: string;
  size?: number;
  children?: React.ReactNode;
  className?: string;
}

const patterns: Record<GridVariant, (color: string, size: number) => React.CSSProperties> = {
  dots: (color, size) => ({
    backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,
    backgroundSize: `${size}px ${size}px`,
  }),
  lines: (color, size) => ({
    backgroundImage: `linear-gradient(${color} 1px, transparent 1px)`,
    backgroundSize: `100% ${size}px`,
  }),
  crosshatch: (color, size) => ({
    backgroundImage: [
      `linear-gradient(${color} 1px, transparent 1px)`,
      `linear-gradient(90deg, ${color} 1px, transparent 1px)`,
    ].join(', '),
    backgroundSize: `${size}px ${size}px`,
  }),
  cross: (color, size) => {
    const arm = Math.round(size / 5);
    return {
      backgroundImage: [
        `linear-gradient(${color} 1px, transparent 1px)`,
        `linear-gradient(90deg, ${color} 1px, transparent 1px)`,
      ].join(', '),
      backgroundSize: `${size}px ${size}px`,
      backgroundPosition: [
        `${size / 2}px ${size / 2 - arm}px`,
        `${size / 2 - arm}px ${size / 2}px`,
      ].join(', '),
    };
  },
};

export function GridBackground({
  variant = 'crosshatch',
  color = 'rgba(255,255,255,0.1)',
  size = 32,
  children,
  className = '',
}: GridBackgroundProps) {
  return (
    <div
      className={className}
      style={patterns[variant](color, size)}
    >
      {children}
    </div>
  );
}

That's around 50 lines and gives you all four variants with typed props. Drop it into any component tree: <GridBackground variant="dots" color="rgba(99,102,241,0.2)" size={24}>. No third-party dependency needed. The color prop accepts any valid CSS color string — rgba, hsl, hex with opacity via the newer #rrggbbaa syntax.

If you're working in Tailwind and want to keep this utility-class-friendly, the cleaner approach is to register these as custom Tailwind plugins so you get classes like bg-grid-dots and bg-grid-cross. That keeps the JSX clean but adds a bit of config overhead — worthwhile if you're using the patterns across more than 10 components.

Animating Grid Patterns Without Hurting Performance

Animating a grid background sounds expensive but it's actually cheap if you do it right. The key: never animate background-size or background-position directly — those trigger layout and paint on every frame. Instead, animate opacity or use a transform on a pseudo-element that holds the pattern.

/* Pseudo-element approach — GPU composited, no layout */
.animated-grid {
  position: relative;
  isolation: isolate;
}

.animated-grid::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(rgba(99,102,241,0.15) 1px, transparent 1px),
    linear-gradient(90deg, rgba(99,102,241,0.15) 1px, transparent 1px);
  background-size: 40px 40px;
  animation: grid-drift 20s linear infinite;
  will-change: transform;
}

@keyframes grid-drift {
  from { transform: translate(0, 0); }
  to   { transform: translate(40px, 40px); }
}

That translate(40px, 40px) moves the pattern exactly one cell width/height — which is where the loop closes invisibly. The grid scrolls diagonally and resets with zero visual glitch. will-change: transform hints to the browser to promote the pseudo-element to its own GPU layer ahead of time. This runs at 60 fps on 2019 mid-range hardware.

Worth noting: if you've got prefers-reduced-motion users to support (you should), wrap the animation in @media (prefers-reduced-motion: no-preference). The static grid already looks great — the animation is enhancement, not content.

For interactive hover effects — say, the grid brightens when the cursor approaches — combine this with a CSS custom property updated by JavaScript. Read the pointer position, calculate distance to center, set --grid-opacity on the element. The CSS does the rendering. That pattern keeps your JS lean and your CSS declarative. Take a look at how Empire UI handles interactive backgrounds in the box shadow generator for inspiration on event-driven CSS custom property updates.

Combining Grid Patterns With Other UI Styles

Grid patterns aren't a standalone aesthetic — they're a background layer that amplifies whatever sits on top. The combination you choose signals the entire design language of your UI before a user reads a single word.

Dark dot grid + glassmorphism cards = the 2024-2026 SaaS dashboard look. Everyone's doing it and it works because the dots recede behind the frosted glass perfectly. If you want to stand out, try the cross grid instead of dots — it reads as more intentional, less generic. Pair with the glassmorphism generator to dial in the card backdrop values against your specific grid color.

Light crosshatch + thick black borders = neobrutalism. The grid gives it that handmade notebook quality. Use a warm off-white (#fafaf7) as the background color so the grid lines in rgba(0,0,0,0.08) read as pencil marks rather than digital artifacts.

Dark cross grid + neon accent colors = cyberpunk or Y2K territory. Check out y2k and vaporwave for the palette references — those styles are built around high-contrast neon on dark surfaces, and a cross or dot grid at 20% opacity underneath adds exactly the right amount of retrofuturism without going full Windows 95.

One more thing — don't forget about the mobile experience. Grid patterns that look elegant at 1440 px wide sometimes turn into noise at 375 px because the tile count per viewport explodes. Consider responsive background-size with a media query: bump from 24 px to 40 px on small screens so the grid feels like texture rather than a test pattern.

FAQ

Can I use CSS grid pattern backgrounds without any JavaScript?

Yes, completely. All four variants — dot matrix, line grid, crosshatch, and cross grid — are pure CSS using background-image with radial-gradient or linear-gradient. No JS, no SVG, no external images needed.

What's the performance impact of animated grid backgrounds?

Minimal if you animate transform or opacity on a pseudo-element with will-change: transform. Never animate background-size or background-position directly — those trigger paint on every frame and will tank performance on mid-range devices.

How do I make a grid pattern responsive so it doesn't look like noise on mobile?

Increase the background-size cell on small screens — 24px for desktop, 40px or 48px for mobile. More space between dots or lines stops the pattern from feeling cluttered at narrow viewport widths.

Can I combine a CSS grid pattern with glassmorphism or other overlay effects?

Absolutely — it's one of the best combinations. Put the grid on the page background, then layer glassmorphism cards on top. The backdrop-filter: blur() picks up the grid pattern and frosts it, which looks great. The glassmorphism generator lets you tune the blur and opacity values visually.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Conic Gradient CSS: Pie Charts, Color Wheels and Angled FillsCSS Mesh Gradient Background: Fluid Color Blobs Without SVGCSS Gradient Animation: background-size, background-position TricksCSS Box Shadow: The Complete Guide With Live Examples