---
title: "Signature effect recipes (library effect → lean void component) (components)"
description: "Ten clean-room translations of the effects agents most often copy from React Bits, Magic UI, Aceternity, skiper-ui and motion-primitives."
canonical: https://void-design.vercel.app/docs/components/recipes
lastModified: 2026-09-16
---

# Signature effect recipes (library effect → lean void component)

Ten clean-room translations of the effects agents most often copy from React Bits, Magic UI, Aceternity, skiper-ui and motion-primitives. Every snippet here was built with `next build` (Next 16.3.5, React 19.2, Tailwind 4.3.3, `@void/tokens` precision direction), rendered in dark, light, reduced-motion and 390px, checked for real text in the server HTML with JS disabled, and scroll-measured with `void smooth` (verified 2026-09-16).

**Rules shared by all ten**
- Tokens only: `--brand`, `--brand-line`, `--fg`, `--fg-muted`, `--surface`, `--line`, `--bg`, `--radius-xl`, `--ease-out`, `--ease-in-out`, `--ease-out-expo`, `--duration-slow`, `--duration-slower`. No hex, no palette classes.
- Continuous motion uses only `transform`/`translate`/`opacity`. Shiny text (paint) is the one exception, and it is limited to one short string.
- Every infinite animation carries `data-fx` and is paused offscreen by `FxGate`. Every effect has a `prefers-reduced-motion: reduce` frame that shows the finished state.
- Server Components by default. Only `SpotlightCard`, `NumberTicker`, `Magnetic` and `FxGate` are client islands, each under 1 KB.
- Decorative layers are `aria-hidden`, `pointer-events-none`, absolutely positioned, and sit behind the content. Their parent needs `relative isolate`.
- These are **independent implementations**. No upstream source was copied, so they carry an "inspired by" courtesy comment and no licence obligation.

| # | Component | Inspired by | Tier / JS | Client? |
|---|---|---|---|---|
| 1 | `Marquee` | Magic UI marquee, React Bits LogoLoop | T1 / 0 KB | no |
| 2 | `SpotlightCard` | React Bits SpotlightCard, Magic UI magic-card | T2 / ~0.5 KB | yes |
| 3 | `RevealText` | React Bits SplitText/BlurText, Aceternity text-generate-effect | T1 / 0 KB | no |
| 4 | `BeamBorder` | Magic UI border-beam, React Bits StarBorder | T1 / 0 KB | no |
| 5 | `PatternBackdrop` (+ `grain`) | Magic UI grid/dot-pattern, Aceternity background-beams | T0 / 0 KB | no |
| 6 | `NumberTicker` | Magic UI number-ticker, React Bits CountUp | T2 / ~0.6 KB | yes |
| 7 | `Magnetic` | React Bits Magnet, motion-primitives magnetic | T2 / ~0.5 KB | yes |
| 8 | `ShinyText` | React Bits ShinyText, Magic UI animated-shiny-text | T1-paint / 0 KB | no |
| 9 | `AuroraBackdrop` | React Bits Aurora/Silk (WebGL) | T1 / 0 KB | no |
| 10 | `HeroSpotlight` | Aceternity spotlight | T1 one-shot / 0 KB | no |

Budget: **1 signature + 1 secondary per page** (see SKILL.md). This page shows all ten only because it is a test bench.

## 0. Setup (once per project)

1. Save each component to `src/components/fx/<kebab-name>.tsx`. They import `cn` from `@/lib/cn`, the template's class joiner.
2. Save the CSS blocks of the recipes you use to `src/styles/fx.css`, then add `@import "../styles/fx.css";` as the **last** line of `src/app/globals.css`, after the direction import. Only paste the blocks you use; each block is self-contained.
3. Mount `<FxGate />` once in `src/app/layout.tsx`, inside `<body>`, after `<SiteFooter />`.

Header and shared pause rule for `src/styles/fx.css`:

```css
/* fx.css: void signature effects. Import in globals.css AFTER the direction file.
   Tokens only (--brand, --fg, --fg-muted, --surface, --line, --bg, --radius-*, --ease-*, --duration-*).
   Classes live in @layer components so Tailwind utilities (p-6, rounded-2xl…) still override them. */

/* ── 0. Offscreen pause, toggled by <FxGate /> on every [data-fx] element ─────────────────── */
[data-fx-paused],
[data-fx-paused] *,
[data-fx-paused]::before,
[data-fx-paused]::after,
[data-fx-paused] ::before,
[data-fx-paused] ::after {
  animation-play-state: paused !important;
}
```

`src/components/fx/fx-gate.tsx` (a ~0.4 KB client island). Hidden tabs already stop CSS animations, so no `visibilitychange` listener is needed. For a single client effect you can wrap it in `speed` → `PauseOffscreen` instead; both mechanisms can coexist.

```tsx
"use client";
import { usePathname } from "next/navigation";
import { useEffect } from "react";

/** Pauses CSS animations of every [data-fx] element while it is offscreen. Mount once in
 *  app/layout.tsx. Starts running; pauses only on evidence of being offscreen (no delayed entrances). */
export function FxGate() {
  const pathname = usePathname();
  useEffect(() => {
    const els = document.querySelectorAll<HTMLElement>("[data-fx]");
    if (els.length === 0) return;
    const io = new IntersectionObserver(
      (entries) => {
        for (const e of entries) e.target.toggleAttribute("data-fx-paused", !e.isIntersecting);
      },
      { rootMargin: "120px" },
    );
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, [pathname]); // re-scan after client navigation
  return null;
}
```

