Reference
Design system
Every ratio was computed with tools/a11y/contrast.py against all three grounds.
This page is noindex: it documents the template for whoever builds with it.
Why the accent is stencil purple
A thermal stencil is transferred to the skin in purple before any needle is switched on, and
it is the last stage at which placement can still change. The whole page is that stage, so
that is its colour. #762BAE sits at hue 274°, the emptiest band in the library's
117-accent corpus — two entries in 270–285° — and is ΔE 16.4 from the nearest of them. Day 55
(tattoo-studio) is ink black and gold; nothing here is adjacent to it.
Headings are 400, on purpose
Antic Didone ships a single weight. Synthesising a bold on a high-contrast didone thickens the thin strokes, which is precisely the thing the page spends eight zones telling you not to do to a drawing. Hierarchy comes from size, and the UI face carries the weight instead.
The split screen is nine lines of CSS
.split is a two-column grid with align-items: start, and the figure
pane is position: sticky. That is the entire mechanism: the figure pins while
its sibling column scrolls and releases at the end of the section, with no JS and no scroll
listener. Below 960px the grid collapses to one column and the pane stops pinning — a real
degradation, said out loud in the figure's caption rather than hidden.
The flash stroke widths are derived, not chosen
Each design is stroked from the line-weight floor of the zone it was drawn for
(strokeFor() in index.astro), so the ankle and hand pieces are
visibly heavier than the outer-arm ones on screen for the same reason they are heavier on
skin. Change a zone's floor in config.ts and the artwork changes with it;
tests/smoke.spec.ts asserts the on-screen order matches the table.
What the JSON-LD refuses to say
No Offer, no price, no priceRange — even though the
page states an hourly rate and a deposit in plain text. Schema's price properties describe a
purchasable thing at a settled price, and what is sold here is a session whose length is set
by the placement, the floor that zone holds and how one person's skin takes ink;
priceRange would present the most variable number on the page as the most
certain. Also withheld: aggregateRating, because there are no reviews, and
image, because the template contains no photographs at all — which is the
argument, not an omission.
Tokens
| Token | Value | Notes |
|---|---|---|
--color-bg | #F3EFF7 | Tracing paper — the stage before ink |
--color-bg-alt | #E5DEEE | Banding |
--color-surface | #FFFFFF | Cards, flash sheets, the body map |
--color-text | #1B1220 | 16.03 / 13.88 / 18.20:1 · AAA |
--color-text-muted | #5A5064 | 6.69 / 5.79 / 7.59:1 · AA |
--color-border | #726A7C | 4.55 / 3.94 / 5.16 — non-text only |
--color-accent | #762BAE | Stencil purple — 6.78 / 5.87 / 7.70:1 · AA |
--stencil | #9B6BC8 | Marker fill, zones that fight back. Ink on it is 4.62:1 |
--stencil-soft | #D9C7EC | Marker fill, zones that behave. Ink on it is 11.55:1 |
--color-band | #1B1220 | The ageing section inverts |
--color-on-band | #F3EFF7 | 16.03:1 on band · AAA |
--color-on-band-muted | #A79FB0 | 7.13:1 on band · AA |
--color-accent-on-band | #C79BF0 | 8.12:1 on band · AAA |