Vaporwave Theme is built almost entirely on CSS — no canvas, no WebGL, no heavy JS libraries. This post breaks down the four core techniques that create the aesthetic.
1. Gradient Text with -webkit-background-clip
The headline gradient fill uses a WebKit-era property that’s now in the CSS spec:
h1 {
background: linear-gradient(90deg, #ff71ce, #01cdfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
The trick: set color to transparent so the background bleeds through the text shape. The background-clip: text (without prefix) is the spec version; -webkit-background-clip is the one that actually works in Chrome and Safari. Firefox needs both.
Important: text-fill-color (not color) must be transparent — color: transparent alone doesn’t work in Safari.
2. Synthwave Perspective Grid
The animated grid in the hero is pure CSS:
.vp-hero::before {
content: '';
position: absolute;
bottom: 0; left: -50%; right: -50%;
height: 60%;
background-image:
linear-gradient(rgba(1,205,254,0.2) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,113,206,0.2) 1px, transparent 1px);
background-size: 80px 80px;
transform: perspective(300px) rotateX(60deg);
transform-origin: bottom center;
animation: grid-scroll 3s linear infinite;
mask-image: linear-gradient(to top, rgba(0,0,0,0.5) 0%, transparent 100%);
}
@keyframes grid-scroll {
from { background-position-y: 0; }
to { background-position-y: 80px; }
}
The transform: perspective(300px) rotateX(60deg) is what tilts the flat grid into the 3D floor illusion. transform-origin: bottom center anchors the rotation at the floor. The mask-image fades the grid to nothing at the top, so it blends into the hero background.
The animation (background-position-y) scrolls the grid towards the viewer — 80px matches background-size, so it loops seamlessly.
3. Glitch Effect with clip-path
The glitch on post title hover uses two pseudo-elements that reveal different clipped slices at staggered offsets:
@keyframes glitch-1 {
0% { clip-path: polygon(0 15%, 100% 15%, 100% 30%, 0 30%);
transform: translate(-3px, 0); }
25% { clip-path: polygon(0 60%, 100% 60%, 100% 75%, 0 75%);
transform: translate(3px, 0); }
100% { clip-path: polygon(0 0, 0 0, 0 0, 0 0);
transform: translate(0, 0); }
}
.glitch::before {
content: attr(data-text); /* text copied from JS */
position: absolute;
top: 0; left: 0;
color: #ff71ce;
animation: glitch-1 0.3s steps(1) forwards;
}
steps(1) is the key — it makes the animation jump instantly between keyframes instead of interpolating. That’s what produces the harsh digital glitch feel rather than a smooth transition.
JavaScript copies the text content into data-text so the pseudo-element can reference it with attr(data-text).
4. CRT Scanlines
The subtle scanlines overlay is a repeating-linear-gradient on body::after:
body::after {
content: '';
position: fixed;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent, transparent 2px,
rgba(0,0,0,0.025) 2px, rgba(0,0,0,0.025) 4px
);
pointer-events: none;
z-index: 9999;
}
repeating-linear-gradient(0deg, ...) draws horizontal bands — 2px transparent, 2px dark. This sits above everything (z-index: 9999) with pointer-events: none so it doesn’t block clicks. The opacity is deliberately low (0.025) so it reads as a texture rather than noise.
Putting It Together
These four techniques combine to create the full aesthetic with under 150 lines of CSS. No images, no JS frameworks, no performance overhead — just geometry and color working together.