Principles and decisions

Design philosophy

The thesis, principles, and decisions behind Payload Components — why it installs, reads, and looks the way it does, with a receipt for each.

  1. 01Source
  2. 02Schema
  3. 03Renderer
  4. 04Types
  5. 05Import map

Plate i, the keying sequence. A Payload block is live once five artifacts exist; a copy lands the first and leaves you the other four. The last stage is the logomark itself on its 24-unit grid: two 8.4-unit blocks, offset by 6, overlapping by 2.4.

Payload Components is a registry and a CLI, and underneath both it is a set of opinions about what should happen when a tool edits your repository. This page writes them down: the thesis they start from, the principles that follow, the decisions those produced, and the visual language that carries them. When a new component, command, or page is in doubt, this is the page to argue with.

The thesis

A Payload block is not installed when its files land. It is installed when your Pages collection registers it, RenderBlocks maps it, and the generated types and the admin import map both know it exists. That is five artifacts. A copy delivers the first and leaves you the other four.

The paste was never the problem. The edits after it were — and proving them, every single time, was worse.

From About

So the project makes one promise and arranges everything around keeping it: finish the wiring, show every edit before it lands, prove it once for everyone, and never ask for trust it hasn't earned. The rest of this page is that promise, broken into the choices that keep it.

Six principles

Wired, not pasted

Copying files is where an install starts, not where it ends. payload-components add registers the block, maps the renderer, and regenerates the types and import map in the same pass, so the block is live when the command finishes.

Source you own

Components land as plain TypeScript in your src/, not as a runtime package. Nothing upgrades behind your back: diff shows where your copy drifted from what shipped, and update refuses to overwrite local edits unless you pass --force.

One reviewable diff

Every install is a set of edits you can read before you trust them. --dry-run prints the plan without writing, the result lands as an ordinary git diff, and install state in .payload-components/state.json makes each stage safe to retry.

Proven once, for everyone

Wiring by hand means re-proving it by hand on every project. Here the proof lives in the registry — installer suites, compile checks of shipped code, and a fresh-repo smoke test — so each install inherits the evidence instead of redoing it.

Choose by looking

A structural variant is its own component — hero-basic, hero-video, hero-split — chosen in the catalog by how it looks, never through a CLI prompt. Anything an editor might switch on or off is a Payload field, not another component.

Receipts over claims

If it can't be checked, it doesn't go on the site. Copy links to the source, the manifest, or the test. The registry, the CLI, every component, and this site are one MIT repository: no pricing, no license keys, no gated tier.

Decisions, with receipts

