# Exploded UI: Design System

> A page lifts off the components it's built from, which lift off the tokens under them. Numbered labels, with the active layer's description under the stack.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/exploded-ui-design-system-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/exploded-ui-design-system-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/exploded-ui-design-system-demo-light-c85f41fda8b2f457.png
- Registry item: https://keyframeui.com/r/exploded-ui-design-system-demo.json
- Categories: scroll, 3d, showcase
- Listed: 2026-10-14, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/exploded-ui-design-system-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/exploded-ui.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/exploded-ui#exploded-ui-design-system-demo

## Files

### `registry/demos/exploded-ui-design-system-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import { ExplodedUI } from "@/components/ui/exploded-ui";

// Sized off the plane's width, so each layer scales like an image.
const screen =
  "[--spacing:0.4cqw] [--text-xs:1.2cqw] [--text-sm:1.4cqw] [--text-base:1.6cqw] [--text-lg:1.9cqw] [--text-2xl:2.6cqw] [--text-4xl:4cqw] [--radius-md:0.6cqw] [--radius-lg:0.8cqw] [--radius-xl:1.2cqw]";

// The page on top is built from the components under it, which are built from
// the tokens at the bottom: the same three colors run through all of them.
export function ExplodedUIDesignSystemDemo() {
  const scrollRef = React.useRef<HTMLDivElement>(null);

  return (
    <div className="relative w-full overflow-hidden rounded-xl border border-zinc-200 bg-stone-100 text-zinc-950 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50">
      <div ref={scrollRef} className="relative h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain">
        <header className="mx-auto max-w-2xl px-6 pt-16 pb-4 text-center sm:pt-20">
          <p className="text-sm font-medium text-orange-600 dark:text-orange-400">Fieldwork Design System</p>
          <h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
            Every page starts as three decisions
          </h2>
        </header>

        <ExplodedUI
          walkthrough
          labels="numbered"
          spread={0.16}
          containerRef={scrollRef}
          stageClassName="h-[var(--demo-height,36rem)]"
          className={cn("aspect-[16/10] w-full max-w-2xl", screen)}
        >
          <ExplodedUI.Layer label="Tokens" description="Color, type and spacing, decided once.">
            <Tokens />
          </ExplodedUI.Layer>
          <ExplodedUI.Layer
            label="Components"
            description="Buttons, fields and toggles built from the tokens."
            className="inset-[7%]"
          >
            <Components />
          </ExplodedUI.Layer>
          <ExplodedUI.Layer label="Pages" description="Composed from components. Nothing one-off.">
            <Page />
          </ExplodedUI.Layer>
        </ExplodedUI>

        <section className="mx-auto max-w-xl px-6 py-16 text-center text-zinc-600 dark:text-zinc-400">
          120 components, 2 themes, 0 hex codes in product code.
        </section>
      </div>
    </div>
  );
}

const palette = [
  ["Ink", ["bg-zinc-50", "bg-zinc-200", "bg-zinc-400", "bg-zinc-700", "bg-zinc-950"]],
  ["Clay", ["bg-orange-50", "bg-orange-200", "bg-orange-400", "bg-orange-600", "bg-orange-800"]],
  ["Moss", ["bg-lime-50", "bg-lime-200", "bg-lime-400", "bg-lime-600", "bg-lime-800"]],
] as const;

function Tokens() {
  return (
    <div className="grid size-full grid-cols-[1.3fr_1fr] gap-6 bg-white p-6 text-xs text-zinc-500 dark:bg-zinc-900 dark:text-zinc-400">
      <div className="flex flex-col gap-4">
        <span className="font-mono">color</span>
        {palette.map(([name, swatches]) => (
          <div key={name} className="flex items-center gap-3">
            <span className="w-10">{name}</span>
            <div className="flex flex-1 gap-1.5">
              {swatches.map((swatch) => (
                <span key={swatch} className={cn("aspect-square flex-1 rounded-md ring-1 ring-zinc-950/5", swatch)} />
              ))}
            </div>
          </div>
        ))}
      </div>
      <div className="flex flex-col gap-3">
        <span className="font-mono">type</span>
        <span className="text-4xl leading-none font-semibold tracking-tight text-zinc-950 dark:text-zinc-50">Aa</span>
        {[
          ["display", "40 / 44"],
          ["title", "24 / 30"],
          ["body", "16 / 24"],
        ].map(([name, size]) => (
          <span key={name} className="flex justify-between border-b border-zinc-950/5 pb-1.5 dark:border-white/10">
            {name}
            <span className="font-mono">{size}</span>
          </span>
        ))}
        <span className="mt-2 font-mono">space</span>
        <div className="flex items-end gap-1.5">
          {[1, 2, 3, 4, 6, 8].map((n) => (
            <span key={n} className="rounded-sm bg-orange-400/70" style={{ width: `${n * 0.5}cqw`, height: `${n * 0.5}cqw` }} />
          ))}
        </div>
      </div>
    </div>
  );
}

