ADR-043: The site wears the project’s brand: artwork by path, favicon rasterized at build time
Status Active · Filed 2026-08-10 · Issue #13 · Influenced by ADR-042
Context
ADR-042 put the record on the web wearing Quartz’s clothes: the
generator’s off-white, the generator’s slate-blue links, and the word luria
set in the sidebar as plain text. The project has a brand kit — a
brain-shelled snail, a wordmark, two colours and a documented ink variable —
and none of it reached the page. The site’s own front page carried the lockup
only because the README happened to embed it.
A favicon is the sharpest case. It is the one piece of a site that appears
somewhere the site does not control, next to a dozen other tabs, and Quartz
serves whatever sits at quartz/static/icon.png — which, unaddressed, is
Quartz’s icon on someone else’s record.
Decision
[luria.site] gains four branding keys — icon, logo, logo_dark,
and a theme table — all optional, all pointing at files the project already
maintains. Two of them carry a constraint worth stating.
The favicon is rasterized during the build, and nothing derived from the
artwork is committed. luria site copies the icon source verbatim;
actions/site renders it to static/icon.png with sharp, which is
Quartz’s own dependency, so this costs no new install and no new system
package. A project therefore points at the vector master it maintains, and
the rendered icon exists only inside a build. The alternative — commit a PNG
— is a hand-maintained projection of a source of truth, and
DP-3 is unambiguous about the rate at
which those go wrong.
The corollary, learned by rendering it: an icon’s first reader is a
rasterizer, and a rasterizer resolves neither a CSS custom property nor a
media query. Artwork that inverts itself through --luria-ink renders as a
solid black square. So an icon carries literal fills, with the dark theme as
an override on top — a rule that applies to the artwork, not to Luria, and is
written down in the icon file itself.
The logo is baked once per theme. Not because artwork cannot invert
itself, but because whether it does is a property of the reader’s browser:
Quartz declares color-scheme per theme, and a browser that carries that
into an embedded SVG resolves the artwork’s own prefers-color-scheme rules
against the site’s toggle — measured, and what Chromium does — while one that
doesn’t resolves them against the operating system, which the toggle has
nothing to do with. Two variants make the answer the same everywhere, and are
the only way to theme a logo that has no media query at all. An SVG that
exposes --luria-ink is re-inked automatically; one that doesn’t is used as
it stands, so the accommodation costs nothing to a project that never heard
of it.
The palette merges over the generator’s defaults by name, per mode, and an unknown colour name is refused with the known ones listed. A palette key silently ignored is a project wondering why its brand didn’t take.
Luria ships none of this as a default. The package’s defaults are
Quartz’s palette and Quartz’s icon; this project’s brand lives in this
project’s luria.toml.
Alternatives considered
- Ship Luria’s brand as the default. Every adopter’s record would arrive wearing a snail. A package that hands out its own identity as a default is handing out a costume nobody asked for, and the ones who notice have to work out which knob turns it off.
- Commit a rendered
icon.pngbeside the vector. One less moving part in CI, and a derived file that drifts the first time the mark is redrawn — silently, because nothing compares them. DP-3 rung 1 is available here, so taking rung 3 would need an argument nobody has. - Rasterize in
luria siteitself. Python has no SVG rasterizer in the standard library, so this means a new dependency (cairosvgand its system libraries) on every install of a package whose other nine commands never touch an image.sharpis already there, in the one place the rendering is needed. - Compose the icon inside Luria — take a wordmark, crop it, pad it, round the corners. Tempting, since this project’s own mark needed exactly that treatment. It is design work, it does not generalize past one logo’s proportions, and it would put the package in the position of arguing about taste. The composition happened once, in the brand kit, where a designer can disagree with it.
- One self-inverting logo file. Correct in the browsers that propagate
color-schemeinto images, which is most of them now. It fails silently in the others, and the failure is a logo that vanishes into the page — the kind of defect nobody reports because it looks like nothing at all. - A custom Quartz component for the sidebar. More control over the
markup, and a much deeper coupling to the generator’s component API than a
stylesheet rule needs.
custom.scssis a documented extension point; replacing the title’s background is the smallest thing that brands it.
Consequences
- The published record looks like the project: its paper, its ink, its lockup in the sidebar, its slug in the browser tab.
- Adopters get a documented brand surface, and one they can ignore entirely — every key is optional, and the site without them is exactly the site ADR-042 shipped.
- The staging directory now has more than files in it. The guard that
ties
stagetoactions/sitehad to widen to directories, and to matchcpcommands rather than mentions of a name — asking whetherstaticappeared anywhere in the action passed on three unrelated lines. - A palette is only as good as its contrast, and nothing checks it. The numbers were computed by hand for this one (every text pair above 6.5:1) and are recorded in the devlog; a project that overrides carelessly gets no warning. That is a report this record does not yet have.