Why start *running* and only pause on evidence of being offscreen: starting paused delays entrances until hydration.

---

## 1. Marquee (logo wall, testimonials)

- **Inspired by:** Magic UI `marquee` (https://magicui.design/docs/components/marquee), React Bits `LogoLoop` (https://reactbits.dev).
- **Upstream bugs fixed:** 4× duplicated children with no `aria-hidden`/`inert` (links read and tabbed 4×); never pauses offscreen; no reduced motion; hover-only pause.
- **Tier:** T1, 0 KB JS. Compositor `transform` only.
- **Reduced motion:** no animation, the duplicate is removed, and the row becomes horizontally scrollable.
- **A11y:** a labelled `<section>` wrapping a `<ul>`. The duplicate track is `aria-hidden` + `inert`. The animation pauses on hover (fine pointers) and on `:focus-within` (keyboard). `pausable` adds a native checkbox pause (zero JS, WCAG 2.2.2) and is **required for testimonials** or any content that moves for more than 5s. Logo text must pass contrast: use `text-fg-muted`, not `text-fg-subtle`. (Historical: axe flagged 3.84:1 in the light theme before `--fg-subtle` was raised to AA; muted is still the right tier for a row of logo wordmarks.)
- **SSR:** a Server Component. Both tracks are in the HTML.

```tsx
import type { CSSProperties, ReactNode } from "react";
import { cn } from "@/lib/cn";

type MarqueeProps = {
  /** <li> elements. Logos: next/image with width/height + alt, equal visual weight. */
  children: ReactNode;
  /** Accessible name for the region, e.g. "Companies using Acme". */
  label: string;
  /** Seconds per loop. ≈ 40s per 1600px of content; never faster than ~60px/s. */
  duration?: number;
  reverse?: boolean;
  /** Content that moves > 5s and carries information (testimonials) needs a pause control (WCAG 2.2.2). */
  pausable?: boolean;
  className?: string;
};

/** Server Component. Two tracks; the duplicate is aria-hidden + inert (no double reading, no double tab stops). */
export function Marquee({ children, label, duration = 40, reverse = false, pausable = false, className }: MarqueeProps) {
  return (
    <div className={cn("fx-marquee-wrap", className)}>
      <section
        aria-label={label}
        data-fx="marquee"
        data-reverse={reverse || undefined}
        className="fx-marquee"
        style={{ "--fx-marquee-duration": `${duration}s` } as CSSProperties}
      >
        <ul className="fx-marquee__track">{children}</ul>
        <ul className="fx-marquee__track" aria-hidden="true" inert>
          {children}
        </ul>
      </section>
      {pausable ? (
        <label className="mt-3 inline-flex cursor-pointer items-center gap-2 text-sm text-fg-muted">
          <input type="checkbox" className="fx-marquee__pause" /> Pause scrolling
        </label>
      ) : null}
    </div>
  );
}
```

```css
/* ── 1. Marquee (T1) ──────────────────────────────────────────────────────────────────── */
@layer components {
  .fx-marquee {
    --fx-gap: 3rem;
    display: flex;
    gap: var(--fx-gap);
    overflow: hidden;
    padding-block: 0.25rem; /* room for focus rings */
    mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
  }
  .fx-marquee__track {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: space-around;
    gap: var(--fx-gap);
    min-width: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
    animation: fx-marquee var(--fx-marquee-duration, 40s) linear infinite;
  }
  .fx-marquee[data-reverse] .fx-marquee__track { animation-direction: reverse; }
  .fx-marquee:focus-within .fx-marquee__track,
  .fx-marquee-wrap:has(.fx-marquee__pause:checked) .fx-marquee__track { animation-play-state: paused; }
  @media (hover: hover) and (pointer: fine) {
    .fx-marquee:hover .fx-marquee__track { animation-play-state: paused; }
  }
  @media (prefers-reduced-motion: reduce) {
    .fx-marquee { overflow-x: auto; mask-image: none; }
    .fx-marquee__track { animation: none; }
    .fx-marquee__track[aria-hidden] { display: none; }
    .fx-marquee-wrap label:has(.fx-marquee__pause) { display: none; }
  }
}
@keyframes fx-marquee { to { transform: translateX(calc(-100% - var(--fx-gap))); } }
```

```tsx
<Marquee label="Companies using Acme">
  {logos.map((l) => (
    <li key={l.name}>
      <Image src={l.src} alt={l.name} width={120} height={32} className="h-8 w-auto opacity-80" />
    </li>
  ))}
</Marquee>
```

Each track has `min-width: 100%`, so the loop is seamless with any item count. For fewer than about 6 logos, use a static row instead.

## 2. SpotlightCard (pointer glow on a feature grid)

- **Inspired by:** React Bits `SpotlightCard` (https://reactbits.dev), Magic UI `magic-card`.
- **Upstream bugs fixed:** `setState` on every mousemove, which re-renders children and repaints a full-card `radial-gradient`; a conditional `useMotionTemplate` hook (magic-card); hardcoded neutral palette.
- **Technique:** a pre-rasterised radial blob moved through the `translate` property. Pointer events are coalesced into one rAF, with one `getBoundingClientRect` per frame and zero React renders. A 120ms `translate` transition retargets every frame, which reads as a light spring without motion.
- **Tier:** T2, ~0.5 KB.
- **Reduced motion / touch / pen:** the effect never attaches, leaving a static `surface-card`.
- **A11y:** the glow is `aria-hidden`. Keyboard parity comes from `:focus-within`, which raises the border to `--brand-line`. Put a real link or button inside the card.
- **SSR:** a client component, but children render on the server (pass Server Components as `children`).

```tsx
"use client";
import { useEffect, useRef, type ComponentProps } from "react";
import { cn } from "@/lib/cn";

/** Pointer-following glow. Zero React renders per move: one rAF-coalesced `translate` write on a
 *  pre-rasterised blob. Touch, pen, keyboard and reduced motion get the static card (+ focus border). */
export function SpotlightCard({ className, children, ...rest }: ComponentProps<"div">) {
  const cardRef = useRef<HTMLDivElement>(null);
  const glowRef = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    const card = cardRef.current;
    const glow = glowRef.current;
    if (!card || !glow) return;
    if (!matchMedia("(hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference)").matches) return;

    let raf = 0;
    let px = 0;
    let py = 0;
    const write = () => {
      raf = 0;
      const r = card.getBoundingClientRect(); // one read per frame, not per event
      glow.style.translate = `${px - r.left}px ${py - r.top}px`;
    };
    const onMove = (e: PointerEvent) => {
      if (e.pointerType !== "mouse") return;
      px = e.clientX;
      py = e.clientY;
      if (!raf) raf = requestAnimationFrame(write);
    };
    card.addEventListener("pointermove", onMove, { passive: true });
    return () => {
      card.removeEventListener("pointermove", onMove);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);

  return (
    <div ref={cardRef} className={cn("fx-spot surface-card p-6", className)} {...rest}>
      <span ref={glowRef} className="fx-spot__glow" aria-hidden="true" />
      {children}
    </div>
  );
}
```

```css
/* ── 2. Spotlight card (T2, ~0.5 KB) ──────────────────────────────────────────────────── */
@layer components {
  .fx-spot {
    position: relative;
    isolation: isolate;
    overflow: hidden;
  }
  .fx-spot__glow {
    position: absolute;
    top: 0;
    left: 0;
    z-index: -1;
    width: var(--fx-spot-size, 22rem);
    aspect-ratio: 1;
    border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(closest-side, color-mix(in oklab, var(--brand) 22%, transparent), transparent);
    transform: translate(-50%, -50%); /* centres on the pointer; JS writes the `translate` property */
    opacity: 0;
    transition: opacity var(--duration-slow) ease, translate 120ms var(--ease-out);
  }
  @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
    .fx-spot:hover .fx-spot__glow { opacity: 1; }
  }
  .fx-spot:focus-within { border-color: var(--brand-line); } /* keyboard parity */
}
```

Use one spotlight grid per page (it is the "secondary" effect). Each card owns its own listener, so there are no window listeners.

## 3. RevealText (per-word headline reveal)

- **Inspired by:** React Bits `SplitText` / `BlurText` (https://reactbits.dev), Aceternity `text-generate-effect`, Magic UI `text-animate`.
- **Upstream bugs fixed:** GSAP + SplitText (45–70 KB); SSR text at `opacity:0` (invisible without JS, LCP delayed to the end of the stagger); hydration flash; per-character nodes; duplicate `aria-label` + `sr-only` copies.
- **Tier:** T1, 0 KB JS.
- **`trigger="load"`** (hero h1): words rise 0.3em via `@starting-style` with a 40ms stagger capped at 400ms. **Opacity stays 1**, so the LCP is the first paint. This also works on client navigations, since new elements get a starting style too.
- **`trigger="scroll"`** (below the fold only): each word fades and rises, scrubbed by `animation-timeline: view()`. Browsers without scroll timelines show static text.
- **Reduced motion:** static text, and `--duration-slower` is 0 in base.css.
- **A11y:** one copy of the text with real spaces between inline-block words, so screen readers read it normally.
- **SSR:** full text in the server HTML (verified with JS disabled).

```tsx
import type { CSSProperties, ElementType } from "react";
import { cn } from "@/lib/cn";

type RevealTextProps = {
  text: string;
  as?: ElementType;
  /** "load": hero/LCP-safe rise on first paint (opacity never 0).
   *  "scroll": scrubbed by a CSS view() timeline where supported, static elsewhere. Below the fold only. */
  trigger?: "load" | "scroll";
  className?: string;
};

/** Server Component. Splits per word (never per character); real spaces keep one readable copy for AT. */
export function RevealText({ text, as: Tag = "h2", trigger = "scroll", className }: RevealTextProps) {
  const words = text.trim().split(/\s+/);
  return (
    <Tag className={cn("fx-reveal", className)} data-trigger={trigger}>
      {words.map((word, i) => (
        <span key={i}>
          <span className="fx-reveal__word" style={{ "--i": i } as CSSProperties}>
            {word}
          </span>
          {i < words.length - 1 ? " " : null}
        </span>
      ))}
    </Tag>
  );
}
```

```css
/* ── 3. Text reveal (T1) ──────────────────────────────────────────────────────────────── */
@layer components {
  .fx-reveal { --fx-stagger: 40ms; }
  .fx-reveal__word { display: inline-block; }
  @media (prefers-reduced-motion: no-preference) {
    /* load: movement only, opacity stays 1 → LCP is the first paint */
    .fx-reveal[data-trigger="load"] .fx-reveal__word {
      transition: translate var(--duration-slower) var(--ease-out-expo);
      transition-delay: min(calc(var(--i) * var(--fx-stagger)), 400ms);
      @starting-style { translate: 0 0.3em; }
    }
    /* scroll: scrubbed by the view timeline; unsupported browsers show static text */
    @supports (animation-timeline: view()) {
      .fx-reveal[data-trigger="scroll"] .fx-reveal__word {
        animation: fx-rise linear both;
        animation-timeline: view();
        animation-range: entry calc(5% + var(--i) * 1.5%) cover calc(25% + var(--i) * 1.5%);
      }
    }
  }
}
@keyframes fx-rise { from { opacity: 0; translate: 0 0.5em; } to { opacity: 1; translate: 0 0; } }
```

```tsx
<RevealText as="h1" trigger="load" text="Ship interfaces that feel inevitable" className="text-display-lg" />
```

Keep it to 1–2 headings per page. For block-level section reveals use base.css `reveal`; for masked line rises use `motion` → `references/recipes.md` §10. Don't add `clip-path` or `opacity:0` to the load variant: whether a fully clipped first frame counts as painted for LCP is unverified.

## 4. BeamBorder (a light travelling around one card)

- **Inspired by:** Magic UI `border-beam` (https://magicui.design/docs/components/border-beam), React Bits `StarBorder`, motion-primitives `border-trail`.
- **Upstream bugs fixed:** motion animating `offset-distance` on the JS main thread forever (border-beam); keyframes that exist only in a commented `tailwind.config.js`, so the effect is dead on v4 (StarBorder); `<button>` without `type`; hardcoded `#ffaa40 → #9c40ff`.
- **Technique:** an oversized conic-gradient layer rotates (compositor `transform`) behind a 1px padding gap. The inner surface must be opaque.
- **Tier:** T1, 0 KB.
- **Reduced motion:** a static `--brand-line` border.
- **A11y:** purely decorative, with no extra DOM.
- **SSR:** a Server Component.

```tsx
import type { ComponentProps } from "react";
import { cn } from "@/lib/cn";

type Props = ComponentProps<"div"> & {
  /** Set when height > ~1.7 × width so the rotating layer still covers the corners. */
  tall?: boolean;
  innerClassName?: string;
};

/** Server Component. A 1px border whose brand highlight travels around once per 6s.
 *  Compositor-only (rotates a conic layer); paused offscreen via data-fx; static brand border under reduced motion. */
export function BeamBorder({ tall, className, innerClassName, children, ...rest }: Props) {
  return (
    <div data-fx="beam" data-tall={tall || undefined} className={cn("fx-beam", className)} {...rest}>
      <div className={cn("fx-beam__inner p-6", innerClassName)}>{children}</div>
    </div>
  );
}
```

```css
/* ── 4. Border beam (T1) ──────────────────────────────────────────────────────────────── */
@layer components {
  .fx-beam {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    padding: 1px; /* border width */
    border-radius: var(--radius-xl);
    background: var(--line);
  }
  .fx-beam::before {
    content: "";
    position: absolute;
    z-index: -1;
    top: 50%;
    left: 50%;
    width: 200%; /* square ≥ diagonal while height ≤ 1.7 × width; use data-tall otherwise */
    aspect-ratio: 1;
    background: conic-gradient(from 0turn, transparent 0 75%, color-mix(in oklab, var(--brand) 90%, transparent) 90%, transparent 100%);
    transform: translate(-50%, -50%) rotate(0turn);
    animation: fx-spin var(--fx-beam-duration, 6s) linear infinite;
  }
  .fx-beam[data-tall]::before { width: auto; height: 200%; }
  .fx-beam__inner {
    height: 100%;
    border-radius: calc(var(--radius-xl) - 1px);
    background: var(--surface); /* must be opaque */
  }
  @media (prefers-reduced-motion: reduce) {
    .fx-beam { background: var(--brand-line); }
    .fx-beam::before { animation: none; display: none; }
  }
}
@keyframes fx-spin { to { transform: translate(-50%, -50%) rotate(1turn); } }
```

Use it on the **one** card that represents a state ("Recommended", "Live", "Provisioning"): ≤1 in view, ≤3 per page. Pass `tall` when height is more than 1.7× the width. For a transparent interior, animate an `@property --angle` conic `border-image` instead; that repaints every frame, so use one per viewport at most.

## 5. PatternBackdrop (grid, dots, glow) and grain

- **Inspired by:** Magic UI `grid-pattern` / `dot-pattern` (https://magicui.design), Aceternity `background-beams` / `dotted-glow-background`, pattern-craft.
- **Upstream bugs fixed:** `dot-pattern` with glow renders ~5,000 `<circle>`s, each with an infinite spring; `background-beams` animates 50 SVG gradients and calls `Math.random()` in render (hydration mismatch); `dotted-glow-background` keeps its rAF running offscreen and calls `getBoundingClientRect` inside the loop.
- **Tier:** T0, 0 KB, no animation.
- **Rules:** one glow per page, and patterns are always masked (an unmasked full-bleed grid is a template tell). For grain, add base.css `grain` to the section. It reads the direction's `--grain-opacity` (0 in light, ≤0.035 in dark), so never hardcode an opacity.
- **A11y / SSR:** `aria-hidden`, Server Component.

```tsx
import { cn } from "@/lib/cn";

type Props = { pattern?: "grid" | "dots" | "none"; glow?: boolean; className?: string };

/** Server Component, zero JS, no animation. One masked pattern + one brand glow.
 *  Parent needs `relative isolate`. Grain: add the base.css `grain` utility to the section instead. */
export function PatternBackdrop({ pattern = "grid", glow = true, className }: Props) {
  return (
    <div aria-hidden="true" className={cn("pointer-events-none absolute inset-0 -z-10", glow && "bg-fx-glow", className)}>
      {pattern !== "none" ? <div className={cn("absolute inset-0", pattern === "grid" ? "bg-fx-grid" : "bg-fx-dots")} /> : null}
    </div>
  );
}
```

```css
/* ── 5. Backgrounds (T0, zero JS). Grain: use base.css `grain` (per-direction --grain-opacity). ── */
@utility bg-fx-grid {
  --fx-cell: 32px;
  --fx-line: color-mix(in oklab, var(--fg) 7%, transparent);
  background-image:
    linear-gradient(to right, var(--fx-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--fx-line) 1px, transparent 1px);
  background-size: var(--fx-cell) var(--fx-cell);
  background-position: center top;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, black 30%, transparent 75%);
}
@utility bg-fx-dots {
  --fx-cell: 20px;
  --fx-dot: color-mix(in oklab, var(--fg) 14%, transparent);
  background-image: radial-gradient(circle at center, var(--fx-dot) 1px, transparent 1.5px);
  background-size: var(--fx-cell) var(--fx-cell);
  mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, black 20%, transparent 70%);
}
@utility bg-fx-glow {
  background-image: radial-gradient(60% 50% at 50% -10%, color-mix(in oklab, var(--brand) 14%, transparent), transparent 70%);
}
```

```tsx
<section className="relative isolate grain">
  <PatternBackdrop pattern="grid" />
  …hero content…
</section>
```

Tune with arbitrary properties rather than new CSS: `[--fx-cell:24px]`, and for density `[--fx-line:color-mix(in_oklab,var(--fg)_10%,transparent)]`.

## 6. NumberTicker (stats that count up)

- **Inspired by:** Magic UI `number-ticker` (https://magicui.design/docs/components/number-ticker), React Bits `CountUp`.
- **Upstream bugs fixed:** server HTML contains `0` (Magic UI) or an empty string (React Bits), so crawlers, link previews and no-JS users see no number; a `new Intl.NumberFormat` every frame; hardcoded `en-US`; spring damping 60 (slow settle); CLS as the width grows; replays on every scroll-by.
- **Prefer `@number-flow/react`** when the dependency is acceptable (it handles locale, digit width and reduced motion). Use this component when it isn't.
- **Behaviour:** SSR renders the final formatted value. If the number is already visible on arrival, it never animates. If it starts offscreen, it is reset to 0 while invisible, reserves its final pixel width, and counts up once (900ms, ease-out quart) when 60% visible.
- **Tier:** T2, ~0.6 KB.
- **Reduced motion:** the final value, no animation.
- **A11y:** the animated copy is `aria-hidden`; screen readers get the stable `sr-only` copy. `data-numeric` gives tabular figures.
- **SSR:** `12,400` in the HTML (verified with JS disabled).

```tsx
"use client";
import { useEffect, useMemo, useRef } from "react";

const formatters = new Map<string, Intl.NumberFormat>();
function getFormatter(locale: string, optionsKey: string) {
  const key = `${locale}|${optionsKey}`;
  let f = formatters.get(key);
  if (!f) formatters.set(key, (f = new Intl.NumberFormat(locale, JSON.parse(optionsKey))));
  return f;
}

type Props = {
  value: number;
  locale?: string;
  options?: Intl.NumberFormatOptions;
  durationMs?: number;
  className?: string;
};

/** Server HTML contains the final formatted value. Counts up once, only if it starts offscreen.
 *  Screen readers get the stable copy; the animated copy is aria-hidden with reserved width (no CLS). */
export function NumberTicker({ value, locale = "en-US", options, durationMs = 900, className }: Props) {
  const ref = useRef<HTMLSpanElement>(null);
  const optionsKey = JSON.stringify(options ?? {});
  const fmt = useMemo(() => getFormatter(locale, optionsKey), [locale, optionsKey]);
  const final = fmt.format(value);

  useEffect(() => {
    const el = ref.current;
    if (!el || matchMedia("(prefers-reduced-motion: reduce)").matches) return;

    let raf = 0;
    let first = true;
    const io = new IntersectionObserver(
      ([entry]) => {
        if (first) {
          first = false;
          if (entry.isIntersecting) return io.disconnect(); // visible on arrival: never yank to 0
          el.style.minWidth = `${el.offsetWidth}px`; // reserve the final width: no shift while counting
          el.textContent = fmt.format(0); // offscreen, invisible reset
          return;
        }
        if (!entry.isIntersecting) return;
        io.disconnect(); // once
        let start = 0;
        const tick = (t: number) => {
          if (!start) start = t;
          const p = Math.min((t - start) / durationMs, 1);
          el.textContent = fmt.format(value * (1 - (1 - p) ** 4)); // ease-out quart
          if (p < 1) raf = requestAnimationFrame(tick);
        };
        raf = requestAnimationFrame(tick);
      },
      { threshold: 0.6 },
    );
    io.observe(el);
    return () => {
      io.disconnect();
      if (raf) cancelAnimationFrame(raf);
      el.textContent = final;
      el.style.minWidth = "";
    };
  }, [value, durationMs, fmt, final]);

  return (
    <span className={className} data-numeric>
      <span ref={ref} aria-hidden="true" className="inline-block tabular-nums">
        {final}
      </span>
      <span className="sr-only">{final}</span>
    </span>
  );
}
```

```tsx
<p className="font-display text-display-sm"><NumberTicker value={12400} /></p>
<NumberTicker value={98.6} options={{ maximumFractionDigits: 1 }} />%
```

## 7. Magnetic (a CTA that leans toward the cursor)

- **Inspired by:** React Bits `Magnet` (https://reactbits.dev), motion-primitives `magnetic`.
- **Upstream bugs fixed:** one `window` mousemove listener per instance; `getBoundingClientRect` + `setState` on every move; no touch or reduced-motion gate; `ease-in-out` release.
- **Technique:** element-scoped pointer events. It measures once on enter and writes `translate` once per frame. A CSS transition does the spring-back (450ms `--ease-out`) and smooth tracking (120ms). Strength is capped at 0.3, so the element stays under the cursor and `pointerleave` stays reliable. A `::before` 16px halo widens the capture zone without layout.
- **Tier:** T2, ~0.5 KB.
- **Reduced motion / touch:** never attaches.
- **A11y:** wraps a real `<a>`/`<button>`. Press feedback stays on the child (template `Button` has `press`). Never use it on toolbars, nav or anything clicked daily.
- **SSR:** a client wrapper; children render on the server.

```tsx
"use client";
import { useEffect, useRef, type ReactNode } from "react";

/** Pulls its child toward the pointer. Element-scoped listeners (no window listener), measure once on
 *  enter, one `translate` write per frame, CSS transition springs it home. Mouse + motion-OK only. */
export function Magnetic({ children, strength = 0.25 }: { children: ReactNode; strength?: number }) {
  const ref = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (!matchMedia("(hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference)").matches) return;
    const k = Math.min(strength, 0.3); // stay under the cursor so pointerleave stays reliable

    let raf = 0;
    let rect: DOMRect | null = null;
    let x = 0;
    let y = 0;
    const onEnter = () => {
      el.style.translate = "";
      rect = el.getBoundingClientRect();
      el.dataset.magnet = "on";
    };
    const onMove = (e: PointerEvent) => {
      if (!rect || e.pointerType !== "mouse") return;
      x = (e.clientX - (rect.left + rect.width / 2)) * k;
      y = (e.clientY - (rect.top + rect.height / 2)) * k;
      if (!raf)
        raf = requestAnimationFrame(() => {
          raf = 0;
          el.style.translate = `${x}px ${y}px`;
        });
    };
    const onLeave = () => {
      if (raf) cancelAnimationFrame(raf);
      raf = 0;
      rect = null;
      delete el.dataset.magnet;
      el.style.translate = "";
    };
    el.addEventListener("pointerenter", onEnter);
    el.addEventListener("pointermove", onMove, { passive: true });
    el.addEventListener("pointerleave", onLeave);
    return () => {
      el.removeEventListener("pointerenter", onEnter);
      el.removeEventListener("pointermove", onMove);
      el.removeEventListener("pointerleave", onLeave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [strength]);

  return (
    <span ref={ref} className="fx-magnet">
      {children}
    </span>
  );
}
```

```css
/* ── 7. Magnetic (T2, ~0.5 KB) ────────────────────────────────────────────────────────── */
@layer components {
  .fx-magnet {
    position: relative;
    display: inline-block;
    transition: translate 450ms var(--ease-out); /* release: settles softly */
  }
  .fx-magnet[data-magnet] { transition-duration: 120ms; } /* tracking: retargets each frame */
  .fx-magnet::before { content: ""; position: absolute; inset: -16px; } /* capture zone, no layout */
}
```

```tsx
<Magnetic><Button size="lg" href="/start">Get started</Button></Magnetic>
```

## 8. ShinyText (a sweep of light across a label)

- **Inspired by:** React Bits `ShinyText` (https://reactbits.dev), Magic UI `animated-shiny-text`, motion-primitives `text-shimmer`.
- **Upstream bugs fixed:** a perpetual `useAnimationFrame` JS loop per instance writing `background-position` (it keeps ticking even when `disabled`); no offscreen or reduced-motion handling.
- **Tier:** T1-paint, 0 KB. `background-clip: text` can't be moved by transform, so this is a **paint** animation. It gets its own layer (`will-change: transform`) so each frame repaints only the label.
- **Measured:** without its own layer, a shiny eyebrow on top of `AuroraBackdrop` dropped 14–46% of frames while scrolling (headless, software raster); without the shine, 0–4%. **Don't put ShinyText over an animated backdrop** even with the fix. They count as two effects in one viewport anyway.
- **Reduced motion:** plain `--fg-muted` text.
- **A11y:** real text, and contrast stays within the fg scale. **Never on h1/h2** (that is `design/gradient-text`), never on body copy, never in brand colour.
- **SSR:** a Server Component.

```tsx
import type { ComponentProps } from "react";
import { cn } from "@/lib/cn";

/** Server Component. A light sweep across one short muted string (eyebrow, badge). Paint animation:
 *  one per viewport, never on h1/body copy, never on top of another animated layer. */
export function ShinyText({ className, ...rest }: ComponentProps<"span">) {
  return <span data-fx="shine" className={cn("fx-shine", className)} {...rest} />;
}
```

```css
/* ── 8. Shiny text (T1-paint: one short string per viewport) ─────────────────────────── */
@layer components {
  .fx-shine {
    color: var(--fg-muted); /* fallback */
    background-image: linear-gradient(100deg, var(--fg-muted) 40%, var(--fg) 50%, var(--fg-muted) 60%);
    background-size: 250% 100%;
    background-position: 100% 0;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: fx-shine 3.2s var(--ease-in-out) infinite;
    /* own layer: otherwise each frame's repaint invalidates whatever big layer it shares (e.g. text over
       an animated backdrop repaints the whole hero: measured 15% → 1.4% dropped frames) */
    will-change: transform;
  }
  @media (prefers-reduced-motion: reduce) {
    .fx-shine { animation: none; background: none; -webkit-text-fill-color: currentColor; will-change: auto; }
  }
}
@keyframes fx-shine { 0% { background-position: 100% 0; } 55%, 100% { background-position: 0% 0; } }
```

```tsx
<p className="label-mono"><ShinyText>Now with agent skills</ShinyText></p>
```

## 9. AuroraBackdrop (WebGL aurora → CSS)

- **Inspired by:** React Bits `Aurora` / `Silk` / `SoftAurora` (https://reactbits.dev), Aceternity `aurora-background`.
- **Upstream bugs fixed:** three + R3F (~150 KB) or ogl for one full-screen quad; `new Color()` ×3 per frame; effect deps that rebuild the GL context; no DPR cap, offscreen pause or reduced motion.
- **Technique:** three large radial gradients with the softness baked in (**no `filter: blur`**), each drifting 27–41s via `transform`. That is 3 composited layers and zero repaints while idle. It gives about 80% of the shader mood at 0 KB. Single hue by construction (`--brand`): multi-hue meshes are a banned trope (`design/purple-gradient`).
- **Measured fix:** fading toward the fold with `mask-image` on the container forced a re-rendered surface every frame (10% dropped frames). A static gradient overlay (`::after` to `--bg`) measured 0%. If the section background isn't `--bg`, set `--fx-aurora-fade`.
- **Tier:** T1, 0 KB. This is the page's **signature**, so nothing else animates in the hero.
- **Reduced motion:** the blobs hold still (static glow).
- **A11y / SSR:** `aria-hidden`, Server Component.

```tsx
import type { CSSProperties } from "react";

/** Server Component. Three pre-softened brand blobs drifting on the compositor (no filter: blur, no WebGL).
 *  Parent needs `relative isolate`. Masked toward the fold so it never sits behind body copy. */
export function AuroraBackdrop({ intensity = 0.2 }: { intensity?: number }) {
  return (
    <div data-fx="aurora" aria-hidden="true" className="fx-aurora" style={{ "--fx-aurora-opacity": intensity } as CSSProperties}>
      <span />
      <span />
      <span />
    </div>
  );
}
```

```css
/* ── 9. CSS aurora (T1, replaces WebGL Aurora/Silk) ──────────────────────────────────── */
@layer components {
  .fx-aurora {
    position: absolute;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
    contain: strict;
  }
  /* fade toward the fold so it never sits behind body copy. A static overlay, not mask-image:
     a mask over animated children forces a re-rendered surface every frame (measured 10% dropped frames). */
  .fx-aurora::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, transparent 35%, var(--fx-aurora-fade, var(--bg)) 92%);
  }
  .fx-aurora > span {
    position: absolute;
    width: 55vmax;
    aspect-ratio: 1;
    border-radius: 50%;
    opacity: var(--fx-aurora-opacity, 0.2);
    animation: fx-drift 32s var(--ease-in-out) infinite alternate;
  }
  .fx-aurora > span:nth-child(1) {
    top: -30vmax;
    left: -12vmax;
    background: radial-gradient(closest-side, var(--brand), color-mix(in oklab, var(--brand) 35%, transparent) 45%, transparent);
  }
  .fx-aurora > span:nth-child(2) {
    top: -26vmax;
    right: -20vmax;
    background: radial-gradient(closest-side, color-mix(in oklab, var(--brand) 55%, var(--fg)), transparent);
    animation-duration: 41s;
    animation-direction: alternate-reverse;
  }
  .fx-aurora > span:nth-child(3) {
    top: -10vmax;
    left: 30%;
    width: 40vmax;
    background: radial-gradient(closest-side, color-mix(in oklab, var(--brand) 45%, transparent), transparent);
    animation-duration: 27s;
    opacity: calc(var(--fx-aurora-opacity, 0.2) * 0.65);
  }
  @media (prefers-reduced-motion: reduce) {
    .fx-aurora > span { animation: none; }
  }
}
@keyframes fx-drift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(8vmax, 5vmax, 0) scale(1.12); }
}
```

```tsx
<section className="relative isolate overflow-hidden">
  <AuroraBackdrop intensity={0.2} />
  …hero…
</section>
```

Keep `intensity` ≤ 0.25 on dark directions, and check gutters stay near the canvas colour. If the brief truly needs a shader, follow `speed` → `references/rendering-smoothness.md` §8 (WebGL2 hook) and `motion` → `references/webgl-canvas.md`, with this component as the CSS fallback underneath.

## 10. HeroSpotlight (angled light behind the hero)

- **Inspired by:** Aceternity `spotlight` (https://ui.aceternity.com/components/spotlight).
- **Upstream bugs fixed:** `animate-spotlight` ships with no CSS in the registry item, so the SVG stays at `opacity-0` forever on Tailwind v4 (silently dead); hardcoded `id="filter"` collides across instances; a 151px `feGaussianBlur`; `z-[1]` sits over content. `spotlight-new` loops forever.
- **Technique:** one rotated elliptical radial gradient that fades in once (1.4s `--ease-out-expo`, 0.2s delay). `light-dark()` tints it with `--brand` in light schemes and `--fg` in dark ones.
- **Tier:** T1 one-shot, 0 KB. Starting at opacity 0 is safe because gradients are never LCP candidates.
- **Reduced motion:** shown immediately.
- **A11y / SSR:** `aria-hidden`, Server Component.

```tsx
import { cn } from "@/lib/cn";

/** Server Component, zero JS. A soft angled light (brand-tinted in light mode, fg-tinted in dark) that
 *  fades in once on load. No SVG filter, no ids, no infinite loop. Parent needs `relative isolate`. */
export function HeroSpotlight({ className }: { className?: string }) {
  return (
    <div aria-hidden="true" className={cn("pointer-events-none absolute inset-0 -z-10 overflow-hidden", className)}>
      <div className="fx-spotlight" />
    </div>
  );
}
```

```css
/* ── 10. Hero spotlight (T1 one-shot) ─────────────────────────────────────────────────── */
@layer components {
  .fx-spotlight {
    position: absolute;
    top: -45%;
    left: -25%;
    width: 110%;
    height: 130%;
    background: radial-gradient(
      ellipse 38% 14% at 50% 50%,
      light-dark(color-mix(in oklab, var(--brand) 16%, transparent), color-mix(in oklab, var(--fg) 16%, transparent)),
      transparent 70%
    );
    transform: rotate(32deg);
    animation: fx-spotlight-in 1.4s var(--ease-out-expo) 0.2s both; /* decorative, never LCP */
  }
  @media (prefers-reduced-motion: reduce) {
    .fx-spotlight { animation: none; }
  }
}
@keyframes fx-spotlight-in {
  from { opacity: 0; transform: translate(-6%, -5%) rotate(32deg) scale(0.96); }
  to { opacity: 1; transform: translate(0, 0) rotate(32deg) scale(1); }
}
```

---

## Verification log (2026-09-16)

- `next build` passed (TypeScript strict); every demo route is static (`○`). `void lint` reported 0 errors, and no findings in `src/components/fx` or `fx.css`.
- Server HTML with JavaScript disabled: the h1 text is complete, the ticker shows `12,400`, the marquee duplicate has `aria-hidden="true" inert`, and no inline `opacity:0`.
- Playwright (Chromium 149): SpotlightCard glow `translate: 120px 60px`, opacity 1 on hover; Magnetic `translate: 14.8px 5.5px` while tracking; the below-fold ticker counts 0 → 65.8 → 98.6 once, with the `sr-only` copy stable at 98.6; FxGate set `data-fx-paused` on the offscreen shine only; no console errors from fx code.
- Screenshots at 1440px (dark, light, `--force-prefers-reduced-motion`) and 390px: all effects visible and on-token. Reduced motion showed the static marquee (no mask), a static brand beam border, and still blobs.
- `void smooth` (desktop, scroll, headless software raster): each effect alone dropped 0–5% of frames. The full ten-effect page dropped 22–39% before the two fixes above (the shine's own layer and the aurora overlay instead of a mask) and 0% in two runs after them. The machine load average was 10–22, so repeat measurements varied by ±15 points; re-measure on your own route.
- `void a11y`: a marquee logo contrast failure with `text-fg-subtle` in the light theme, measured before `--fg-subtle` was raised to AA (fixed by using `text-fg-muted`, noted in §1).
