---
name: cyber-grid-background
description: Use when you want "Cyberpunk, techy, exotic" - Uses perspective grids, neon lines, and futuristic geometry.
---

# Cyber Grid Background

> **Category:** Background  -  **Personality:** Cyberpunk, techy, exotic
>
> **Best use:** Gaming, cybersecurity, futuristic pages
>
> **Ratings:** Professional 2/5 - Casual 3/5 - Exotic 5/5 - Visual impact 5/5 - Difficulty 4/5 - Performance cost 3/5

## What it is

A synthwave / "outrun" scene: a neon grid **floor in 3D perspective** that scrolls endlessly toward a horizon, crowned by a banded retro sun and a glowing horizon line. The depth comes from one CSS `perspective` + `rotateX()` plane; the motion is a single looping `background-position` shift, so no canvas, WebGL or JavaScript is required. It is loud, nostalgic and unmistakably retro-futuristic — perfect as a hero backdrop for games, arcades, sci-fi products or cybersecurity pages. Unlike a *flat* grid-glow background, the grid genuinely recedes into the distance and the rows accelerate as they rush past the viewer.

## Dependencies / CDN

**None - pure CSS** for the effect itself. The demo loads two display fonts (optional) and uses ~12 lines of vanilla JS for an optional pointer parallax — the grid runs without it.

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=Chakra+Petch:wght@300;400;500;600&display=swap" rel="stylesheet">
```

## HTML

The background is a stack of absolutely-positioned layers inside one `overflow:hidden` stage; foreground content sits on top. Two floor copies are used — a blurred one for the neon bloom and a crisp one on top.

```html
<div class="cgb-stage">
  <div class="cgb-stars"   aria-hidden="true"></div>   <!-- starfield      -->
  <div class="cgb-sunglow" aria-hidden="true"></div>   <!-- soft sun bloom -->
  <div class="cgb-sun"     aria-hidden="true"></div>   <!-- banded sun     -->
  <div class="cgb-horizon" aria-hidden="true"></div>   <!-- glowing line   -->
  <div class="cgb-floorwrap" aria-hidden="true">       <!-- perspective box -->
    <div class="cgb-floor cgb-floor--glow"></div>      <!-- neon bloom copy -->
    <div class="cgb-floor"></div>                      <!-- crisp grid      -->
  </div>
  <div class="cgb-scan" aria-hidden="true"></div>      <!-- CRT scanlines  -->
  <div class="cgb-vig"  aria-hidden="true"></div>      <!-- vignette       -->

  <div class="cgb-content"><!-- nav / hero / HUD goes here --></div>
