Plate: deterministic project marks
The generator behind every mark on this site. One module per project, seeded by name: the seed varies how many and how heavy, never what. Same input, same bytes, every time.
Deterministic project marks for nullsilver. One generator per project, seeded by name, emitting SVG for the Svelte site and baked PNGs for Hugging Face and GitHub. Marks only — text, cards and layout are assembled elsewhere.
The name: a plate is both the thing you print from and the word for a figure in a paper. Both readings are the intent — these are figures, not decoration.
Use
pip install -e .
python -m plate build --tokens ../nullsilver.com/src/app.css
python -m plate build --ink "#dedad2" --paper "#0a0a0a"
python -m plate params remora
python3 tests/test_plate.py
Colour only ever applies to baked pixels. --tokens reads --ns-silver and
--ns-black out of the site's stylesheet and follows @import, so the entry
sheet works as well as tokens.css — point it at the real file and the PNGs
cannot drift from the site. --ink and --paper override it. Light output is
the inversion, matching how the site's accent already works.
Outputs land in out/:
| Path | For |
|---|---|
svg/<seed>.svg |
Svelte inlines this; currentColor picks up the theme |
svg/<seed>.min.svg |
coarse variant for favicons and small use |
png/<seed>@<w>.png |
transparent, for READMEs and HF |
png/<seed>@<w>-dark.png |
silver on black |
png/<seed>@<w>-light.png |
black on silver |
<mark>-contact-{dark,light}.png |
the sheets you judge the mark by |
In Svelte
Inline the SVG so currentColor resolves against your theme. Serving it through
<img> breaks that and costs you a request per mark.
<script>
import Remora from '$lib/marks/remora.svg?raw';
</script>
<span class="mark">{@html Remora}</span>
<!-- `color` is load-bearing: bars are fill="currentColor". Without it they
inherit the CSS initial value and render black. -->
<style>
.mark { color: var(--color-text); display: inline-flex; }
.mark :global(svg) { width: 52px; height: auto; }
</style>
Copy out/svg/*.svg into src/lib/marks/ as a build step, or commit them —
they are deterministic, so the diff is empty unless a generator changed.
The marks
| Seed | Project | Grammar |
|---|---|---|
remora |
calls large models only where the work needs them | a field ramping hairline to heavy, with a disc knocked out of the dense end |
sieve |
scores recall on only the spans an answer rests on | a uniform mesh of dashes, crossed by a few solid spans |
budget |
a loop spending against a deadline that does not move | runs decaying toward a heavy wall they never reach |
plate |
this generator | an engraved block and its identical impressions |
sieve, budget and plate were built for placeholder projects on the site,
to see the home page carrying four different figures rather than one repeated.
They skipped the sign-off gate in specs/new-mark.md on those grounds; if any of
those projects becomes real, run its brief through the spec properly.
Adding a mark
See specs/new-mark.md. The short version: one module in plate/marks/,
subclass Mark, seed quantity and weight but never shape, and implement the
coarse variant properly or the mark dies below 48px.
Design invariants
- Stroke only. One colour. No gradients, no fills that aren't load-bearing.
- The seed varies how many and how heavy, never what.
- Every mark encodes its project's mechanism, not its name.
- Negative space is cut by stopping strokes at the edge of the void, never by
painting over them with a background colour. The background is not ours to
assume —
currentColoris the only colour a mark knows, and no hex may appear in emitted SVG. There is a test for this. - Heavy elements are filled rects via
bar(), not thick strokes. A round linecap adds half a stroke-width of radius at each end, so a 6-unit stroke gains a 3-unit bulge and any silhouette cut from it goes soft.CORNERsets the house radius as a fraction of a bar's short dimension; passcorner=0for square.