BlueprintGrid · BreathingWash · GlowRule

Three zero-JavaScript layers meant to stack. The wash breathes on an irregular cadence so it reads as coals, not a mechanical fade.

Install

$ npx shadcn@latest add wisp.pouriah.com/r/atmosphere.json

Pulls in seam.

Atmosphere · CSSno JS
blueprint grid + breathing wash + glow rule

BlueprintGrid · BreathingWash · GlowRuleesc or double-click to exit full screendouble-tap to exit

atmosphere/atmosphere.tsx
"use client";

import * as React from "react";
import { cx } from "@pouriahlabs/wisp-ui";

/**
 * The zero-JavaScript atmosphere layers, meant to stack.
 *
 * `<BlueprintGrid />` under `<BreathingWash />` under a canvas backdrop, with a
 * `<GlowRule />` on the section's bottom edge, is the whole Tier 1 stack. None
 * of these read the DOM or run a loop — they're custom properties and
 * keyframes, so they cost nothing and are stilled by the same
 * `prefers-reduced-motion` rule as everything else.
 *
 * Requires the Wisp keyframes, which ship in `@pouriahlabs/wisp-ui/styles.css` —
 * installing this component prints the exact import line, no separate stylesheet to add.
 */

interface LayerProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Token for the layer's colour. */
  token?: string;
}

/**
 * A cold blueprint grid, radial-masked so it fades toward the headline.
 * Precise and static: the counterweight to whatever moves above it.
 */
export function BlueprintGrid({
  token = "--wisp",
  className,
  style,
  ...props
}: LayerProps & { size?: number }) {
  const { size = 46, ...rest } = props as LayerProps & { size?: number };
  return (
    <div
      aria-hidden
      className={cx("wisp-grid pointer-events-none absolute inset-0", className)}
      style={
        {
          "--wisp-grid-ink": `hsl(var(${token}) / var(--wisp-grid-alpha, 0.13))`,
          "--wisp-grid-size": `${size}px`,
          ...style,
        } as React.CSSProperties
      }
      {...rest}
    />
  );
}

/**
 * Two radial washes that breathe on an irregular cadence — deliberately not a
 * clean sine, so it reads as a bed of coals rather than a mechanical fade.
 */
export function BreathingWash({
  fromToken = "--wisp",
  toToken = "--lantern",
  className,
  style,
  ...props
}: React.HTMLAttributes<HTMLDivElement> & { fromToken?: string; toToken?: string }) {
  return (
    <div
      aria-hidden
      className={cx("wisp-wash pointer-events-none absolute inset-0", className)}
      style={
        {
          "--wisp-wash-from": `hsl(var(${fromToken}) / var(--wisp-wash-a, 0.2))`,
          "--wisp-wash-to": `hsl(var(${toToken}) / var(--wisp-wash-b, 0.15))`,
          ...style,
        } as React.CSSProperties
      }
      {...props}
    />
  );
}

/**
 * A hairline on a section's edge whose glow travels along it, so the boundary
 * never sits perfectly still.
 */
export function GlowRule({ token = "--lantern", className, style, ...props }: LayerProps) {
  return (
    <div
      aria-hidden
      className={cx("wisp-rule pointer-events-none absolute inset-x-0 bottom-0 h-px", className)}
      style={{ "--wisp-rule-ink": `hsl(var(${token}))`, ...style } as React.CSSProperties}
      {...props}
    />
  );
}

Certified against the contract