When Claude Code loads it
Use when adding animated or fancy UI effects, hero backgrounds (aurora, grid, dots, beams, spotlight, shader), text effects (split/blur/shiny/typewriter reveal), marquees and logo walls, bento or spotlight cards, border beams, number tickers/counters, magnetic buttons, or when the user mentions or pastes code from React Bits, reactbits.dev, skiper-ui, Magic UI, Aceternity, shadcn registries, `npx shadcn add @…`, 21st.dev, motion-primitives, Animate UI, Cult UI, Kokonut, Eldora, smoothui, uiverse, Hover.dev, coss/Origin UI, Tailark or kibo-ui. Also use when choosing Base UI vs Radix vs React Aria for dialogs, drawers, toasts and comboboxes.
Component libraries are catalogues of techniques, not dependencies. The output is always void-native: tokens only, a Server Component where possible, the lowest effect tier that carries the look, audited, and attributed.
Owner of: library sourcing, licences, registry audits, and signature-effect translations. For UI micro-interactions (press, dropdown, dialog, toast, accordion, tabs), timing and effect tiers, see motion. For WebGL and canvas hooks, see speed → references/rendering-smoothness.md §8 and motion → references/webgl-canvas.md. For rebuilding a site you admire, see inspire. For the visual system and anti-slop checks, see craft.
The 10 rules
- Check the licence before you read the code. MIT/Apache/Unlicense can be vendored with the notice kept. React Bits, Animate UI, Aceternity, Hover.dev and skiper-ui (free tier) are inspiration only in anything open-source: write a clean-room rewrite. React Bits' Commons Clause explicitly bans redistributing "a ported version".
- Effect budget: 1 signature + 1 secondary per page. A signature is a hero backdrop, a hero product animation, or one scroll set piece. A secondary is one marquee, one spotlight grid or one beam card. Masked patterns, one glow and grain are free. Zero decorative effects on authenticated app routes, and none on anything a user triggers 100+ times a day or from the keyboard.
- Behaviour comes from a headless library, visuals from tokens. Dialogs, drawers, menus, comboboxes, tabs and toasts come from Base UI / Radix / React Aria (+ sonner, cmdk). Never ship a registry's hand-rolled
divmodal. - Use a recipe before a registry. Check
references/recipes.mdfirst. Adapt registry source only when no recipe or library covers the effect, and only after the audit. - Never
npx shadcn addstraight into the app while evaluating. Fetch the JSON, readfiles[].content,dependenciesandcss/cssVars, and run the audit. Unaudited registry code does not ship. - Assume every registry component is broken until proven otherwise: no reduced motion (Magic UI 5/78, Aceternity 3/118, React Bits 3/56 backgrounds), no offscreen pause, hardcoded colours, v3-only Tailwind. Catalogue of failures:
references/upstream-bugs.md. - The server HTML carries the real content. Headlines, numbers and links are in the HTML and visible without JS. Never use
opacity:0on LCP text, and never let a counter SSR as0. - Compositor-first. Continuous motion uses only
transform/translate/opacity. Zero React renders per frame. Every loop pauses offscreen and has a reduced-motion finished frame. - One of each: one animation runtime (
motion, never alsoframer-motionor GSAP), one WebGL context per page, one primitive library per app. - Attribute. Vendored MIT code keeps its copyright header plus a
THIRD_PARTY_NOTICES.mdentry. A clean-room rewrite gets an "inspired by" comment. Never claim affiliation.
Decision tree
Need a component or effect?
├─ Behaviour (focus trap, menu, combobox, drawer, toast, ⌘K, date picker)?
│ → headless library + token styling:
│ Base UI (@base-ui/react 1.8: Dialog, Drawer, Toast, Combobox, OTPField) · Radix (radix-ui) ·
│ React Aria (i18n, dates, drag a11y) · sonner (toasts) · cmdk (⌘K, no open animation).
│ vaul is unmaintained → Base UI Drawer. Motion for these: `motion` recipes.
├─ Static structure (pricing table, bento, footer, auth form)?
│ → build from template primitives (Card, Section, Button). For layout ideas, read MIT sources
│ (shadcn blocks, Tailark GitHub, coss ui, opensourceui). No animation needed.
├─ Signature/secondary visual (marquee, spotlight, reveal, beam, aurora, ticker…)?
│ 1. GATE: frequency (first visit only), a purpose in one word, budget left on this page? No → T0 static.
│ 2. RECIPE: is it in references/recipes.md? → paste it. Done.
│ 3. LIBRARY: a real package that solves it? numbers → @number-flow/react · globe → cobe (T4) ·
│ text morph → torph · springs/layout/exit → motion (LazyMotion + m).
│ 4. REGISTRY (last resort): shortlist 2–4 by licence + tier → fetch JSON → 16-point audit →
│ MIT-class: vendor + fix every fail · anything else: write a spec, close the source, clean-room.
│ Tier too high for the budget (WebGL for a mood)? → down-tier (inspire → effect-downgrades).
└─ User pasted code from a library? → identify licence + component → run the audit → rewrite.Effect tiers (summary; the full rules are in motion §3)
| Tier | What | JS | Page budget |
|---|---|---|---|
| T0 static CSS | gradients, masks, grid/dots, grain, glow | 0 KB | free within taste: one glow, one grain, masked patterns |
| T1 CSS animated | keyframes, transitions, @starting-style, view() timelines |
0 KB | ≤3 infinite per viewport, ≤1 paint-bound |
| T2 JS / motion | pointer-driven, springs, counters | rAF 0.3–1 KB · LazyMotion +32 KB · full motion +46 KB | 1 hero signature + 1 secondary; ≤2 islands |
| T3 canvas 2D | particle/line fields | 2–12 KB | 1 viewport canvas; excludes T4 |
| T4 WebGL | shaders, globes, 3D | ogl 10–14 · cobe 5 · three 140+ KB | 1 context, marketing hero only |
Assign a tier from a registry item's dependencies: nothing or CSS → T1 · motion/framer-motion/gsap/rAF → T2 · getContext('2d') → T3 · ogl/three/@react-three/*/cobe/webgl → T4.
Licence matrix (verified 2026-09-16; not legal advice)
| Library | Licence | Install in an app | Copy into an OSS repo / template | Attribution |
|---|---|---|---|---|
| shadcn/ui, Magic UI, motion-primitives, Cult UI, kibo-ui, smoothui, Kokonut, Eldora, Luxe, RareUI, opensourceui, uiverse (galaxy) | MIT | yes | yes, keep notice | licence text in notices |
coss ui / Origin UI (coss.com/ui/r, coss.com/origin/r) |
MIT (repo default AGPL; apps/ui + apps/origin MIT) |
yes | registry components yes; never the AGPL app code | notice |
| Tailark | MIT on GitHub; hosted /r/ blocks paywalled (401) |
from GitHub | from GitHub | notice |
| Base UI, Radix, Ark UI, sonner, cmdk, vaul | MIT | depend | depend | — |
| React Aria | Apache-2.0 | depend | keep NOTICE | NOTICE |
| GSAP (all plugins, incl. SplitText) | free standard licence (no Webflow-like builders) | yes, but ~25–70 KB and a second runtime | depend only | — |
| ogl | Unlicense | yes | yes | — |
| React Bits | MIT + Commons Clause (no selling or redistribution, "or as a ported version") | yes, via reactbits.dev | no: clean-room only | courtesy |
| Animate UI | MIT + Commons Clause ("in their original form") | yes | clean-room | courtesy |
| Skiper UI free / Pro | free: use and modify with attribution required; Pro paid | free with its licence comment kept | clean-room (else every user inherits attribution) | required if used |
| Aceternity UI | proprietary terms: no redistribution of item or source | copy free items into the app | clean-room | courtesy |
| Hover.dev | proprietary | per purchase | nothing | — |
| 21st.dev | ToS 2026-07-20: use via the platform; per-author licences | per author's licence, via official MCP/CLI | design reference only | credit the author |
| shadcnblocks (paid), Skiper Pro, Aceternity Pro | commercial | buyers only, never in public repos | nothing | — |
Ideas and techniques are free to reuse; the source text is not. Details and per-library notes: references/catalogue.md.
Fetch registry source (read-only; nothing is written to the repo)
curl -sL https://magicui.design/r/marquee.json | jq -r '.files[].content' # source
curl -sL https://magicui.design/r/marquee.json | jq '{dependencies, registryDependencies, css, cssVars}'
npx shadcn@latest view @magicui/marquee # via the 344-namespace index
curl -sL https://reactbits.dev/r/Aurora-TS-TW.json | jq -r '.files[0].content' # <Name>-<TS|JS>-<TW|CSS>
curl -sL https://ui.aceternity.com/registry/spotlight.json | jq -r '.files[0].content'
curl -sL https://skiper-ui.com/registry/skiper40.json | jq -r '.files[0].content' # new path; /r/skiperN.json still 200
curl -sL https://coss.com/ui/r/button.json | jq -r '.files[0].content'
curl -sL https://ui.shadcn.com/r/registries.json | jq -r '.[] | select(.name=="@smoothui") | .url' # find any URL template
# 429 bot challenge (motion-primitives, Cult UI) → raw GitHub:
curl -sL https://raw.githubusercontent.com/ibelick/motion-primitives/main/public/c/magnetic.json | jq -r '.files[0].content'
curl -sL https://raw.githubusercontent.com/nolly-studio/cult-ui/main/apps/www/public/r/texture-card.json | jq -r '.files[0].content'
# no registry (opensourceui) → raw file:
curl -sL https://raw.githubusercontent.com/bidyut10/opensourceui/main/components/<cat>/<name>.tsx401/403/Missing license keymeans paid or authenticated. Stop. Don't look for mirrors.- RareUI slugs differ from its docs (
/components/fluidorb→/r/fluid-orb.json). - 21st.dev
/r/*returns 403 without auth. Use the official@21st-dev/magicMCP with the user's key, and treat results as reference. - Record the dependencies. A custom
animate-*class with nocss/cssVarsin the item means the animation is dead on Tailwind v4 (Aceternity ships none).
Audit (16 points; full grep hints in references/audit-checklist.md)
Each fail becomes a line in the rewrite plan.
- Licence: the class from the matrix plus the actual file header.
- SSR safety: no
window/document/navigator/matchMediain render or at module scope; noMath.random()/Date.now()in render (lint/browser-global-in-render). - Server HTML complete: headline, numbers and links present and visible; no
opacity-0/initial="hidden"on text. - Cleanup 1:1: every
addEventListener/observe/rAF/setInterval/.on(/new Lenis/createGlobehas a remove, disconnect, cancel, clear, off or destroy (lint/listener-without-cleanup,lint/raf-without-cancel,lint/timer-in-render). - Hooks: none inside conditionals or JSX; no
m.create()in render; noref.currentin deps; no MotionValue.onin render (lint/motionvalue-subscribe-in-render). - Zero per-frame React work: no
setStatein pointer/scroll/rAF handlers; no allocation (new Intl.NumberFormat,new Color) in loops. - Reduced motion: a finished static frame with the content intact (
lint/no-reduced-motion,smooth/reduced-motion-ignored). - Offscreen pause: infinite animations use
data-fxor IntersectionObserver; JS loops also stop onvisibilitychange(smooth/raf-loop-idle). - DPR cap ≤1.5 (1 for backdrops); full-bleed shader render scale ≤0.6.
- FPS cap ≤30 for ambient T3/T4.
- Properties: continuous animation only on transform/opacity; no
width/height/top/left, gradient orfilterloops (lint/animate-layout-prop,smooth/animate-layout-property). - A11y: decorative layers
aria-hidden; duplicatesaria-hidden+inert; real/(lint/div-button); hover has a focus equivalent; content moving >5s has a pause. - Hover gating:
(hover:hover) and (pointer:fine); pointer handlers checkpointerType. - Weight and duplicates: fits the tier; no
framer-motion+motion, no@base-ui-components/react+@base-ui/react, no three for a 2D quad, no face-api (lint/heavy-import). - Tokens: no hex/rgb/
neutral-800/purple-500; motion uses--ease-*/--duration-*(lint/hardcoded-colors,design/purple-gradient,design/gradient-text). - Tailwind v4: no
bg-gradient-to-*,flex-shrink-0,duration-[--x], config keyframes or undefinedanimate-*(lint/tw-v3-arbitrary-var,lint/tw-v3-renamed,lint/tw-unknown-class,lint/tw-js-config-ignored).
Rewrite onto void tokens
Clean-room procedure (required for non-MIT sources, recommended for all):
- Write a spec of 5–10 lines: what moves, which property, duration, trigger, and the reduced-motion frame.
- Close the source.
- Build at the lowest tier, reusing recipe patterns.
- Diff check: no identifiers, comments, magic numbers or file structure carried over.
| Registry code | void |
|---|---|
bg-black, bg-neutral-950, dark:bg-black |
bg-bg (page) · bg-surface / surface-card (cards) |
text-white, text-black dark:text-white · text-neutral-400 |
text-fg · text-fg-muted (text-fg-subtle for meta/captions/placeholders, AA 4.5:1; text-fg-faint only for disabled controls or aria-hidden decoration, 3:1, never readable text) |
border-neutral-800, border-white/10 |
border-line (border-line-strong on hover) |
#9c40ff → #ffaa40, from-purple-500 to-pink-500 |
one hue: var(--brand), color-mix(in oklab, var(--brand) 20%, transparent), --brand-line |
shadcn bg-background text-muted-foreground bg-primary bg-accent |
bg-bg text-fg-muted bg-brand bg-surface-hover, or import @void/tokens/shadcn.css (shadcn accent = hover surface, not brand) |
shadow-lg, rounded-3xl |
shadow-2 (default shadows are removed), rounded-xl |
transition-all duration-500 ease-in-out |
transition-[translate,opacity] duration-(--duration-slow) ease-out |
bg-gradient-to-r, flex-shrink-0, duration-[--x] |
bg-linear-to-r, shrink-0, duration-(--x) |
animate-spotlight with no CSS; tailwind.config keyframes |
@keyframes in plain CSS inside @layer components, or --animate-* + @keyframes in @theme |
import { motion } from "framer-motion" |
CSS first; else import * as m from "motion/react-m" + LazyMotion |
useState position on mousemove |
a ref + rAF writing el.style.translate (recipe 2) |
missing 'use client' (all React Bits files) |
add it only on the smallest interactive leaf; keep the rest server |
Recipes (references/recipes.md, all verified in a Next 16 build)
| Component | Replaces | Tier / JS |
|---|---|---|
Marquee |
Magic UI marquee, React Bits LogoLoop | T1 / 0 |
SpotlightCard |
React Bits SpotlightCard, Magic UI magic-card | T2 / ~0.5 KB |
RevealText |
React Bits SplitText/BlurText, Aceternity text-generate-effect | T1 / 0 |
BeamBorder |
Magic UI border-beam, React Bits StarBorder | T1 / 0 |
PatternBackdrop (+ grain) |
Magic UI grid/dot-pattern, Aceternity background-beams | T0 / 0 |
NumberTicker |
Magic UI number-ticker, React Bits CountUp | T2 / ~0.6 KB |
Magnetic |
React Bits Magnet, motion-primitives magnetic | T2 / ~0.5 KB |
ShinyText |
React Bits ShinyText, Magic UI animated-shiny-text | T1-paint / 0 |
AuroraBackdrop |
React Bits Aurora/Silk (WebGL) | T1 / 0 |
HeroSpotlight |
Aceternity spotlight | T1 one-shot / 0 |
Shared infrastructure: FxGate (mount once, pauses every [data-fx] offscreen) and src/styles/fx.css imported last in globals.css. Two measured traps: never put a paint animation (ShinyText) over an animated backdrop, and never fade an animated layer with mask-image (use a static overlay).
PatternBackdrop/AuroraBackdrop/HeroSpotlight above cover T0/T1 section backgrounds. For the rest of the "React Bits Backgrounds" vocabulary the owner points to (liquid/fluid distortion, particle field, pixel/dither grid, displacement grid) — a measured decision table by cost tier, the cheap-first CSS/SVG ladder to try before any of them, the one-shared-WebGL-context pattern for a page with several background sections, and paste-ready clean-room OGL implementations: motion/references/backgrounds.md. Same rule 1 applies: these are the idea, rewritten from scratch — never a ported React Bits/Animate UI file.
Attribution
/**
* Adapted from Magic UI "marquee": https://magicui.design/docs/components/marquee
* Copyright (c) Magic UI. MIT License (see THIRD_PARTY_NOTICES.md).
* Changes: CSS-only, aria-hidden/inert duplicate, offscreen pause, reduced motion, void tokens.
*//**
* Visual idea inspired by React Bits "SpotlightCard" (https://reactbits.dev).
* Independent implementation; no source code copied.
*/- Vendored MIT: keep the header, and append the full licence text to
THIRD_PARTY_NOTICES.md(create it at the repo root). - Skiper free code actually used: keep its entire trailing licence comment. Prefer clean-room.
- 21st.dev: credit the component author and follow that author's licence.
Verify
void lint: 0 errors, and nolint/hardcoded-colors,lint/no-reduced-motion,lint/raf-without-cancelorlint/tw-*in the new files.next build; thencurl -s localhost:3000/ | grep '12,400'(or the real headline and number) proves server HTML.void smooth --start "next start -p 3000" --port 3000: dropped frames ≤5% while scrolling, and the WebGL context count ≤1.- Screenshot dark, light,
--force-prefers-reduced-motionand 390px (seeaudit); every effect shows a finished, on-token frame.
References
references/recipes.md: the 10 components plus FxGate and fx.css, paste-ready, with a verification log.references/catalogue.md: 30+ libraries by best use, licence, registry URL, deps, a11y/RM quality and maintenance.references/audit-checklist.md: the 16 points with grep/AST commands and pass criteria.references/upstream-bugs.md: 48 known bugs grouped by pattern, with fix patterns.