Self-contained, drop-in design system extracted from the Stargazer Outreach landing page. Purple base + cyan accent, animated dither + star-field background, display/mono type, and the App Router shell pattern that ties it together. Copy these files into any Next.js (App Router) + Tailwind v4 service and re-skin via tokens.
Source library — no build step. Clone, copy the files you want into your
src/, install the deps below, wire the 3 steps.
mcp-design-system/
tokens.css # @theme tokens + global resets (→ becomes app/globals.css)
layout-shell.tsx # drop-in app/layout.tsx: fonts + bg layers + nav slot
page-shell.tsx # per-page <main> wrapper (relative z-10 pattern)
components/
dither-bg.tsx # animated dithered background (z-0), color-parametrized
star-field.tsx # canvas star field (z-0), density/color-parametrized
nav.tsx # generic top nav: brand + links[] + action slot, no auth
button.tsx # primary / ghost / outline × sm / md / lg
animated-number.tsx # framer-motion tweened counter
lib/utils.ts # cn() — clsx + tailwind-merge
config/
postcss.config.mjs # Tailwind v4 postcss plugin
tsconfig.paths.json # optional @/ alias
package.json # deps (peer: next/react)
README.md
pnpm add @paper-design/shaders-react framer-motion clsx tailwind-merge
pnpm add -D tailwindcss @tailwindcss/postcssPeer (already present in any Next app): next, react, react-dom. Fonts
(Geist, Geist Mono, Major Mono Display) come from next/font/google — no
package needed.
| Dep | Used by |
|---|---|
@paper-design/shaders-react |
dither-bg.tsx |
framer-motion |
animated-number.tsx |
clsx + tailwind-merge |
lib/utils.ts → button.tsx |
tailwindcss v4 + @tailwindcss/postcss |
tokens / all utilities |
- Tokens → globals. Copy
tokens.csstosrc/app/globals.css. It already@import "tailwindcss", so it replaces the default globals entirely. - PostCSS. Copy
config/postcss.config.mjsto your project root. Tailwind v4 needs notailwind.config.js— the theme lives in the@themeblock. - Layout. Use
layout-shell.tsxas yoursrc/app/layout.tsx(fix the./tokens.cssimport to./globals.cssand adjust component import paths). It mounts<DitherBg />+<StarField />(fixed, z-0) and<Nav />(z-20), then renders{children}. Each page wraps content in thepage-shell.tsx<main className="relative z-10 ...">so it floats above the background.
That's the whole routing pattern: one layout owns the chrome + background,
every route is just a page.tsx that renders a relative z-10 main.
All exposed as Tailwind utilities via --color-* → bg-(--color-x),
text-(--color-x), border-(--color-x).
| Token | Value | Use |
|---|---|---|
--color-bg |
#150825 |
page background (keep = DitherBg colorBack) |
--color-bg-elev |
#1f0e35 |
elevated surfaces |
--color-fg |
#f4eeff |
body text (16:1) |
--color-fg-muted |
#b8aedb |
secondary (8.1:1) |
--color-fg-faint |
#8a7eb8 |
tertiary labels (4.6:1, still AA) |
--color-accent |
#5eead4 |
primary accent / buttons |
--color-accent-hi |
#99f6e4 |
accent hover / selection text |
--color-accent-soft |
rgba(94,234,212,.16) |
selection bg, soft fills |
--color-orange / --color-red |
#fb923c / #fb7185 |
state |
--color-border / -strong |
white @ 10% / 20% | hairlines |
--color-card / -hover |
white @ 4% / 7% | card surfaces |
Type: --font-sans (Geist) · --font-mono (Geist Mono) · --font-display
(Major Mono Display — the spaced brand wordmark). Easing: --ease-out,
--ease-in-out, --ease-drawer on :root.
Globals in tokens.css also wire: ::selection, a universal :active
scale(0.97) on buttons, 160ms transitions on button/link/input, .tabular
numerals, and a prefers-reduced-motion kill-switch.
<Nav brand links={[]} action />—brandis a string (rendered in the display font) or any node;links: {href,label}[];actionis the right-side slot (sign-in button, avatar menu, or omit). No session coupling.<DitherBg colorBack colorFront opacity />— fixed z-0. Defaults match the purple palette. Reduced-motion → static.<StarField density color />— fixed z-0 canvas over the dither.coloris an"r, g, b"string. Reduced-motion → no drift/twinkle.<Button variant size />—variant:primary | ghost | outline,size:sm | md | lg.:activescale is global.<AnimatedNumber value format duration />— tweens between integers, tabular-nums for stable width.
- Edit the
@themevalues intokens.css(keep the token names). - Set
<DitherBg colorBack={--color-bg} colorFront={...} />to match, and optionally<StarField color="r, g, b" />. - Swap the
<Nav brand>and metadata in the layout.
Foreground colors are tuned for WCAG AA against --color-bg — re-check
contrast if you change the base hue.
- z-index contract: background layers =
z-0(fixed), page<main>=relative z-10, nav =z-20. Anything above the background needsrelative z-10or it renders behind the dither. "use client":dither-bg,star-field,animated-numberare client components (canvas / shaders / motion).navandbuttonare server-safe.- Tailwind v4 arbitrary-property syntax:
text-(--color-fg)is v4, not the v3text-[var(--color-fg)]. Don't "modernize" it back. - Import paths: files here use relative imports (
../lib/utils). If you prefer@/, mergeconfig/tsconfig.paths.jsonand rewrite imports.