engineering
Print is a theme now
cia's print colour was done three ways at once — a forced light scheme, a "legible" patch for dark themes, and a docs site that hardcoded black-on-white anyway. Collapsing them into four tokens turned print into just another themeable surface.
We already had print. Printing a design system and print-to-PDF with zero JavaScript covered the mechanism — the page is the PDF, the browser is the generator, cia is only the styling. What we didn't have was a coherent story for print colour. We had three.
Three mechanisms, one job
The one job is: put legible ink on paper, whatever theme you were looking at.
Here's how print-base was doing it before:
- Force the light scheme.
@media print { color-scheme: light }makes a pairedlight-dark()theme print its light branch — dark text on a white sheet, for free. - Patch the dark-only themes. Terminal has literal light ink, so forcing
the scheme doesn't help it. A
$legibleflag remapped--inkand friends toCanvasText— a targeted darken so the body text survived on white. - Ignore all of that on the docs site. Our global print layer just wrote
body { background: #fff; color: #000 }and hardcoded the code blocks, because a dark theme printing its own surfaces would burn a whole cartridge.
Three answers to one question, and the third one threw away the first two. That is the shape of a thing that wants to be one idea.
The idea: print is a surface, so give it tokens
A theme already restyles every surface by owning tokens. Print is just another
surface. So print-base now defines a print palette —
--print-ink; /* body, links, code text — default #000 */
--print-paper; /* backgrounds — default #fff */
--print-line; /* rules, hairlines — default #999 */
--print-muted; /* URLs, captions — default #666 */
— and then, inside @media print, rebinds the theme's own colour tokens onto
that palette:
@media print {
:root {
--ink: var(--print-ink);
--surface-default: var(--print-paper);
--border-default: var(--print-line);
--code-bg: var(--print-paper);
/* …the rest of the colour tokens… */
}
body { background: var(--print-paper); color: var(--print-ink); }
}
That one move does everything the three mechanisms did, and more. Every element
that reads a theme token — which is every element — resolves to ink on paper.
Dark-only themes are legible with no flag, because their --ink is now
--print-ink, which is black. And because it's all tokens, overriding a single
--print-* restyles the whole page.
$legible became a deprecated no-op. The rebind supersedes it; passing it warns.
The payoff: paper you can theme
Once print reads a palette, "make it look different on paper" stops being a special case:
- A theme's own paper identity lives in its one file. Press — our newsprint
theme — sets
--print-line: #D93025in its own@media printblock, and every rule on the printed page turns press-red. No other theme changes. - A shareable look — a corporate letterhead palette — is a print-only
overlay you drop onto any theme with
<link media="print">, because it's just--print-*overrides and they compose. - A letterhead itself — the branded header on the paper — turned out not to
be a theme at all. It's markup: a
print-onlyheader that reads the palette. So it's a recipe, not a feature, and an AI agent can generate one on request.
The editor twist
Here's the part that needed a trick. cia's theme editor edits the live page, but
--print-* tokens only do anything inside @media print — so editing them
shows nothing on screen. You'd have to actually print to see your change.
So the editor's new Print preview does the rebind on screen: a paper panel
that binds the theme's tokens to the palette the same way print-base does in
print, live. Edit the ink, watch the preview re-ink, copy the @media print
block into your theme. The simulation is the same six lines of CSS as the real
thing — which is the nice thing about building on tokens instead of a special
print path.
The honest cost
One behaviour changed, and it's worth saying plainly: a light theme used to
print its own faint surface tints; now every surface flattens to --print-paper
(white by default). That's the intended result — predictable, toner-friendly
paper — but it is a visible change, and it's in the changelog as one. A theme
that wants its tints back declares them in its own @media print block. The
print look is entirely the theme's to decide; the default is just sensible.
None of this added a byte of JavaScript to the package, and the analyzer even
learned the lesson — a literal colour inside @media print is now understood as
an intentional paper choice, not a hard-coded-colour smell. Print stopped being
the awkward exception at the edge of the system and became what it should have
been all along: a theme.