function Components() {
  return (
    <div className="grid size-full grid-cols-2 content-center gap-x-8 gap-y-6 bg-stone-50 p-8 text-xs dark:bg-zinc-800">
      <div className="flex flex-wrap items-center gap-2">
        <span className="rounded-lg bg-zinc-950 px-4 py-2 font-medium text-white dark:bg-white dark:text-zinc-950">Primary</span>
        <span className="rounded-lg px-4 py-2 font-medium ring-1 ring-zinc-950/15 ring-inset dark:ring-white/20">Secondary</span>
        <span className="rounded-lg bg-orange-600 px-4 py-2 font-medium text-white">Accent</span>
      </div>
      <div className="flex items-center gap-3">
        <span className="flex h-6 w-11 items-center rounded-full bg-lime-600 p-0.5">
          <span className="ml-auto size-5 rounded-full bg-white shadow-sm" />
        </span>
        <span className="flex size-5 items-center justify-center rounded-md bg-zinc-950 text-white dark:bg-white dark:text-zinc-950">
          <svg viewBox="0 0 12 12" aria-hidden className="size-3 fill-none stroke-current" strokeWidth="2">
            <path d="m2.5 6.5 2.5 2.5 4.5-6" />
          </svg>
        </span>
        <span className="rounded-full bg-lime-100 px-2.5 py-1 font-medium text-lime-800 dark:bg-lime-900/50 dark:text-lime-200">Shipped</span>
      </div>
      <div className="flex flex-col gap-1.5">
        <span className="font-medium">Email</span>
        <span className="rounded-lg bg-white px-3 py-2 text-zinc-400 ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:ring-white/10">you@fieldwork.co</span>
      </div>
      <div className="flex -space-x-2">
        {["bg-orange-300", "bg-lime-300", "bg-zinc-300", "bg-orange-500"].map((color) => (
          <span key={color} className={cn("size-8 rounded-full ring-2 ring-stone-50 dark:ring-zinc-800", color)} />
        ))}
      </div>
    </div>
  );
}

function Page() {
  return (
    <div className="flex size-full flex-col bg-orange-50 p-6 text-xs dark:bg-zinc-900">
      <div className="flex items-center justify-between">
        <span className="flex items-center gap-2 text-sm font-semibold">
          <span className="size-5 rounded-full bg-orange-600" />
          Fieldwork
        </span>
        <div className="flex items-center gap-5 text-zinc-600 dark:text-zinc-400">
          <span>Product</span>
          <span>Pricing</span>
          <span className="rounded-lg bg-zinc-950 px-3 py-1.5 font-medium text-white dark:bg-white dark:text-zinc-950">Sign up</span>
        </div>
      </div>
      <div className="mt-[8%] grid flex-1 grid-cols-[1.2fr_1fr] gap-6">
        <div>
          <span className="rounded-full bg-lime-100 px-2.5 py-1 font-medium text-lime-800 dark:bg-lime-900/50 dark:text-lime-200">Shipped: v4</span>
          <p className="mt-4 text-4xl leading-[1.05] font-semibold tracking-tight text-balance">Field notes for teams that build</p>
          <div className="mt-6 flex gap-2">
            <span className="rounded-lg bg-orange-600 px-4 py-2 font-medium text-white">Start free</span>
            <span className="rounded-lg px-4 py-2 font-medium ring-1 ring-zinc-950/15 ring-inset dark:ring-white/20">Book a demo</span>
          </div>
        </div>
        <div className="flex flex-col gap-3 self-start rounded-xl bg-white p-5 shadow-xl ring-1 shadow-orange-950/10 ring-zinc-950/5 dark:bg-zinc-800 dark:ring-white/10">
          <span className="font-medium">Join the beta</span>
          <span className="rounded-lg bg-white px-3 py-2 text-zinc-400 ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:ring-white/10">you@fieldwork.co</span>
          <span className="flex items-center gap-2 text-zinc-500">
            <span className="flex h-5 w-9 items-center rounded-full bg-lime-600 p-0.5">
              <span className="ml-auto size-4 rounded-full bg-white" />
            </span>
            Weekly digest
          </span>
          <span className="mt-2 rounded-lg bg-zinc-950 py-2 text-center font-medium text-white dark:bg-white dark:text-zinc-950">Request access</span>
        </div>
      </div>
    </div>
  );
}
```