</div>
```

## CSS

```css
.cgb-stage{
  --cgb-ink:#070217; --cgb-pink:#ff2e88; --cgb-cyan:#2be7ff; --cgb-amber:#ffd23f;
  --cgb-cell:64px;          /* grid square size */
  --cgb-px:0px;             /* parallax offset, set by JS (optional) */
  position:relative; overflow:hidden; border-radius:28px; min-height:720px; isolation:isolate;
  /* sky: indigo -> magenta, hard cut to ink at the 60% horizon */
  background:
    radial-gradient(120% 78% at 50% 60%, rgba(255,46,136,.32), transparent 58%),
    linear-gradient(180deg,#10031f 0%,#220a4b 28%,#561585 48%,#9c1f7e 59%,#c23a7e 60%,#1f0834 60.3%,var(--cgb-ink) 100%);
}

/* ---- THE GRID FLOOR — this is the whole effect ---- */
.cgb-floorwrap{
  position:absolute; left:0; right:0; top:60%; bottom:0; z-index:3;
  perspective:300px; perspective-origin:calc(50% + var(--cgb-px) * 2) 0%;   /* vanishing point at the horizon */
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 8%,#000 78%,transparent 100%);
          mask-image:linear-gradient(180deg,transparent 0,#000 8%,#000 78%,transparent 100%);
}
.cgb-floor{
  position:absolute; left:-50%; top:0; bottom:0; width:200%;          /* overscan so tilted edges never gap */
  transform-origin:50% 100%; transform:rotateX(80deg);               /* lay the plane down toward the horizon */
  background-image:
    repeating-linear-gradient(90deg, rgba(43,231,255,.95) 0 2px, transparent 2px var(--cgb-cell)),  /* lane lines */
    repeating-linear-gradient(0deg,  rgba(43,231,255,.55) 0 2px, transparent 2px var(--cgb-cell));  /* rungs      */
  background-position:0 0, 0 0;
  animation:cgb-roll 1.15s linear infinite;
}
.cgb-floor--glow{ filter:blur(6px) brightness(1.7); opacity:.6; }      /* neon bloom under the crisp copy */

/* scroll ONLY the horizontal rungs by exactly one cell => seamless loop, lane lines stay put */
@keyframes cgb-roll{ to{ background-position:0 0, 0 var(--cgb-cell); } }

/* ---- horizon glow line ---- */
.cgb-horizon{
  position:absolute; left:0; right:0; top:60%; height:2px; z-index:4; transform:translateY(-1px);
  background:linear-gradient(90deg,transparent,var(--cgb-cyan) 16%,#fff 50%,var(--cgb-cyan) 84%,transparent);
  box-shadow:0 0 14px 2px var(--cgb-cyan),0 0 40px 7px rgba(255,46,136,.7);
}

/* ---- banded retro sun (mask cuts widening gaps into the lower half) ---- */
.cgb-sun{
  position:absolute; left:50%; top:60%; width:min(300px,58vw); aspect-ratio:1; z-index:2;
  transform:translate(calc(-50% + var(--cgb-px) * .55),-56%); border-radius:50%;
  background:linear-gradient(180deg,#fff4ad 0%,var(--cgb-amber) 20%,#ff8a3d 42%,var(--cgb-pink) 70%,#b51fc9 100%);
  -webkit-mask-image:var(--cgb-bands); mask-image:var(--cgb-bands);
  --cgb-bands:linear-gradient(180deg,
    #000 0 47%, transparent 47% 49.5%, #000 49.5% 55%, transparent 55% 58.5%,
    #000 58.5% 64%, transparent 64% 68.5%, #000 68.5% 75%, transparent 75% 80.5%,
    #000 80.5% 89%, transparent 89% 100%);
}
.cgb-sunglow{
  position:absolute; left:50%; top:60%; width:min(470px,82vw); aspect-ratio:1; z-index:1; border-radius:50%;
  transform:translate(calc(-50% + var(--cgb-px) * .55),-48%); filter:blur(10px);
  background:radial-gradient(circle at 50% 50%,rgba(255,98,168,.6),rgba(150,33,201,.3) 44%,transparent 70%);
  animation:cgb-pulse 6s ease-in-out infinite;
}
@keyframes cgb-pulse{ 0%,100%{opacity:.82;filter:blur(10px)} 50%{opacity:1;filter:blur(13px)} }

/* ---- CRT overlays ---- */
.cgb-scan{position:absolute;inset:0;z-index:8;pointer-events:none;mix-blend-mode:overlay;opacity:.45;
  background:repeating-linear-gradient(0deg,transparent 0 2px,rgba(0,0,0,.6) 2px 3px);}
.cgb-vig{position:absolute;inset:0;z-index:8;pointer-events:none;
  background:radial-gradient(125% 88% at 50% 36%,transparent 54%,rgba(7,2,23,.72) 100%);}

/* ---- respect reduced motion: a static perspective grid, no scroll ---- */
@media (prefers-reduced-motion: reduce){
  .cgb-floor,.cgb-sunglow{ animation:none !important; }
  .cgb-floor{ background-position:0 0,0 0 !important; }
}
```

(The `.cgb-content` nav / hero / HUD styling is ordinary demo chrome and is omitted here — only the layers above produce the effect.)

## JavaScript

Not required. Optional flourish — nudge the grid's vanishing point and the sun a few pixels toward the cursor by writing one custom property. Skipped entirely under reduced-motion.

```js
(function(){
  var stage=document.querySelector('.cgb-stage');
  if(!stage) return;
  if(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
  stage.addEventListener('pointermove',function(e){
    var r=stage.getBoundingClientRect();
    var dx=((e.clientX-r.left)/r.width-.5)*26;        // -13px .. +13px
    stage.style.setProperty('--cgb-px',dx.toFixed(1)+'px');
  });
  stage.addEventListener('pointerleave',function(){ stage.style.setProperty('--cgb-px','0px'); });
})();
```

## How it works

- **Perspective plane.** `.cgb-floorwrap` owns the camera (`perspective:300px`, `perspective-origin: 50% 0%` puts the vanishing point on the horizon). Its child `.cgb-floor` is laid flat with `transform-origin:50% 100%; transform:rotateX(80deg)` — the near (bottom) edge stays put while the far edge folds back to the horizon. Parallel lines now converge and rows foreshorten exactly like a real road.
- **The grid** is just two `repeating-linear-gradient`s (vertical lane lines + horizontal rungs) at `var(--cgb-cell)` spacing — no SVG, no mesh.
- **The infinite scroll** is a single `background-position` keyframe that moves the *rungs* down by one whole cell. Because the pattern repeats every cell, the end frame is pixel-identical to the start, so it loops with no seam. The lane lines are left at `0 0` so they stay anchored to the horizon. Under perspective, a constant texture shift reads as rows that *accelerate* toward you.
- **Neon bloom** is a second, blurred + brightened copy of the same floor (`.cgb-floor--glow`) sharing the same animation, sitting underneath the crisp one.
- **Retro sun** is a circle with a vertical gradient; a `mask-image` with progressively wider transparent slits in its lower half carves the iconic horizontal bands. A separate blurred radial `.cgb-sunglow` provides the halo and a slow breathing pulse.
- A top/bottom `mask` on the floor wrapper dissolves the grid into haze at the horizon and into darkness up close.

## Customization

- **Speed:** lower the `cgb-roll` duration (e.g. `0.6s`) for a faster drive; raise it for a calm idle.
- **Density / scale:** change `--cgb-cell` (smaller = finer grid). Keep the keyframe tied to `var(--cgb-cell)` so the loop stays seamless.
- **Depth:** smaller `perspective` (e.g. `220px`) or a higher `rotateX` (`84deg`) = flatter, more extreme floor; larger perspective = gentler.
- **Palette:** retheme via the `--cgb-*` vars — cyan→magenta lane lines, an amber/teal "Tron" look, etc. Recolour the sky gradient and sun stops to match.
- **Mood:** drop the `.cgb-scan` scanlines for a cleaner modern look, or raise its opacity for a heavier CRT feel.

## Accessibility & performance

- The whole background is decorative — every layer is `aria-hidden="true"` and `pointer-events:none` so it never traps focus or screen-reader output.
- **Reduced motion is honored twice:** the demo's own `@media (prefers-reduced-motion: reduce)` freezes the scroll and pulse (and the JS parallax never attaches), leaving a clean *static* perspective grid.
- Animating `background-position` repaints one element per frame; it is cheap but not GPU-composited. Keep it to a couple of floor layers and avoid stacking many blurred copies. The `filter:blur` glow copy is the most expensive part — drop it on low-end targets.
- Ensure foreground text never sits over the bright sun; keep copy over the darker sky or add a scrim (the demo narrows its text column and shadows the tagline).

## Gotchas

- **Loop must move exactly one cell.** If the keyframe shift (`var(--cgb-cell)`) and the gradient period drift apart, you get a visible jump on every cycle.
- **Scroll the texture, not the element.** Animate `background-position`, not a `translate` on the plane — translating the box moves its on-screen projection and breaks the "fixed floor" illusion (and the seam).
- **Overscan the plane.** A `rotateX`-tilted element's corners swing outside its box; `width:200%; left:-50%` (plus the stage's `overflow:hidden`) hides the edges. Without it you'll see triangular gaps at the sides.
- **Mask needs the `-webkit-` prefix** for the sun bands and the floor fade in Safari/iOS, or the cut-outs silently do nothing.
- Give the stage `isolation:isolate`; the blurred glow and `mix-blend-mode` scanlines must composite *inside* the rounded, clipped stage, not against the page.
- Animating `background-position` with a CSS variable inside the keyframe is fine in modern browsers, but the variable must be defined on (or inherited by) the animated element.
