Skip to content

Reference · components

Signature effect recipes (library effect → lean void component)

components/references/recipes.md853 linesupdated 16 Sept 2026

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/.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 once in src/app/layout.tsx, inside , after .

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 speedPauseOffscreen 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
    wrapping a
      . 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 motionreferences/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);
    • 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 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)

    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 speedreferences/rendering-smoothness.md §8 (WebGL2 hook) and motionreferences/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).