Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
33d75f8
Export the brand font weights as variables and Tailwind weights
Oct 6, 2026
dbdc1d3
Export the halftone colours and read them in .sg-halftone-cutout
Oct 6, 2026
f15d6d2
Canon type classes read the font, weight and size variables
Oct 6, 2026
e8a9dd7
Add the base highlighter, measured from the Brand Book
Oct 6, 2026
a4909d9
Canon geometry: unclipped pattern rows and a frame scale that covers
Oct 6, 2026
25ce1f9
Spec: links follow the contrast rule; list the new variables
Oct 6, 2026
8a0a06f
Ship brand-spec.md in the package
Oct 6, 2026
0a87821
README: say how the package is built; document the new variables
Oct 6, 2026
0936cf9
v1.4.0
Oct 6, 2026
4b6f535
Provenance: halftone source slide, full quotes, how measured values a…
Oct 6, 2026
2c43702
Highlighter: aligned in any heading, measured offset, word keeps its …
Oct 6, 2026
7b74b95
README and spec: upgrade notes, link rule evidence, US spelling
Oct 6, 2026
3813258
CONTRIBUTING: the source rule (Brand Book first, the website where it…
Oct 6, 2026
0111455
Tokens from the Brand Book and, where it is silent, from the website
Oct 6, 2026
3c2492b
Spec: color roles, layout, radius, shadow and motion with sources; bl…
Oct 6, 2026
8bc11e6
Ship brand-check.mjs, one brand guard for every project
Oct 6, 2026
18b4a19
Review fixes: sources and counts, a sharper brand guard, canon differ…
Oct 6, 2026
1e1e61f
Canon follows the Brand Book: dark text on orange, black on light, bo…
Oct 6, 2026
0b09f36
Brand guard: catch the website drifts it missed, stop failing correct…
Oct 6, 2026
fce65cd
Review fixes: info bar on orange and light, H5 and H6 headlines, a gu…
Oct 6, 2026
25c88f3
Cite the website by commit and CSS name, not by private file and line
Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/design.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
fi
echo "Version bump to $head_version: merging this pull request releases v$head_version."
else
changed=$(git diff --name-only "$base" HEAD -- README.md brand.css index.js supergraphics.css tailwind.js theme.css tokens.json | tr '\n' ' ')
changed=$(git diff --name-only "$base" HEAD -- README.md brand-spec.md brand-check.mjs brand.css index.js supergraphics.css tailwind.js theme.css tokens.json | tr '\n' ' ')
if [ -n "$changed" ]; then
echo "::warning::Published files change without a version bump, so merging releases nothing: $changed"
else
Expand Down Expand Up @@ -130,7 +130,7 @@ jobs:
tag_sha=$(printf '%s\n' "$tag_lines" | tail -n 1 | cut -f1)
if [ "$tag_sha" = "$GITHUB_SHA" ] || [ "$before_version" = "$version" ]; then
echo "v$version is already tagged: nothing to release."
changed=$(git diff --name-only "$BEFORE" "$GITHUB_SHA" -- README.md brand.css index.js supergraphics.css tailwind.js theme.css tokens.json | tr '\n' ' ')
changed=$(git diff --name-only "$BEFORE" "$GITHUB_SHA" -- README.md brand-spec.md brand-check.mjs brand.css index.js supergraphics.css tailwind.js theme.css tokens.json | tr '\n' ' ')
if [ "$tag_sha" != "$GITHUB_SHA" ] && [ -n "$changed" ]; then
echo "::warning::Published files changed without a version bump, so they are in no release until the next one: $changed"
fi
Expand Down
87 changes: 73 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
# Contributing to @myfirstbitcoin/design

This repository is the single source of the My First Bitcoin brand as code: the design tokens,
the build that turns them into the published package, the written brand specification, and the
check against the Brand Book in Figma. The Brand Book in Figma is where the brand is decided;
`tokens.json` mirrors it and is edited only to match it.
the build that turns them into the published package, the written brand specification, the
brand guard that projects run, and the check against the Brand Book in Figma. The Brand Book in
Figma is where the brand is decided; `tokens.json` mirrors it and is edited only to match it,
under the source rule below.

## The source rule

1. **Brand Book first.** Where the Brand Book in Figma (file `mFIc75UUSyftaqnNUQgjLX`, page
`262:2`) defines a value, its value is the token, cited by node (`"source": "figma:<node>"`).
2. **Where the Brand Book is silent**, the value used on the live website, myfirstbitcoin.org,
may become the token. The website was built following the Brand Book, so what it does where
the book says nothing is the best evidence there is. Such a token is declared with its origin
on the site's main branch: `"sourceKind": "declared"` and
`"source": "myfirstbitcoin.org@<commit>, <CSS name>"`, with the number of uses in its note.
The CSS name is what carries the value in the CSS and pages the site serves, which are public:
a custom property (`myfirstbitcoin.org@6368b2a, --r-lg`), a selector and property
(`myfirstbitcoin.org@6368b2a, .article-prose line-height`), or a property set inline, with
where it is used (`box-shadow (inline, media frames)`). The site's source repository is
private, so no file, directory or line of it is named in this repository; the deciders named
under Releases keep the exact lines outside it.
3. **Never a website value that contradicts the Brand Book**, however often the site uses it: a
slant other than the book's angle, a highlighter other than the book's thin line, duotone
photos, a font or a color outside the book, all-caps text, orange text on a light background.
`brand-spec.md` lists the ones found so far under "Not imported from the website".
4. **Where the site has no single value** (several near-duplicates with no convention), no token
is made until someone picks one.
5. **The deciders named under Releases decide** every brand change, as for any pull request here.

Every token carries `$extensions.mfb.sourceKind`, defined in `$verification` at the end of
`tokens.json`: `rendered-fill` or `rendered-text` (checked by the Figma check), `prose` (stated in
the Brand Book in words), `measured` (read off the Brand Book's vector geometry or a node's
rendered fill, with the node ids in the note), `declared` (not in the Brand Book: a website value
with its site source, or a value this package recommends, with `"source": "declared"` and the
reason in the note), or `unverified`. Color roles and shape tones are aliases of palette colors
(`{color.black}`), and the build refuses one that is not.

Propose every change by pull request. The conventions in [AGENTS.md](AGENTS.md) apply to all
contributions, by people and AI assistants alike.
Expand All @@ -16,13 +48,17 @@ contributions, by people and AI assistants alike.
| `package.json` | Name, version, exports and the `files` list (published) | Yes: the version is how a release happens |
| `src/supergraphics.canon.css` | The supergraphics canon, the origin of the published supergraphics (see below) | Yes, by pull request, with no added comment |
| `src/brand-spec.template.md` | The prose of the brand specification | Yes |
| `brand-check.mjs` | The brand guard projects run after their build (published) | Yes, with its test in `scripts/brand-check.test.mjs` |
| `scripts/` | Build, spec generator and checks | Yes |
| `README.md`, `brand.css`, `theme.css`, `tailwind.js`, `index.js`, `supergraphics.css` | Generated package files (published) | No: rebuild them |
| `brand-spec.md` | Generated brand specification (public, not part of the package) | No: regenerate it |
| `brand-spec.md` | Generated brand specification (published, so consumers and their AI tools read the rules from `node_modules`) | No: regenerate it |

What consumers install is exactly the list in `package.json`'s `files`, plus `package.json`
itself: `README.md`, `brand.css`, `index.js`, `supergraphics.css`, `tailwind.js`, `theme.css`
and `tokens.json`. Nothing else in this repository reaches them.
itself: `README.md`, `brand-spec.md`, `brand-check.mjs`, `brand.css`, `index.js`,
`supergraphics.css`, `tailwind.js`, `theme.css` and `tokens.json`. Nothing else in this
repository reaches them.
`brand-spec.md` is there so that a consumer, and the AI tools working in its repository, read
the brand rules at `node_modules/@myfirstbitcoin/design/brand-spec.md`, at the version they pin.

`package.json` must never get `scripts` (including `prepare` or `postinstall`) or any kind of
dependencies. Consumers install this package as a git dependency, and npm runs a git
Expand All @@ -40,14 +76,24 @@ node scripts/check.mjs # the rebuild check that GitHub Actions
```

Commit the source change and the regenerated files together. The build refuses to write
anything if the canon's geometry disagrees with `tokens.json`, and the spec generator refuses if
a token it needs is missing.
anything if the canon's geometry disagrees with `tokens.json`, if a geometry token is neither
gated against the canon nor listed as token-only, if a tokens.json group has no entry in its
`GROUP_MAP` (where each group is emitted), if a new Tailwind key would equal a Tailwind 3 or 4
default, or if a font weight would change Tailwind's value for its name (`font-medium` is 500).
The spec generator refuses if a token it needs, or its source, is missing.

A new token group needs an entry in `GROUP_MAP` in `scripts/build-design-package.mjs`: its
Tailwind 4 namespace, its Tailwind 3 key and its `brand.css` prefix, or `null` where it is not
emitted. New Tailwind keys are `mfb-` prefixed, type levels or color roles
(`<element>-on-<surface>`), so that no release changes a utility a project already uses.

## Checks on every pull request

- **rebuild-check** (`scripts/check.mjs`): rebuilds into a temporary directory and fails if any
committed output differs from a fresh build, if the build or the spec generator refuses, or if
`package.json` gains scripts or dependencies. It also runs on every push to `master`.
committed output differs from a fresh build, if the build or the spec generator refuses, if
`package.json` gains scripts or dependencies, or if `brand-check.mjs` misses a planted
violation or reports a clean line (`scripts/brand-check.test.mjs`). It also runs on every push
to `master`.
- **version-check**: if the pull request changes the version in `package.json`, the new version
must be above the newest `v*` tag and must not already be tagged. If published files change
without a version bump, it warns: merging would release nothing.
Expand All @@ -68,7 +114,8 @@ release only when they raise their pin.
## The brand-change loop

1. Patrick changes the Brand Book in Figma and tells Quentin, or the Monday check flags a
difference between Figma and `tokens.json`.
difference between Figma and `tokens.json`, or a value the Brand Book is silent on is
declared from the website under the source rule.
2. The admin session opens a pull request with the change to `tokens.json` and the rebuild.
3. Quentin or Patrick decides.
4. Merging a pull request that raises the version releases it.
Expand All @@ -88,6 +135,17 @@ It is not run in GitHub Actions, and no Figma token is stored in this repository
check runs it outside GitHub. `--status FILE` writes the full result as JSON for that job, and
`--summary` writes a short Markdown summary (to `$GITHUB_STEP_SUMMARY` when that is set).

## The brand guard

`brand-check.mjs` is published, and projects run it after their build
(`node node_modules/@myfirstbitcoin/design/brand-check.mjs`; the README says how). It needs
Node.js and nothing else, and reads every value it compares with from the `tokens.json` next to
it, so a token change reaches the guard without editing it. It generalises the guards that two
of My First Bitcoin's sites already ran. A rule that cannot see enough to be sure (which
background a text sits on, say) is listed in `DEFAULT_WARN` and only warns unless a project passes
`--error`. When you change a rule, add a planted line for it to `scripts/brand-check.test.mjs`
(and a clean line for anything it must not report); `scripts/check.mjs` runs that test.

## The supergraphics canon

`src/supergraphics.canon.css` is the origin of the supergraphics primitives. It began as a
Expand All @@ -100,9 +158,10 @@ Keep it free of added comments: any byte added here changes the published file.
here, by pull request; the internal copy is then brought into line, and a weekly check compares
the two.

The canon, `tokens.json` and the README template keep the punctuation they were published with,
em-dashes included, because rewording them changes published files. Such rewording belongs in a
release pull request.
The canon and `tokens.json` keep the punctuation they were published with, em-dashes included,
because rewording them changes published files. Such rewording belongs in a release pull request.
The README template (in `scripts/build-design-package.mjs`) was reworded in the v1.4.0 release
and follows the conventions in [AGENTS.md](AGENTS.md): new published prose carries no em-dash.

## Where the history lives

Expand Down
Loading
Loading