---
title: "components skill: take the idea, not the code"
description: "Source animated and \"fancy\" UI from component libraries safely, then ship it as lean void code."
canonical: https://void-design.vercel.app/docs/components
lastModified: 2026-09-16
---

# components — take the idea, not the code

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

1. **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".
2. **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.
3. **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 `div` modal.
4. **Use a recipe before a registry.** Check `references/recipes.md` first. Adapt registry source only when no recipe or library covers the effect, and only after the audit.
5. **Never `npx shadcn add` straight into the app while evaluating.** Fetch the JSON, read `files[].content`, `dependencies` and `css`/`cssVars`, and run the audit. Unaudited registry code does not ship.
6. **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`.
7. **The server HTML carries the real content.** Headlines, numbers and links are in the HTML and visible without JS. Never use `opacity:0` on LCP text, and never let a counter SSR as `0`.
8. **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.
9. **One of each:** one animation runtime (`motion`, never also `framer-motion` or GSAP), one WebGL context per page, one primitive library per app.
10. **Attribute.** Vendored MIT code keeps its copyright header plus a `THIRD_PARTY_NOTICES.md` entry. 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)

```bash
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>.tsx
```

- `401`/`403`/`Missing license key` means 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/magic` MCP with the user's key, and treat results as reference.
- Record the dependencies. A custom `animate-*` class with no `css`/`cssVars` in 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.

1. **Licence:** the class from the matrix plus the actual file header.
2. **SSR safety:** no `window`/`document`/`navigator`/`matchMedia` in render or at module scope; no `Math.random()`/`Date.now()` in render (`lint/browser-global-in-render`).
3. **Server HTML complete:** headline, numbers and links present and visible; no `opacity-0`/`initial="hidden"` on text.
4. **Cleanup 1:1:** every `addEventListener`/`observe`/rAF/`setInterval`/`.on(`/`new Lenis`/`createGlobe` has a remove, disconnect, cancel, clear, off or destroy (`lint/listener-without-cleanup`, `lint/raf-without-cancel`, `lint/timer-in-render`).
5. **Hooks:** none inside conditionals or JSX; no `m.create()` in render; no `ref.current` in deps; no MotionValue `.on` in render (`lint/motionvalue-subscribe-in-render`).
6. **Zero per-frame React work:** no `setState` in pointer/scroll/rAF handlers; no allocation (`new Intl.NumberFormat`, `new Color`) in loops.
7. **Reduced motion:** a finished static frame with the content intact (`lint/no-reduced-motion`, `smooth/reduced-motion-ignored`).
8. **Offscreen pause:** infinite animations use `data-fx` or IntersectionObserver; JS loops also stop on `visibilitychange` (`smooth/raf-loop-idle`).
9. **DPR cap** ≤1.5 (1 for backdrops); full-bleed shader render scale ≤0.6.
10. **FPS cap** ≤30 for ambient T3/T4.
11. **Properties:** continuous animation only on transform/opacity; no `width/height/top/left`, gradient or `filter` loops (`lint/animate-layout-prop`, `smooth/animate-layout-property`).
12. **A11y:** decorative layers `aria-hidden`; duplicates `aria-hidden` + `inert`; real `<a>`/`<button type>` (`lint/div-button`); hover has a focus equivalent; content moving >5s has a pause.
13. **Hover gating:** `(hover:hover) and (pointer:fine)`; pointer handlers check `pointerType`.
14. **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`).
15. **Tokens:** no hex/rgb/`neutral-800`/`purple-500`; motion uses `--ease-*`/`--duration-*` (`lint/hardcoded-colors`, `design/purple-gradient`, `design/gradient-text`).
16. **Tailwind v4:** no `bg-gradient-to-*`, `flex-shrink-0`, `duration-[--x]`, config keyframes or undefined `animate-*` (`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):
1. Write a spec of 5–10 lines: what moves, which property, duration, trigger, and the reduced-motion frame.
2. Close the source.
3. Build at the lowest tier, reusing recipe patterns.
4. 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

```ts
/**
 * 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.
 */
```

```ts
/**
 * 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

1. `void lint`: 0 errors, and no `lint/hardcoded-colors`, `lint/no-reduced-motion`, `lint/raf-without-cancel` or `lint/tw-*` in the new files.
2. `next build`; then `curl -s localhost:3000/ | grep '12,400'` (or the real headline and number) proves server HTML.
3. `void smooth --start "next start -p 3000" --port 3000`: dropped frames ≤5% while scrolling, and the WebGL context count ≤1.
4. Screenshot dark, light, `--force-prefers-reduced-motion` and 390px (see `audit`); 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.
