Dark surfaces now carry light: frames glow along their edge and shed binary digits (BitAura), dark sections open with a lit seam (BitSeam), the career path is a line of light and "My work" became a dark stage. Buttons carry a travelling light and a spot that follows the pointer (BitGlow). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
|
|
const SURFACE = "[data-glow]";
|
|
|
|
/**
|
|
* BitGlow: a light that follows the pointer across every `data-glow` surface.
|
|
* It only publishes the pointer position as `--gx` / `--gy` on those surfaces;
|
|
* the light itself is drawn in CSS by a `.bit-glow` child (see globals.css).
|
|
* Purely decorative, and off when reduced motion is requested.
|
|
*/
|
|
export const BitGlow: React.FC = () => {
|
|
React.useEffect(() => {
|
|
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
|
|
let frame = 0;
|
|
let latest: PointerEvent | null = null;
|
|
|
|
const publish = () => {
|
|
frame = 0;
|
|
if (!latest) return;
|
|
const { clientX, clientY, target } = latest;
|
|
let surface =
|
|
target instanceof Element ? target.closest<HTMLElement>(SURFACE) : null;
|
|
// Surfaces nest (a button inside a dark section): each one gets its own position.
|
|
while (surface) {
|
|
const rect = surface.getBoundingClientRect();
|
|
surface.style.setProperty("--gx", `${clientX - rect.left}px`);
|
|
surface.style.setProperty("--gy", `${clientY - rect.top}px`);
|
|
surface =
|
|
surface.parentElement?.closest<HTMLElement>(SURFACE) ?? null;
|
|
}
|
|
};
|
|
|
|
const onPointerMove = (event: PointerEvent) => {
|
|
latest = event;
|
|
if (frame === 0) frame = window.requestAnimationFrame(publish);
|
|
};
|
|
|
|
window.addEventListener("pointermove", onPointerMove, { passive: true });
|
|
return () => {
|
|
window.removeEventListener("pointermove", onPointerMove);
|
|
if (frame !== 0) window.cancelAnimationFrame(frame);
|
|
};
|
|
}, []);
|
|
|
|
return null;
|
|
};
|