Writing

One neutral and two planets

Notes on rebuilding 01.inc: black on white, one typeface at one weight, and a canvas hero that is the only colour on the site.

We rebuilt this site. Here is what it is made of and why, in case any of it is useful.

One neutral

The page is #000 on #FFF, and at night it is exactly the same two values swapped. Between them sits a grey ramp that is strictly achromatic — R, G and B are equal on every token — so there is no warmth, no cast, and no brand hue anywhere. Links are ink with an underline; the active navigation item is marked with a small filled square in the gutter rather than a tint.

We tried an off-white first. It is the conventional choice and there are good reasons for it: a warm ground is easier on the eye and it makes a screen read as paper. But it is a decision that has to be defended on every subsequent choice, and the argument for simple is that there is nothing to defend. White is white.

That is a constraint with a payoff: because nothing else on the page is chromatic, the two planets in the hero do not have to be loud to be the thing you remember. A site with a brand blue would have to shout over itself.

The theme is one animatable number:

@property --night {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

:root {
  --mix: calc(var(--night) * 100%);
  --color-ink: color-mix(in oklab, var(--day-ink), var(--night-ink) var(--mix));
}

Every token on the page derives from that one scalar, which means the entire theme is a single number moving from 0 to 1 rather than two stylesheets being swapped.

The number does not animate, though, and that is worth explaining because we tried it first. Cross-fading --night over 700ms looked good when the palette was an off-white against a near-black. Against exact inverses it does not work at all: at the midpoint of the fade, ink and paper are the same grey and the page is briefly blank. We measured the contrast ratio across the transition — 21:1, then 1:1, then 21:1, sitting under 3:1 for about 180ms.

No easing curve fixes that. The distance between the two colours is 255·|1 − 2t| for any t, so the curve only decides when contrast reaches zero, never whether. A cross-fade between opposites has to pass through invisibility. So the theme cuts, which is the right register for a palette with no half-measures in it anyway.

One typeface, one weight

Geist, weight 400, for everything — body, navigation, buttons, and headings included. There is no second face and no bold. The only monospace on the site is inside code blocks, where proportional type would break column alignment, and that uses the OS stack rather than a downloaded font.

Nothing is set in capitals either. Small grey text at 12px does the job that letter-spaced small caps were doing, without asking the reader to decode a different mode of address for what is usually just a date.

The largest type on the site is 32px. That was deliberate. A headline that fills the screen reads as advertising; one held near reading scale reads as a company that expects to be taken at its word.

Two planets

The hero is a canvas. Stars are placed by hashing grid cells, so the field is deterministic — the same size produces the same sky on every load — and baked once into an offscreen buffer that gets blitted each frame. Only the planets are recomputed.

The planets are drawn, not textured. A texture-mapped globe reads as stock 3D; a graticule sphere reads as a technical drawing, which is the same register as everything else here. The latitude and longitude lines are projected from an actual sphere:

const p = orient(lat, lon, spin, tilt);   // unit sphere, rotated
if (p.z <= 0) continue;                   // facing away
const alpha = Math.max(0, dot(p, LIGHT)) ** 1.6;

The alpha of each segment is the dot product of the surface normal with the light direction. That one line is why the wireframe dissolves into the terminator on its own — there is no hand-painted shadow anywhere in the component, and the night side of each planet goes dark because the geometry says it should.

Both planets share one light source, off-panel to the upper right. Two light sources in one frame is the fastest way to make a composite look pasted together.

What we deleted

An entry splash page, a drafting-grid texture, a dev-mode inspector panel, a serif body face, a film-grain layer, an off-white ground, and about nine hundred lines of CSS. All of it was competent. None of it was doing work that the rest of the page could not do without it.

The grain went with the off-white, and it had to. It was a fixed layer of turbulence multiplied over the page, and its entire job was to stop a warm ground from reading as an unlit screen. On #FFF the only thing it can produce is a dirty white — which is the specific outcome the palette exists to avoid.

Next: Provenance as a product constraint