Principles are cheap until they cost something. Each decision below names what it replaced and links to the file that enforces it, so you can check it rather than take it on trust. Architecture shows how the pieces fit together.

  1. D·01Registry

    One variant, one registry item

    Every structural variant is its own registry item and manifest, and every name carries a suffix — there is no bare hero. The install pipeline stays keyed on one stable name, which keeps install state, retries, and recovery simple.

    Instead ofan interactive --variant prompt

    Receiptpayload-components/registry.json
  2. D·02Registry

    Family code ships as files

    Code a family shares, like heroFields.ts, is a real source file that every variant lists in files[] and installs once to src/blocks/shared/. Edit it and every installed variant follows; re-running an install never overwrites your copy.

    Instead ofregistryDependencies, which resolve only public shadcn UI, so an internal name would 404

    Receiptpayload-components/source/blocks/shared/heroFields.ts
  3. D·03CLI

    Patch by anchor, refuse when unsure

    Fragments are placed by finding text anchors such as const blockComponents = {, with a duplicate check before every insert. When an anchor is missing, the file is left unchanged and the CLI prints the exact lines it would have written, so the fallback is a paste, not a research task.

    Instead ofguessing at a file shape it does not recognise

    Receipttools/payload-components/project.ts
  4. D·04CLI

    Two runtime dependencies

    The published CLI ships ajv and semver and nothing else, so npx payload-components never drags a framework into your install. Its MCP server for coding agents is hand-rolled JSON-RPC over stdio, and every tool it exposes is read-only.

  5. D·05CLI

    The CLI never opens your database

    Demo seeding writes a script you review and run yourself with your project's Payload CLI. It requires drafts, records the exact documents it owns, and never deletes media, so every elevated call sits in code you can read.

    Instead ofelevated Local API calls hidden inside the installer

    Receipttools/payload-components/commands/seed.ts
  6. D·06Registry

    Released source is immutable

    Changing a component's source means a version bump and changelog entry for every affected manifest, plus an appended source baseline. Published baselines are never rewritten, so the CLI can tell an upstream change from a local edit — and when it can't, it fails closed.

    Instead ofediting a released version in place

    Receiptpayload-components/install-baselines.json
  7. D·07Site

    Shipped code is compiled, not just read

    Component source is excluded from this repository's type-check, which is how v1.3.0 shipped a starter base that could not compile. A fast gate now parses every shipped starter file, applies real manifest fragments, and typechecks the result in the normal gate.

    Instead oftrusting review for code no gate had compiled

    Receipttests/int/payload-components-source-compiles.int.spec.ts
  8. D·08Site

    Twins, not runtimes

    This site runs no Payload: no admin, no database, no PAYLOAD_SECRET. Catalog previews are demo twins — backend-free specimens that must repeat every class sequence of the component they stand in for, checked by test.

    Instead ofrunning consumer block code inside the docs site

    Receipttests/int/demo-twins.int.spec.ts
  9. D·09Brand

    Forced light, one dark surface

    The site ships a single light theme with no toggle. The terminal is the one permanent dark surface, painted from its own tokens, so it reads as the product sitting on the page rather than as a second theme.

    Instead ofa dark-mode toggle

    Receiptsrc/app/globals.css
  10. D·10Brand

    Two keyed blocks

    The mark is two equal squares overlapping on the diagonal: separate blocks fitted into one shape, which is what the CLI does. The union has no seam to lose, so the silhouette survives a 16px browser tab.

    Instead ofthe > prompt-and-cursor mark it replaced

    Receiptsrc/components/site/Logomark.tsx
  11. D·11Site

    English is canonical

    The public site is English-only. Translation catalogs stay drafts until a native reviewer signs off on a path, because an honest English page is better than a confident wrong one.

    Instead ofpublishing machine translations as if they were reviewed

    Receiptsrc/i18n/config.ts

Visual language

The site is a workbench, not a showroom: white paper, zinc hairlines, black ink, and one emerald accent that appears only when something means yes — active, installed, linked. The brand guide holds the full token sheet; these are the rules behind it.

Color

Monochrome carries the structure and emerald carries meaning, so color never needs decoding. When the accessibility gate flagged muted text as short of AA, --muted-foreground was darkened to 46% lightness rather than excused.

  • PaperAlmost every surface--background
  • QuietTonal bands and code wells--muted
  • HairlineEvery divider on this page--border
  • Second ink46% lightness, AA on white--muted-foreground
  • InkHeadlines and primary actions--foreground
  • The accentActive, installed, linked--brand
plate ii — homage to one accent

Type

Geist Sans does the work. Geist Mono holds anything you might copy — commands, paths, versions — so it looks copyable. Instrument Serif appears as one italic word per heading and never a paragraph: a single warm note in a technical voice.

Geist SansProse, headings, controls
Geist MonoAnything you might copy
Instrument SerifOne word per heading
plate iii — three faces, one voice

Shape

Radii step down as surfaces nest — frame, panel, card, inset, base — so a box inside a box reads as contained rather than stacked. Pills are kept for status and metadata, and nothing gets an arbitrary radius when a named one exists.

frame · 2rem
panel · 1.5rem
card · 1.25rem
inset · 1rem
base · 0.625rem
plate iv — containment, outside in

Motion

Motion confirms; it never gates. Transitions run about 200 to 300ms, entrances play once, and under reduced motion the final state is simply there — the install replay still shows its whole transcript.

plate v — out fast, settle long

What we refuse

A system is defined as much by what it leaves out. These stay out.

  • Gradient SaaS theatre — color means something here, or it is not used.
  • Pricing tiers, license keys, waitlists — MIT end to end; success is adoption and pull requests.
  • Fake logos and invented quotes — nothing claims a user, a logo, or a quote it cannot point to.
  • Flags for languages — a language is not a country, so languages are named, never flagged.
  • A second accent color — emerald is the only hue; everything else is zinc.
  • Ornamental dashboards — a panel earns its place by showing something the product really does.

Colophon

Set in Geist Sans and Geist Mono, with Instrument Serif for one word per heading. Every plate on this page is inline SVG or plain HTML, drawn from two primitives — the logomark's rounded square and a hairline — and painted with the site's live tokens, so it changes when they do.

The same story, told in pictures, is the design case study.

Disagree with something? That is why it is written down. Open an issue or a pull request against content/docs/design.mdx; decisions are meant to be argued with.