White, rounded and quiet.
The look was made for capta.cam, one of the apps, and the rest of the studio took it. Everything below is the live system, rendered from the same stylesheet the rest of the pages use - so when one of them changes, this page has already changed with it.
One accent. If it is indigo, you can press it or it is telling you where you are.
Objects are fills, not outlines. A tile is soft grey on white, or white on a shadow.
Colour belongs to the products. Each app brings its own tint; the site stays out of the way.
White, soft grey, and some light
The page is --bg. A band or a tile that needs to separate from it is
--soft. Behind a section there can be two large, faint radial gradients, one
from each side: they are gradients and not blurred elements, because a half-viewport box
under filter: blur() is a full-screen compositing layer, and a gradient that
fades to transparent is the same picture for nothing.
And the dark tile closes a page.
Six tokens, and the colours that are not ours
Every ratio below is computed from the hex beside it at build time. The palette itself is
imported from src/data/palette.mjs, the same literal
tailwind.config.mjs reads - this page holds no copy of it.
The tokens
#FFFFFF The ground. The name is from the look before this one; base.css says why it stayed.
#F5F5F7 A tile sitting on the ground, and the alternate section band.
#1D1D1F Headings and strong text. Never a fill, except the code slab.
#6E6E73 Body copy. The one token tuned to a threshold rather than a look.
#5B57E6 Links, buttons, kickers, the current thing. The only colour with a job.
#E8E8ED Hairlines: table rows, dividers, the nav's edge once you scroll.
The apps' tints
Read from projects.ts, where each was taken from the product's own site. A tile
is washed in its app's tint at about a tenth strength, and the six together, blurred, are the
light behind the pile of prints on the home page.
#31583F Retriever #3563F0 Cleat #E0623F Small Skies #4FB0F0 Capta #5B57E6 Sunflower #F0B429 Cadence #3DBA84 Sleep Timer #7A86F5 One face, four weights
Inter, variable, self-hosted. JetBrains Mono is still loaded and sets code and nothing
else; it was the body face of every page until this look, and the day it stopped being a
label face is the day code started standing out again.
Software for people
Body copy is --ink-2 at 17px on a 1.6 line-height. Longform in
prose.css opens that to 1.72, and a lesson darkens it, because twelve hundred
words beside a terminal is a different kind of reading.
The coloured word, the kicker, the link
One word in a heading can carry colour: a gradient clipped to the glyphs, solid accent
where background-clip: text is missing. The class is still .hl,
from when it was a highlighter stripe behind the word.
The one object
Everything that needs to read as a thing is a rounded tile at --radius. The
older class names are from the paper look and were kept because nearly forty files use them;
new work should reach for .s-tile.
Soft grey, no shadow. Sits on the page.
White on --shadow. Floats above it.
A link. White on --shadow-sm, and it lifts under the pointer.
.s-tile, tinted
An app's tile
Washed in the product's own colour.
.s-lilac
A course
One of two fixed washes.
.s-mint
The other course
So neighbours never match.
Pills
A button is a pill. The solid one is the accent, for the one thing a view most wants pressed; the quiet one is a wash of the same colour. Skill chips fill with their level under the pointer.
Arrive once, then hold still
Content reveals with .reveal, a view() timeline with no
JavaScript; where scroll-driven animation is unsupported the keyframes run for zero
seconds and land on the visible frame, so unsupported means already revealed and never
invisible. Tiles lift two or three pixels on --ease. Only the games on the
home page use --spring, which overshoots: tools sit still, toys do not.
Everything that moves stops under prefers-reduced-motion.
One object, and a lot of air
Every cover, card and share image is drawn by a script and committed; the tooling that
draws them is not. A picture is one object: the thing you would put on the app's icon.
It sits on one of two grounds. Dark is near-black with stepped rings of light, a glass
tile and a glowing glyph, with colour spilling out from behind the tile. Pastel is a
pale field deepening into the product's own colour, with one frosted object floating on
a soft shadow. A set mixes the two, so a row of cards is never six black rectangles.
There are no outlines anywhere, and nothing is random: the same input draws the same
bytes. scripts/art-kit.mjs is the whole vocabulary, and its header has the
two attempts that came before this one.



scripts/product-cards.mjs images/products/<app>-card.webp One object per app: the pile on the home page and the corner of each tile.
scripts/post-heroes.mjs post heroes One object per post. Wordless: the alt text carries the meaning, so a reworded sentence never costs a re-render.
scripts/og-card.mjs og-default.png The share card every page falls back to.
scripts/og-product-cards.mjs og-<app>.png One share card per product page, carrying the same glyph as the app's tile.
scripts/course-card.mjs course covers The title, the lesson count in a pill, and the count again as the glyph.
scripts/lesson-cards.mjs lesson covers Titles read straight out of the lesson frontmatter. A title that does not fit fails the run.
scripts/x-banner.mjs images/x-banner.png Laid out by hand, because the avatar covers the corner a cover puts its address in.
scripts/favicon.mjs favicon.png, apple-touch-icon.png The mark, at the sizes Google and Apple ask for.
What would drag it back
Each of these is something the site did while it was paper, and did well. They are listed because they are habits in this codebase, not because they are wrong.
Borders that draw the object
A fill, a radius, sometimes a soft shadow
An outline says diagram. A tile is a surface, and a surface has no edge line.
Hard offset shadows
Two blurred ones, small and large
An offset says block print. Here a thing either sits on the page or floats a little above it.
Uppercase, letter-spaced labels
Sentence case at 600 in the accent
Tracked capitals at 11px were the mono look's voice. In Inter they read as a form.
Ink-filled tabs and strips
A chip in a wash of the accent
A black bar across a rounded white tile is the heaviest thing on the screen and says the least.
The mono as a label face
The mono for code, only
When every label is mono, code stops looking like code.
A class called .btn, .badge, .card or .hero
An s- prefix
daisyUI is still a Tailwind plugin here and owns those names. It has cost a morning before.