clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

DESIGN.md: the rules for how pages look, and for changing them

cat committed f11685d
commit
f11685db11cfbae4f4c3d756842fd6ac1346e883
parent
368898018eb6007fe240aec9e232e0f11be6107e
author
cat <admin@dev.site>
ci
✓ passed in 76s, signed by cat
kind
docs risk score 0.5 of 3, Jev's estimate
review
#13 A design system: tokens, three fonts, no caps, real controls
ci attestation
forge-attestation 1
type ci
commit f11685db11cfbae4f4c3d756842fd6ac1346e883
tree 2989fddff84fc8169d32b6fb0505f8b70ec3a66d
script .forge/ci ba20ec15392fe0c74a07ee735a5cb0194d5e5381
result pass
exit 0
started 2026-10-19T03:02:58Z
duration 76s
runner (not shown here; refs/notes/ci has it)
system Darwin 25.5.0 arm64
env host
log sha256:504d9f8abf1faf3288f47d8eb6b80efc7a0c07e3e880111e7f8b0e90f8438600 259 lines
|pushed wip/i18n
|041b643 running .forge/ci: greet.sh: say what it is
|  | sh: /bin/sh
|  | ok   greet
|041b643 pass in 0s, signed
|pushed main
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|09331da running .forge/ci: wave at the world
|  | sh: /bin/sh
|  | FAIL greet : got 'hello, world o/', want 'hello, world'
|09331da fail in 0s, signed
|forge: 1 commit(s) did not pass
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|644fc35 running .forge/ci: update the test for the wave
|  | sh: /bin/sh
|  | ok   greet
|644fc35 pass in 0s, signed
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|warning: You appear to have cloned an empty repository.
|site home: deployed 1b2fa10 -> /sites/ana/dotfiles/home/
|created ana/notes
|remote: review #1: /ana/notes/reviews/1/
|pushed ana/todo
|landing #1 A todo list: ana/todo -> main
|eb33e8b running .forge/ci: web, server and Python versions of greet
|  | sh: /bin/sh
|  | ok   greet
|eb33e8b pass in 0s, signed
|pushed main
|4bb49f4 running .forge/ci: messages: notes for the later regions
|  | sh: /bin/sh
|  | ok   greet
|4bb49f4 pass in 1s, signed
|remote: review #7: /dami/greet/reviews/7/
|pushed dami/regions
|created dami/forge
|warning: You appear to have cloned an empty repository.
|site guide: mkdir -p guide/fonts && cp site/assets/fonts/*.woff2 guide/fonts/
|site guide: deployed 5909ec8 -> /sites/dami/forge/guide/
|(node:60561) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/dotfiles in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.H7TPaU/wt/demo/root/.forge/www
|(node:60691) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/notes in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.H7TPaU/wt/demo/root/.forge/www
|(node:60835) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/forge in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.H7TPaU/wt/demo/root/.forge/www
|(node:60981) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/greet in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.H7TPaU/wt/demo/root/.forge/www
|(node:61660) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built - in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.H7TPaU/wt/demo/root/.forge/www
|234 of 234 pages render the same without 11ty
|guide: 8 files, links ok
|ok

Signature: good, cat

2 files changed, +101 -3
ADESIGN.md96++++++++++++++++++++++++++++++
MREADME.md8++-
A DESIGN.md +96 -0
@@ -0,0 +1,96 @@
1+# Design2+3+How the forge's pages look, and the rules for changing them. They are4+written for whoever edits the templates, people and agents alike. CI holds5+the CSS to them (`node lib/check-css.ts`, run by `.forge/ci`), and the style6+guide at `/design/` (`site/design.njk`) draws every token and control with7+the real stylesheet and macros. Check it after a change.8+9+## The look10+11+A plain page (no texture, no grid), square corners, and hairline rules12+between things rather than boxes around them; only a file viewer, a diff or13+a code block is a bordered region. Structure comes from the order of things14+and the rules between them, in the spirit of the Vanilla framework. Phone15+first: lists restack, tabs scroll sideways.16+17+## Tokens18+19+Every colour, font size, weight, font family, space, rule width and the20+radius is a custom property in the token block at the top of21+`site/assets/style.css`, between the `== tokens ==` and `== end tokens ==`22+comments, with its dark value in the `prefers-color-scheme: dark` block23+under it. Nothing outside that block names a colour, size, weight or24+family: not `style.css` below it, not a template's `{% css %}` block, not25+a `style=""` attribute. If no token fits, add one there (with a comment if26+it starts a group: the style guide titles groups by those comments).27+28+- Colour: `--bg` the page, `--raised` surfaces over it (white in light29+ mode), `--hover`, ink in three strengths `--fg`, `--muted` (at least30+ 4.5:1 on the page) and `--faint`, rules `--border` and `--border-strong`.31+ One accent, `#3148F6`: `--link` for links (lighter in dark mode),32+ `--accent` for the current tab's bar, focus, primary fills, `--deep` on33+ hover. Meaning: `--add-*`, `--del-*`, `--pass`, `--fail`, `--warn`,34+ `--status-*` fills.35+- Type: `--text-xs` 12, `--text-code` 12.5, `--text-sm` 13.5, `--text-md`36+ 15 (body), `--text-prose` 16, `--text-lg` 18, `--text-xl` 22,37+ `--text-2xl` 28. Weights `--fw-regular` 400 (body), `--fw-medium` 50038+ (links, controls), `--fw-strong` 650 (emphasis, headings).39+- Space: `--space-1` to `--space-10` on a 4px scale, `--hairline` 1px for40+ rules, `--bar` 3px for the current tab, `--radius` 0.41+42+## Fonts43+44+Self-hosted variable woff2 in `site/assets/fonts/` (how they were cut:45+`make-fonts.sh`; licenses: the `OFL-*.txt` files there).46+47+- `--sans`, Open Sans: all interface text. Navigation, tabs, lists,48+ subjects, page and section headings, forms, buttons, labels.49+- `--mono`, Noto Sans Mono: anything copied or compared. Hashes, review50+ ids, branch names, paths, counts like +12, commit kinds, code, diffs,51+ clone and install commands.52+- `--serif`, Noto Serif: only rendered prose people wrote. READMEs and53+ markdown files, the wiki, release notes, review descriptions and54+ comments, the guide's text. Never interface chrome.55+56+## Words57+58+- No capitals by CSS and no letter-spacing, anywhere. Interface chrome59+ (tabs, buttons, labels, table headers, section headings, status labels,60+ summaries) is written in lowercase in the templates. Hierarchy comes61+ from weight, size and colour.62+- What users wrote (commit subjects, review titles, repo descriptions) is63+ never case-transformed.64+- Kinds, topics and filters are plain words, not chips.65+- Never the middle dot (U+00B7), in code, comments, UI strings or66+ commits; use a comma, a hyphen or a pipe.67+68+## Controls69+70+Words for where you are, symbols for how you look at it.71+72+- Tabs say where you are (`ui.tabs`): the repo's sections, the shelves on73+ the index (with an "all" tab).74+- A checkbox turns one thing on or off (`ui.checkbox`): wrap lines,75+ viewed.76+- An icon toggle is a checkbox drawn as a symbol (`ui.iconToggle`): a77+ button with `aria-pressed`, `aria-label` and `title`, the size of an78+ icon. Example: the globe, "only repos with a site".79+- A segmented control picks one of a few (`ui.segmented`): the sort,80+ newest first or by name.81+- Buttons do things (`ui.button`); one primary, filled with the accent,82+ per place at most. A toggle that is on is filled with ink. A filled primary darkens on hover (`--accent-hover`); `--deep` is for text.83+- Links only go places. Never use a link, or a word that changes when84+ clicked, as a toggle.85+86+Use the macros in `site/_includes/ui.njk` (also `ui.input`,87+`ui.textarea`, `ui.listTable`, `ui.status`, `ui.heading`, `ui.icon`)88+rather than writing the markup again. A new control goes there first,89+with its CSS in `style.css` under "controls", and onto `/design/`.90+91+## After a change92+93+Run `sh .forge/ci` (it runs the CSS check and the scenarios). After a94+deploy, look at the pages: the home page, a repo, its commits, a review95+and its diff, a README, releases and `/design/`, in light and dark, at96+desktop and phone widths. Grepping HTML is not looking.
M README.md +5 -3
@@ -1321,8 +1321,10 @@ pagination:
13211321 {% endblock %}13221322 ```13231323 1324-`site/assets/style.css` holds what every page needs: colors, layout, lists,1325-code views. CSS for one feature sits next to its markup in a `{% css %}`1324+`site/assets/style.css` holds what every page needs: the design tokens1325+(every colour, size, weight and font), layout, lists, code views. The rules1326+for the look, and for changing it, are in [DESIGN.md](DESIGN.md); the1327+controls are macros in `site/_includes/ui.njk`, and `/design/` shows both. CSS for one feature sits next to its markup in a `{% css %}`13261328 block, in the template, include or macro that renders it (the discussion,13271329 the search overlay, the shortcuts dialog, the review bar, review lists, the13281330 wiki), and 11ty's bundle plugin gives each page one file of the blocks it
@@ -1331,7 +1333,7 @@ rendered, `/assets/<hash>.css`, shared by pages of a kind. Import
13311333 13321334 Assets ship under content-hashed names (`/assets/style.<hash>.css`,13331335 `ui.<hash>.js`, mermaid, sqlite-wasm), so browsers can cache them for good;1334-the stylesheet's `url(./paper.webp)` becomes the texture's hashed name. The same1336+the stylesheet's `url(./fonts/open-sans.woff2)` becomes the font's hashed name. The same13351337 sources give the same names, and pages' fingerprints include them, so13361338 unchanged pages are still hard-linked. A build keeps the files of the last13371339 five code versions (listed in `assets/versions.json`), because pages built by