Infinite Perspective Grid
A synthwave-tinted floor of grid lines receding to a glowing horizon, scrolling toward the viewer forever. Pure CSS 3D transforms and gradients — GPU-friendly, with a masked horizon fade and a centred caption.
- perspective + rotateX
- repeating-linear-gradient
- mask fade
- transform animation
Source
A single self-contained .astro component. It uses Prism's design tokens (--spectrum-*, --ink…) with sensible fallbacks, so it renders even outside this site.
---
export const meta = {
title: "Infinite Perspective Grid",
tags: ["background", "grid", "3d", "css-only"],
description:
"A synthwave-tinted floor of grid lines receding to a glowing horizon, scrolling toward the viewer forever. Pure CSS 3D transforms and gradients — GPU-friendly, with a masked horizon fade and a centred caption.",
tech: ["perspective + rotateX", "repeating-linear-gradient", "mask fade", "transform animation"],
height: 460,
};
---
<section class="pgrid">
<div class="pgrid__scene" aria-hidden="true">
<div class="pgrid__sun"></div>
<div class="pgrid__floor"><div class="pgrid__lines"></div></div>
<div class="pgrid__haze"></div>
</div>
<div class="pgrid__caption">
<p class="pgrid__kicker">Endless runway</p>
<h2 class="pgrid__title">Toward the horizon</h2>
<p class="pgrid__sub">A grid that never stops arriving.</p>
</div>
</section>
<style>
.pgrid {
position: relative; min-height: 460px; display: grid; place-items: center;
overflow: hidden; isolation: isolate; padding: 2.5rem 1.5rem;
color: var(--ink, #f4f2ff);
background:
linear-gradient(180deg,
oklch(18% 0.06 285) 0%,
oklch(14% 0.05 290) 46%,
oklch(10% 0.04 300) 100%);
}
.pgrid__scene { position: absolute; inset: 0; z-index: -1; overflow: hidden; }
/* glowing sun on the horizon line (~52% down) */
.pgrid__sun {
position: absolute; left: 50%; top: 52%; width: 26rem; height: 26rem;
transform: translate(-50%, -60%);
background:
radial-gradient(circle at 50% 50%,
color-mix(in oklab, var(--spectrum-6, #f472b6) 65%, #fff 0%) 0%,
color-mix(in oklab, var(--spectrum-1, #8b5cf6) 45%, transparent) 42%,
transparent 66%);
filter: blur(4px); opacity: 0.85;
}
/* the receding floor plane */
.pgrid__floor {
position: absolute; left: 50%; top: 52%; width: 240%; height: 150%;
transform: translateX(-50%) perspective(340px) rotateX(74deg);
transform-origin: 50% 0;
-webkit-mask-image: linear-gradient(to top, #000 8%, color-mix(in oklab, #000 55%, transparent) 40%, transparent 82%);
mask-image: linear-gradient(to top, #000 8%, color-mix(in oklab, #000 55%, transparent) 40%, transparent 82%);
}
.pgrid__lines {
position: absolute; inset: -50% 0 0 0; height: 200%;
background-image:
repeating-linear-gradient(90deg,
color-mix(in oklab, var(--spectrum-3, #45d3e8) 62%, transparent) 0 2px,
transparent 2px 88px),
repeating-linear-gradient(0deg,
color-mix(in oklab, var(--spectrum-1, #8b5cf6) 68%, transparent) 0 2px,
transparent 2px 88px);
background-position: 0 0;
animation: pgrid-scroll 1.6s linear infinite;
will-change: background-position;
}
/* moving the horizontal ruling downward reads as motion toward the viewer */
@keyframes pgrid-scroll { to { background-position: 0 88px; } }
/* soft atmospheric haze pooling at the horizon */
.pgrid__haze {
position: absolute; left: 0; right: 0; top: 40%; height: 26%;
background: linear-gradient(180deg, transparent, color-mix(in oklab, var(--spectrum-1, #8b5cf6) 22%, transparent) 60%, transparent);
filter: blur(10px); pointer-events: none;
}
/* --- caption ----------------------------------------------------------- */
.pgrid__caption {
position: relative; text-align: center; display: grid; gap: 0.5rem; justify-items: center;
max-width: 32rem; margin-top: -3rem;
text-shadow: 0 2px 18px oklch(10% 0.04 300 / 0.7);
}
.pgrid__kicker {
margin: 0; font-size: var(--step--1, 0.85rem); font-weight: 700;
letter-spacing: 0.28em; text-transform: uppercase;
color: color-mix(in oklab, var(--spectrum-3, #45d3e8) 80%, #fff);
}
.pgrid__title {
margin: 0; font-family: var(--font-display, system-ui, sans-serif);
font-size: clamp(2rem, 1.3rem + 3.4vw, 3.4rem); font-weight: 800;
letter-spacing: -0.02em; line-height: 1.05; color: var(--ink, #fff);
}
.pgrid__sub { margin: 0; color: color-mix(in oklab, var(--ink, #fff) 78%, transparent); font-size: var(--step-0, 1rem); }
@media (prefers-reduced-motion: reduce) {
.pgrid__lines { animation: none; }
}
</style>