Skip to content

feat!: the 0.4.0 macOS identity - #31

Merged
fajarhide merged 9 commits into
mainfrom
feat/macos-identity-tokens
Aug 6, 2026
Merged

fajarhide merged 9 commits into
mainfrom
feat/macos-identity-tokens

Conversation

@fajarhide

Copy link
Copy Markdown
Contributor

Implements the spec merged in #30. Nine commits, each one green.

One neutral ladder and one system blue replace six colour families. Themes drop
to light and dark. primary is ink, so a filled pill is the only solid
button on a page and blue is left for ring and chart-1. Corners gain a
ladder at 6, 10 and 14px, all continuous through corner-shape: squircle, which
is the signature the rest of the system stays quiet for. Inter ships as one
variable file with the optical size axis, so six woff2 become three and 215KB
becomes 113KB.

The terminal survives as ten tokens with a real ANSI palette on a ground that
does not follow the theme, because the same palette over white tops out at
2.87:1.

Four gates added, and they earned it. Two caught the values this release was
specified with: the terminal cyan sat 24.9 degrees from blue, and every terminal
token was emitted by nothing and resolved to nothing in a browser while the
suite stayed green. A third caps neutral chroma at 0.008, verified by setting
dark.card to shadcn's slate default and watching it fail. The fourth stops
dist/fonts/ holding a file the tokens no longer name.

npm run check:overflow is new. It loads each built docs page in an iframe at
fourteen widths from 280 to 1280 and fails on sideways scroll. Two failures on
its first run, both fixed here: a patch of mine had flattened a one-line rule
into orphan declarations, which swallowed the .links { display: none } that
follows it, so the nav rendered its full link row at 320px with the hamburger
hidden.

npm test                # 61 pass, 0 fail
npm run docs:build      # 3 pages
npm run check:overflow  # no page scrolls sideways at any width checked

Closes nothing yet. #23, #24 and #25 are the phases after this one.

Replaces sand, navy, stone, amber, phosphor and black.crt with a single neutral
ladder and one system blue. Themes are light and dark; paper is gone, and Omni
keeps its paper stock in its own stylesheet.

primary is ink rather than blue, so a filled pill is the only solid button on a
page and blue is left for ring, link and chart-1.

The blue is not Apple's. systemBlue #007AFF fails AA on white at 4.02:1, and
#0071E3 passes on white at 4.70:1 but fails on the muted surface at 4.31:1. The
values here clear every surface they are allowed on, 4.62:1 at worst.

The terminal moves out of the theme table into its own group of ten roles,
unthemed. One ground means one set of ratios; the same palette over white tops
out at 2.87:1.

Two gates move with the rename: the theme list, and the derived pair count,
which is 12 now that terminal-foreground belongs to TERMINAL rather than ROLES.
The css block test gains a negative lookahead so an undeclared theme fails,
which a list of match calls could not catch.
The terminal roles are colours that are not themed, so the CSS formatter's
theme branch skipped them and its scale branch skipped them too. They shipped
as ten tokens that resolved to nothing in a browser, and the existing "every
unthemed token reaches the CSS" gate could not see it because it filters
colours out by design.

All four formats carry them now, so `bg-terminal-green` compiles and
`terminal` is readable from tokens.js. The new gate walks TERMINAL against
tokens.css and shadcn.css.
The chroma ceiling is 0.008 for every neutral role. Verified it fails by
setting dark.card to shadcn's slate default, which reports chroma 0.042.
Without it a dark theme drifts toward a hue while its light twin stays neutral,
and that is invisible unless the two are compared side by side.

The terminal gets four gates: every declared role exists and no more, every
colour clears 4.5:1 on the ground, the ground equals the dark card so a
terminal block inside a card is not two near blacks a few percent apart, and
the six colours stay 25 degrees apart in hue.

That last one caught the palette as specified. Cyan sat 24.9 degrees from
blue, close enough to read as the same status in one-word output. It moves from
228 to 204 degrees, roughly midway between blue and green, and gains contrast
on the way: 9.44:1 against 8.92:1.
Six woff2 become three. Inter ships as a single variable file carrying the
whole weight range and the optical size axis, so a hero and a caption are drawn
by the same file with different letterforms, which is what SF Pro Display and
SF Pro Text do with two. Bricolage Grotesque and the three IBM Plex families
are gone with the paper theme.

font.ui.* and font.paper.* collapse to font.sans, font.mono and font.accent.
The accent is Instrument Serif italic, one face, loaded only by pages that
want an editorial line.

