DESIGN.md — the forge's design system

The principles, in short. (The numbered list in §1 is the long form.)

  1. One confident sentence says what the page is for; nothing explains itself twice.
  2. The real product is the visual: show the actual rows, not a picture of them.
  3. Generous rhythm: space between things, not lines.
  4. One background, one text colour (--bg, --fg), muted for the rest.
  5. At most one accent, --accent, for interaction alone. Enforced: accent in scripts/design-lint.ts.
  6. Few borders: a hairline where a thing ends, never a box around a thing.
  7. Rows over boxes: a list is rows, a fact is a dl row, not a card.
  8. Plain words, as in docs/WRITING.md.

The five rules of attention. Every pass over a page follows them; the owner's time is the cost. Each has a real page as its pair. Enforced by rule 5: test/attention.test.ts.

  1. One answer. A page answers its one question at first sight. State is a word and a glyph, not a sentence.

- Do: securityPage gives each fact a toned badge (Recent, Overdue), not a paragraph. - Don't: previewSection (src/web/pages/apps.ts) says "The last deploy of this preview failed. Push again to redeploy it." where Failed with the tone says it.

  1. No notes. Explanatory prose that does not change a decision goes. Help lives in one place, on demand.

- Do: the home composer is the sentence of purpose and the control itself (studioHome). - Don't: a <p class="muted small"> under a list that explains what the list is; the heading already did.

  1. Real depth only. Details, history, logs and evidence open in place, in their own compartment. The essential is never one tap away, and nothing is a tap with nothing behind it.

- Do: the disclosure in src/web/ui.ts (used in src/web/pages/new-forge.ts) holds the optional extras; the choice stays on the page. - Don't: a disclosure that opens onto one line, or one that hides the verdict.

  1. Compartments. Each concern sits in one region with one heading, in the same order on every page. Regions never repeat each other.

- Do: /settings, where each concern is one settings-block with one heading. - Don't: the same fact in the header, the tab count and a status line, as the Pulls tab and its list once did (#10312).

  1. Measured. Every page has a first-view budget at a narrow (390 px) and a wide (1280 px) width: visible words, text blocks and distinct colours, in docs/design/attention-budget.json. A page over its budget fails test/attention.test.ts. A pass may lower a budget; raising one needs a note in the pull. See docs/features/attention.md.

Worked examples.

one sentence of purpose (1) and the real composer, not a picture of one (2); <section class="stack"> gives the rhythm (3); the lines <p class="small muted"><a href="/feed">The feed</a></p> are muted text, not buttons or boxes (4, 7).

per fact (Traffic, Backup, Admin accounts, …) is rows over boxes (7); a row that leads anywhere ends in one <a> (Traffic, Backups), the only interactive colour (5); each row says its state in a few plain words, with a toned badge for the verdict (Recent, Overdue) (8).

One vocabulary, two surfaces. The web renders it as HTML and CSS; a terminal UI renders the same names as cells and boxes. If a component is not in these files, it does not exist — and if you find yourself hand-rolling markup a page already has a name for, that is the bug.

Read this once. It is short on purpose.

Each section is its own file under docs/design/sections/, so a change to one never conflicts with a change to another. This page is the spine: it orders them, and the design check (bun scripts/design-lint.ts) reads them in this order — a section file not listed here is not part of the contract. A new component class is named in docs/design/areas/<area>.md, not here.

---

- 5a. Saying where you are reading from - 5b. The workflow units - 5c. Apps, runs and the crew - 5d. Page-local classes

- 6a. The code browser - 6b. Code, coloured - 6c. The editor - 6·0. The pointer - 6·0a. The dashboard's panes - 6·0a·1. Laid out for the terminal it is in - 6·0b. Icons, and the appearance picker - 6·0c. Pictures in the terminal - 6a. Counts and marks on a row - 6b. The workspace — one screen for a whole repository - 6c. Editing, and the commit dialog - 6c·1. Contributing: fork at the origin, propose a change - 6d. $FORGE_EDITOR — the operator's own editor, over a pty - 6e. Links that work - 6e·1. Reviews, threads, and the merge gate - 6f. Realtime - 6g. Apps, runs and the crew, where the terminal still shows them

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs