ReferenceDesignEvergreenupdated 2026.085 min read
Reference · the token layer

Two-layer palette and themes

styles/tokens.css holds every color the stylesheets and components use — the one exception is a domain's tone pair, which a site's registry supplies as data. It is written in two tiers — a raw palette of paper-and-ink pigments, and semantic tokens built on it — and the semantic tier comes in two contexts, because a wiki has two kinds of page: the column you read, and the shelf you browse.

one palette · two contexts · both themes hand-set · every text value measured for AA

PART IThe tiers

§1.1Tier 1: pigments

The raw palette is a painter's box, theme-invariant, named --p-*. Papers: the browse ground 纸 and its card face 页, the reading ground 素 (a notch cooler, easier on the eyes over an hour) and its soft surface 帛, the white card, the code and math grounds. Inks: a warm set for the shelf and a neutral set for the column, each in three strengths. Binding: the shelf rule, the pill border, the two hairlines. Pigments, the colors that carry meaning:

tokens.css (tier 1, the pigments)
--p-zhu: #7d3a3a; /* 朱 · wine red — browse accent, the site's mark */
--p-shi: #b6552e; /* 石 · burnt orange — reading accent: links, numbers, kickers */
--p-shi-text: #8f3f1f; /* 石 · small-text tier (AA on its own tint) */
--p-zhe: #c08a2c; /* 赭 · ochre — lines and borders */
--p-dai: #2a6f6b; /* 黛 · teal — secondary content accent */
--p-zi: #6b4ec4; /* 紫 · violet — "system" semantics */

Plus a night set for the dark edition: dusk grounds, moon-white inks, and a brightened twin of every pigment.

§1.2Two contexts, one palette

Tier 2 is what the component layer consumes, and it has two faces:

ContextTokensGroundInkAccentWhere
reading column--color-*#fbf9f4neutral #1f2024石 burnt orangenote pages, every content component, base.css
browse shelf--wb-*#faf6ecwarm #2b2622朱 wine redlanding and facet pages, the wiki components, browse.css

The column is the quieter of the two — you read it for an hour. The shelf is warmer and set in a display serif — you glance at it and pick. A page enters the shelf context with one class, wb-root, on its body; inside it the chrome tokens (--color-bg, --color-accent, …) are re-mapped onto the shelf's, so a nav bar written once sits correctly on both grounds — and so the taxonomy atoms (kind badge, status dot, domain chip), which are written against --color-*, read as part of the column on a note page and as part of the shelf on a browse page. The component layer may consume only tier 2base.css, browse.css and every component; no rule contains a raw color value, and nothing falls back to one. That is the package's hard discipline.

PART IIIdentity and discipline

§2.1How a site customizes identity

Override tier 1. Swap the pigments for your own and both contexts and the whole component layer follow. Each pigment is a day/night pair: the light theme reads --p-shi and --p-zhu, the dark theme reads their night twins --p-shi-n and --p-zhu-n — an identity override sets both members, or dark keeps the default pigments:

your site.css
:root {
  --p-shi: #b03a48; /* your reading accent */
  --p-shi-text: #93303c; /* its small-text step is darkened separately, see below */
  --p-zhu: #3b4a7a; /* your shelf accent and mark */
  --p-shi-n: #e0919b; /* the night twins, brightened for the dusk grounds */
  --p-zhu-n: #9fb0e4;
}

Re-mapping tier 2 onto your own palette is equally supported when your design system already has one — point --color-* and --wb-* at your variables. This garden deliberately overrides nothing: what you are reading is the package's bare face, the same one paper-and-ink argues for.

§2.2Contrast is audited

Every text-tier value is vetted for WCAG AA on every surface it sits on: small text starts at 4.5:1; an accent used as small text gets an independently darkened text step (--p-shi-text, --p-zhe-text), because a 12px label in the accent color usually sits on the accent's own tint; the faint inks are set at the darkest shade that still reads as faint — #6a6d74 on the reading ground is 4.9:1, and still 4.5:1 on the soft surface; the code ground is a notch lighter than the paper so the syntax theme's faintest token (comments) clears 4.5:1; the dark edition carries its own brightened set, measured against the night grounds. The package ships the measurement as a tool: scripts/contrast_probe.mjs renders the demo in a real browser, both themes and two widths, samples the ground under every text run from pixels and reports every run under the bar (checks#the-contrast-probe). Before changing any value, know every surface it sits on.

§2.3Light is the identity

The theme deliberately does not read prefers-color-scheme: a page must never flip its own theme under the reader. Dark activates only through [data-theme='dark'], toggled by the site's own switch (the sun/moon button at the right of this site's nav bar; the choice is stored under the inkstone-theme key in localStorage and replayed by the ThemeInit inline script before first paint, so dark-theme readers never see a cream flash). Dark is not an inversion but a second hand-set edition: dusk grounds, moon-white inks, re-brightened pigments — paper-and-ink#dark-is-a-second-edition-not-an-inversion.

§2.4Fonts live here too

Four stacks. --font-display is the shelf's face — an optical-size serif (Source Serif 4, with Noto Serif SC for Hanzi) for mastheads and shelf titles; this demo self-hosts it through @fontsource, and a site that doesn't falls back to the reading face. --font-body is the reading column's — a system serif stack led by Charter and Iowan Old Style, with the display face stepping in where they are absent. --font-ui is Inter (Noto Sans SC for Hanzi) for headings, chrome and small print. --font-mono is a fixed-charset subset of Maple Mono CN (ASCII + box-drawing + the common-3500 Hanzi + the union of existing content): Latin at 0.6em, Hanzi at 1.2em — a Hanzi is exactly two character cells wide, so box-drawing diagrams align even in mixed Chinese/Latin text. The subset recipe and regeneration script ship with the package (fonts/build_font_subset.py); when content needs glyphs beyond coverage, re-run it and commit the woff2. The code frames are set entirely in the subset.

Titles, sections and body text, in this language.
    ↑↓ · Enter · Escastro-inkstone