build/fonts.js gains a three-line table for what a token cannot say about
itself: whether a family ships variable or per weight, and whether the face
wanted is the italic. Everything else still derives from the tokens, and a role
with no entry throws rather than silently shipping nothing.

Two new gates. The sans must stay a variable face, because static weights would
quietly cost the optical size axis. And dist/fonts may hold nothing the tokens
do not name: the build copies and never deletes, so a swapped family leaves the
old woff2 committed and served, with its licence beside it covering a font the
package no longer ships. That had already happened here.
…widths

radius gains control at 6px, card at 10px and window at 14px beside the pill,
matching what macOS uses for a control, a card and a window. corner.shape ships
squircle, which is what makes a corner continuous rather than circular; a
browser without corner-shape ignores it and keeps border-radius, so the token
needs no guard at the call site.

The per-theme radius stays, and a gate now ties it to radius.card. If those
drift, a .wl-card and a shadcn Card on the same page round differently, which
reads as a rendering fault rather than a config mistake.

Container widths are the odd group. @container cannot read a custom property,
so shipping --wl-container-narrow would advertise a query that parses, never
matches, and reports nothing. They ship as data in tokens.js and as literals in
the Tailwind bridge, where the query resolves at build time, and a gate asserts
both plus the absence from tokens.css.
The triptych is a diptych. It always iterated THEMES, so the third panel left
on its own; the rename and the copy are the work. Terminal variables follow
the token rename, and bg-terminal keeps working because ground registers as the
default rather than a suffix.

Every call site moves off the single --wl-radius onto the step it actually
wants, control for chrome and cards for panels, and each one now sets
corner-shape beside it. The signature of this system is a continuous corner, so
the page that argues for it should be drawn with one.

Copy that had gone stale against the code: fourteen pairs became twelve, the
alert no longer claims each theme picks its own radius, and the terminal shows
the real test count. The failure the alert demonstrates is a real one from this
branch, the cyan that sat 24.9 degrees from blue.
scripts/check-overflow.mjs serves the built site and loads each page in an
iframe at 280 through 1280, failing when scrollWidth exceeds the viewport.
Elements inside a box that scrolls on the x axis are ignored, or every terminal
block and filter row reports as a false positive.

It is a script rather than a test because it needs Chrome, which CI does not
have. `npm run check:overflow`.

Three harness shapes were tried before one told the truth, and the two that
lied did it quietly. A requestAnimationFrame chain never completes under
--virtual-time-budget, so --dump-dom snapshots an empty result. Resizing one
iframe reports the same stale scrollWidth at every width, because the child
does not relayout when the parent attribute changes; that version reported the
page overflowing everywhere by a shrinking amount, which looks plausible enough
to act on. Sequential frames, created and awaited one at a time, agree with a
screenshot.

It earned its place immediately. The nav was rendering its full link row at
320px and pushing the page 162px wide, because a patch of mine had flattened
`.brand img { border-radius }` into two orphan declarations, which swallowed
the `.links { display: none }` rule that follows it. The hamburger was hidden
at the same time, so the header looked deliberate and was not.

The second failure is real and older: an import path in a code span has no
space to break at, so at 320 one span was wider than the column.
ABOUT loses the two arguments that are no longer true, the paper stock and the
amber CRT, and gains the ones that replaced them: why a Mac identity, why the
signature is the corner rather than the colour, why primary is ink, and why
Apple's own blue is not used. Three sections it kept: the three layers, mono
means a machine emitted it, and contrast as a test rather than a comment.

One section is new and general. A token can be defined and still not exist: the
terminal roles were emitted by nothing and resolved to nothing in a browser
while the suite stayed green, and container widths must never be CSS variables
for the mirror-image reason. Only a test that reads the built output tells the
difference.

README follows the code: two themes, three faces, the radius ladder, the
container width warning, the terminal's ten roles, and a 0.3.x migration table.
The 0.2.x section stays as it was, with a line saying it describes the move to
0.3.0 and still names a theme that no longer exists.

CHANGELOG is new and ships in the tarball, since a breaking bump is the one
thing a consumer reads before taking it.
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
design Ready Ready Preview Aug 6, 2026 3:09am

@fajarhide
fajarhide merged commit f1bf033 into main Aug 6, 2026
3 checks passed
@fajarhide
fajarhide deleted the feat/macos-identity-tokens branch August 6, 2026 03:14

This branch was successfully deployed

1 active deployment
Preview — b4dd3dba Deployed Aug 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant