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.
- 01Sourcesrc/blocks/
- 02SchemaPages/index.ts
- 03RendererRenderBlocks.tsx
- 04Typespayload-types.ts
- 05Import mapimportMap.js
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.
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.
- 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 of
Receiptpayload-components/registry.jsonan interactive--variantprompt - D·02Registry
Family code ships as files
Code a family shares, like
heroFields.ts, is a real source file that every variant lists infiles[]and installs once tosrc/blocks/shared/. Edit it and every installed variant follows; re-running an install never overwrites your copy.Instead of
Receiptpayload-components/source/blocks/shared/heroFields.tsregistryDependencies, which resolve only public shadcn UI, so an internal name would 404 - 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 of
Receipttools/payload-components/project.tsguessing at a file shape it does not recognise - D·04CLI
Two runtime dependencies
The published CLI ships
ajvandsemverand nothing else, sonpx payload-componentsnever 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.Instead of
Receipttools/payload-components/mcp/server.tsan MCP SDK - 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 of
Receipttools/payload-components/commands/seed.tselevated Local API calls hidden inside the installer - 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 of
Receiptpayload-components/install-baselines.jsonediting a released version in place - 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 of
Receipttests/int/payload-components-source-compiles.int.spec.tstrusting review for code no gate had compiled - 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 of
Receipttests/int/demo-twins.int.spec.tsrunning consumer block code inside the docs site - 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 of
Receiptsrc/app/globals.cssa dark-mode toggle - 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 of
Receiptsrc/components/site/Logomark.tsxthe>prompt-and-cursor mark it replaced - 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 of
Receiptsrc/i18n/config.tspublishing machine translations as if they were reviewed
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
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.
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.
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.
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.