# Design decisions

This is the standard's **decision record**: *why* it's built the way it is, and the alternatives that were tried and dropped. The full reasoning lives in context — in a **Why** aside on the page each decision affects — so this page is the map, not the territory. Each entry links to where the thinking lives.

:::note[Why keep this at all?]
A standard's *shape* is easy to read off the [schema](https://standard.mode.place/reference/schema/) and [contract](https://standard.mode.place/contract/); its *reasons* are not. Recording the reasons — especially the roads not taken — is what stops a settled question from being relitigated, and what lets someone extending the standard tell a load-bearing choice from an incidental one. This page replaces the project's old architecture-decision record; the site is now the single source of truth.
:::

## The premise

- **Intent-based, user-owned, visitor-first.** A look is described by the *job* a value does (`the background of a primary action`), saved to *your* account, and — where you've expressed one — rendered *your* way over the app's. Rejected: app-owned styling (the status quo, where your preference stays behind), and a flat color palette (the nearest prior art, [standard.site](https://standard.site), ships four color fields — a palette, not a system that can tell an action from a warning from a surface). → [What is a mode?](https://standard.mode.place/what-is-a-mode/)

## The data model

- **The expression is the record key; themes are self-contained.** Identity lives on the record key, not in an `expresses` field, because only the key enforces "one theme per concept per repo" — the protocol can't check a cross-record constraint. Rejected: an `expresses` string/array (no uniqueness), a two-record *values + binding* normalization (extra write, deletion dangles the group), and a same-collection *symlink* (cycles, a privileged record). → [Schema › the record key](https://standard.mode.place/reference/schema/#placemodestandardtheme)
- **Reuse is the owner's tooling, not the protocol.** A look answering to several names is several self-contained copies, kept in sync by the author's build step — the network stores compiled artifacts, like published CSS, not a normalized database. → [Schema › the record key](https://standard.mode.place/reference/schema/#placemodestandardtheme)
- **Per-aspect completeness.** Aspects are optional, but each present aspect is complete. Rejected: requiring all 33 (forbids color-only/type-only) and partial aspects (forces a per-intent merge engine). → [Schema › per-aspect completeness](https://standard.mode.place/reference/schema/#placemodestandardtheme)
- **An `aspects` container, not top-level `color`/`typography`.** So a renderer emits everything under it as variables without guessing style from metadata. → [Schema › aspects](https://standard.mode.place/reference/schema/#aspects)
- **Fonts are theme-level assets.** They render to document-global `@font-face`, not scoped variables, so they sit beside `aspects`, not inside typography. → [Schema › typographyAspect](https://standard.mode.place/reference/schema/#typographyaspect)
- **Values are plain CSS strings.** Format-agnostic, no transform layer; a malformed value just doesn't render. → [Schema › from value to CSS](https://standard.mode.place/reference/schema/#from-value-to-css)
- **An optional `displayName`, presentation only.** Identity and matching stay on the record key; the display name exists for galleries and pickers — the `app.bsky.feed.generator` shape (semantic rkey as identifier, display name for presentation). An app never matches or dedupes on it. → [Schema › theme](https://standard.mode.place/reference/schema/#placemodestandardtheme)
- **A thin profile.** Themes are a one-to-many collection (like posts), so they live in their own collection, not folded into the singleton profile; and the reserved `default` key replaced the old `defaultTheme` pointer. → [Schema › profile](https://standard.mode.place/reference/schema/#placemodestandardprofile)

## How looks resolve

- **A `data-mode` marker + cascade layers, not scoped placement.** Boundaries are declared once at build time so the late-arriving viewer look is one appended `<style>` the cascade routes — no per-scope runtime injection. Rejected: position-based `@scope` placement. → [Contract › Rule 3](https://standard.mode.place/contract/#rule-3--applying-a-look-to-a-scope)
- **A two-form `data-mode` grammar.** Bare expression *or* disjoint aspect-fragments, never mixed — so ordering can never matter. Rejected: a list of bare expressions (ambiguous order). → [Contract › Rule 4](https://standard.mode.place/contract/#rule-4--selecting-one-aspect-instead-of-the-whole-look)
- **The aspect delimiter is `/` — a character outside the record-key charset.** No expression can ever contain the delimiter, so the parse is unambiguous by construction and no expression name needs reserving. Rejected: `:` (a legal rkey character — it forced convention-only "avoid the colon" rules, reserved `color`/`typography` as words, and left valid-but-unaddressable keys). → [Contract › Rule 4](https://standard.mode.place/contract/#rule-4--selecting-one-aspect-instead-of-the-whole-look)
- **`:where()` keeps specificity flat; layers rank tiers, not peers.** So layer order alone decides, and multi-author feeds need containment because the layer can't separate peers. → [Contract › Rule 5](https://standard.mode.place/contract/#rule-5--whose-look-wins-fill-through-not-conflict)
- **`@scope` for multi-author containment.** It bounds matching without adding specificity, so the layer contest (and viewer override) survives inside a scoped post. → [Contract › Rule 7](https://standard.mode.place/contract/#rule-7--keeping-a-foreign-look-contained)
- **Fill-through, not conflict.** Each variable comes from the most-preferred source that sets it; the viewer's guaranteed override is the accountability valve. → [Contract › Rule 5](https://standard.mode.place/contract/#rule-5--whose-look-wins-fill-through-not-conflict)

## Naming & growth

- **Expressions are a convention, not a registry.** An opaque string compared for equality; good ids converge by network effect. Rejected: a canonical `/expressions.json`, a lexicon `knownValues` enum, and an AT-URI-referenced `expression` record — each read as authoritative or added a resolution dependency. → [Common expressions › a convention, not a registry](https://standard.mode.place/reference/expressions/#coining-your-own)
- **One reserved word: `default`.** The viewer's baseline, designated by saving a theme at that key. → [Common expressions › the one reserved word](https://standard.mode.place/reference/expressions/#the-one-reserved-word-default)
- **Additive growth; a frozen core.** The 33 intents were reviewed against the book and frozen (records can't be tightened once on the network); likely additions (letterSpacing, density, motion) are deferred, not pre-included. → [Schema › the core grows](https://standard.mode.place/reference/schema/#from-value-to-css)

## Where the rest lives

- **Identity, hosting, and the protocol features leveraged** — the DID, PDS, Tangled/Netlify setup, blob-stored fonts, firehose discovery, OAuth, and the standard.site lineage — are in the [Colophon](https://standard.mode.place/colophon/).