backgrounds
Drift Cubes Background
A server component that renders an animated isometric cube wireframe layer (SVG) drifting slowly over a diagonal stripe backdrop. Full-bleed absolute layer; zero JS — animation is pure CSS keyframes with prefers-reduced-motion guard. Backdrop derives from --background + color-mix(--foreground 6%); cube strokes from color-mix(--foreground 12%, transparent), making it theme-robust on both light and dark surfaces.
Ported from chase2k25 (MIT)
drift-cubes-bg
Install
1. Register the namespace (once per project):
json
// components.json — register the @bottega namespace once
{
"registries": {
"@bottega": { "url": "https://bottega.ariacode.ca/r/{name}.json" }
}
}2. Add the component:
bash
npx shadcn add @bottega/drift-cubes-bgUsage
Usagetsx
<DriftCubesBg />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| speed | number | 18 | Animation duration in seconds. Default: 18. |
| fade | boolean | false | Apply a radial fade-out mask toward edges. Default: false. |
Source
drift-cubes-bg.tsxtsx
import type React from "react";
import styles from "./drift-cubes-bg.module.css";
export interface DriftCubesBgProps extends React.HTMLAttributes<HTMLDivElement> {
/** Animation duration in seconds. Default: 18. */
speed?: number;
/** Apply a radial fade-out mask toward edges. Default: false. */
fade?: boolean;
}
/**
* Animated isometric cube wireframe layer drifting over a diagonal stripe backdrop.
* Full-bleed absolute layer; server component (CSS-only animation, zero JS).
* Backdrop stripes derive from --background + color-mix(--foreground 6%);
* cube strokes derive from color-mix(--foreground 12%, transparent).
* Place in a `relative` parent — fills it absolutely with pointer-events-none.
*/
export function DriftCubesBg({
speed = 18,
fade = false,
className = "",
style,
...props
}: DriftCubesBgProps) {
return (
<div
aria-hidden="true"
className={["absolute inset-0 pointer-events-none overflow-hidden", className]
.filter(Boolean)
.join(" ")}
style={{
background: [
"repeating-linear-gradient(",
" 135deg,",
" var(--background) 0px,",
" var(--background) 60px,",
" color-mix(in oklch, var(--foreground) 6%, transparent) 70px,",
" color-mix(in oklch, var(--foreground) 6%, transparent) 130px",
")",
].join(""),
...(fade
? {
/* mask alpha — not a UI color */
maskImage:
"radial-gradient(ellipse 80% 80% at 50% 50%, rgb(0 0 0 / 1) 40%, transparent 100%)",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 50% 50%, rgb(0 0 0 / 1) 40%, transparent 100%)",
}
: {}),
...style,
}}
{...props}
>
{/*
SVG is 200% × 200% of the container, offset so the pattern bleeds beyond
edges; `preserveAspectRatio="xMidYMid slice"` makes it cover regardless
of aspect ratio. `vector-effect="non-scaling-stroke"` keeps strokes at a
consistent 1 px visual width even after the SVG scales up.
*/}
<svg
className={styles.cubeSvg}
style={{ "--drift-speed": `${speed}s` } as React.CSSProperties}
viewBox="0 0 120 104"
preserveAspectRatio="xMidYMid slice"
xmlns="http://www.w3.org/2000/svg"
>
{/*
Isometric cube wireframes.
Each cube = 3 polygons (top face, left face, right face).
Grid params: dx=20 (half-width), dy=10 (top-face rise), fh=20 (face height).
3 rows (cy=2, 42, 82) with staggered columns so cube vertices share edges.
*/}
<g
fill="none"
style={{ stroke: "color-mix(in oklch, var(--foreground) 12%, transparent)" }}
strokeWidth="1"
strokeLinejoin="round"
>
{/* ── Row 0 — cy=2 ── */}
{/* Cube A cx=20 */}
<polygon vectorEffect="non-scaling-stroke" points="20,2 40,12 20,22 0,12" />
<polygon vectorEffect="non-scaling-stroke" points="0,12 20,22 20,42 0,32" />
<polygon vectorEffect="non-scaling-stroke" points="20,22 40,12 40,32 20,42" />
{/* Cube B cx=60 */}
<polygon vectorEffect="non-scaling-stroke" points="60,2 80,12 60,22 40,12" />
<polygon vectorEffect="non-scaling-stroke" points="40,12 60,22 60,42 40,32" />
<polygon vectorEffect="non-scaling-stroke" points="60,22 80,12 80,32 60,42" />
{/* Cube C cx=100 */}
<polygon vectorEffect="non-scaling-stroke" points="100,2 120,12 100,22 80,12" />
<polygon vectorEffect="non-scaling-stroke" points="80,12 100,22 100,42 80,32" />
<polygon vectorEffect="non-scaling-stroke" points="100,22 120,12 120,32 100,42" />
{/* ── Row 1 — cy=42, staggered +20 ── */}
{/* Cube D cx=0 (left edge bleeds off — fine with overflow:hidden) */}
<polygon vectorEffect="non-scaling-stroke" points="0,42 20,52 0,62 -20,52" />
<polygon vectorEffect="non-scaling-stroke" points="-20,52 0,62 0,82 -20,72" />
<polygon vectorEffect="non-scaling-stroke" points="0,62 20,52 20,72 0,82" />
{/* Cube E cx=40 */}
<polygon vectorEffect="non-scaling-stroke" points="40,42 60,52 40,62 20,52" />
<polygon vectorEffect="non-scaling-stroke" points="20,52 40,62 40,82 20,72" />
<polygon vectorEffect="non-scaling-stroke" points="40,62 60,52 60,72 40,82" />
{/* Cube F cx=80 */}
<polygon vectorEffect="non-scaling-stroke" points="80,42 100,52 80,62 60,52" />
<polygon vectorEffect="non-scaling-stroke" points="60,52 80,62 80,82 60,72" />
<polygon vectorEffect="non-scaling-stroke" points="80,62 100,52 100,72 80,82" />
{/* Cube G cx=120 (right edge bleeds off) */}
<polygon vectorEffect="non-scaling-stroke" points="120,42 140,52 120,62 100,52" />
<polygon vectorEffect="non-scaling-stroke" points="100,52 120,62 120,82 100,72" />
<polygon vectorEffect="non-scaling-stroke" points="120,62 140,52 140,72 120,82" />
{/* ── Row 2 — cy=82 ── */}
{/* Cube H cx=20 */}
<polygon vectorEffect="non-scaling-stroke" points="20,82 40,92 20,102 0,92" />
<polygon vectorEffect="non-scaling-stroke" points="0,92 20,102 20,122 0,112" />
<polygon vectorEffect="non-scaling-stroke" points="20,102 40,92 40,112 20,122" />
{/* Cube I cx=60 */}
<polygon vectorEffect="non-scaling-stroke" points="60,82 80,92 60,102 40,92" />
<polygon vectorEffect="non-scaling-stroke" points="40,92 60,102 60,122 40,112" />
<polygon vectorEffect="non-scaling-stroke" points="60,102 80,92 80,112 60,122" />
{/* Cube J cx=100 */}
<polygon vectorEffect="non-scaling-stroke" points="100,82 120,92 100,102 80,92" />
<polygon vectorEffect="non-scaling-stroke" points="80,92 100,102 100,122 80,112" />
<polygon vectorEffect="non-scaling-stroke" points="100,102 120,92 120,112 100,122" />
</g>
</svg>
</div>
);
}
Dependencies
- @bottega/tokens