bottega

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-bg

Usage

Usagetsx
<DriftCubesBg />

Props

PropTypeDefaultDescription
speednumber18Animation duration in seconds. Default: 18.
fadebooleanfalseApply 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