A phrase renders invisible while a canvas draws a cloud of particles sampled from its own glyph shapes: the text is painted once into a 2x offscreen canvas, every third pixel with alpha above 0.35 becomes a dot, and each dot twinkles on its own sine phase at 30 fps. A tap opens an iris -- a radial-gradient mask on the text grows from the touch point over 820 ms on an easeOut curve, while each particle keeps twinkling until the wavefront reaches its distance, then dies over the following 26 px, sliding outward along its own radius as it goes. Tile mode swaps the plain host for a solid swatch that shuffles every 1.3-3.2 s and re-rolls on hover, with particles and revealed text taking the swatch's contrast colour. Reduced motion falls back to a CSS blur that simply clears on tap.
Source
"use client";
import { useState } from "react";
import Spoiler from "@/components/Spoiler";
import ScrambleLink from "../ScrambleLink";
/* The Telegram/Threads spoiler, shown on the three studio beliefs. Each phrase
renders invisible under a cloud of particles sampled from its own glyphs; a
tap opens an iris from the touch point and the cloud dissolves along the
wavefront. `tile` swaps the plain host for a solid swatch that shuffles on
its own and re-rolls on hover. Reveals are one-way, so `run` remounts the
set to re-obscure it. */
const BELIEFS = [
"Make it exist first",
"Embrace imperfection",
"God is in the details",
];
const FACE = "font-[family-name:var(--flux)]";
const action: React.CSSProperties = {
color: "var(--faint)",
fontFamily: "var(--flux)",
fontVariationSettings: '"wght" 400, "SRIF" 100',
};
export default function SpoilerText() {
const [run, setRun] = useState(0);
const [tile, setTile] = useState(false);
return (
<div
className="fixed inset-0 flex flex-col items-center justify-center gap-5 overflow-hidden px-6"
style={{ background: "var(--paper)" }}
>
{BELIEFS.map((text) => (
<Spoiler
key={`${text}-${run}-${tile}`}
text={text}
tile={tile}
// lab-keep: the spoiler's host IS the button, so the preview
// recorder's chrome-hiding rule has to leave it alone (lab.css)
className="lab-keep text-[clamp(1.1rem,2.6vw,2rem)] px-3 py-1 rounded-[10px]"
textClassName={FACE}
/>
))}
<div className="mt-8 flex gap-6 text-[11px] tracking-[0.1em] uppercase">
<button onClick={() => setRun((n) => n + 1)} style={action}>
re-obscure
</button>
<button
onClick={() => setTile((t) => !t)}
style={{ ...action, color: tile ? "var(--ink)" : "var(--faint)" }}
>
tile mode
</button>
</div>
<div className="fixed bottom-6 left-6 z-10">
<ScrambleLink
from="SPOILER"
to="← LAB"
href="/lab"
className="text-[11px] tracking-[0.1em] text-foreground/30 uppercase hover:text-foreground/60 transition-colors"
style={{ fontVariationSettings: "'wght' 400, 'SRIF' 100" }}
/>
</div>
</div>
);
}