themes-editorial/RECOMMENDED_FONTS.md
Alex Dunmow a66113fce0 feat(editorial): Wave A theme lane (WO-TF-017) — personal essayist
Deliver the full Wave A contract for the editorial theme (personal
essayist / independent newsletter persona).

- 49 mandatory builtin overrides in templates/overrides/editorial/,
  re-skinned from the frozen builtins: hairline rules, small-caps labels,
  drop caps, narrow reading measure, oxblood accent. All content fields,
  provider fields, custom tags ({% img %}/{% form %}/{% signup_form %}),
  hx wiring, navbar drawer JS and auth/contact form wiring preserved
  verbatim; legacy button/card field reads reconciled (link/style/label,
  media/text).
- 7 page templates (default, full-width, landing, article, blog-index,
  contact, auth); article is the reading centrepiece with drop-cap opener,
  marginalia rail and a CSS-only reading-progress underline. All set the
  html.dark class and route body font through var(--font-body).
- Block rework: masthead/byline/pullquote/section_label/colophon deleted
  (now overrides of navbar/post-metadata/quote/divider/footer). Persona
  blocks: dropcap_intro, marginalia, reading_list, field_notes.
- 4 presets, all mode "both", book-paper light + sepia-on-charcoal dark.
- Bundled Playfair Display + Source Serif 4 (OFL, latin subset) in
  assets/fonts with license record; fonts.json populated; every
  font-family via var(--font-heading|body|mono, fallback).
- Themed email-safe wrapper (tables, inline styles, literal fonts).
- Master pages + seed demo site (home, about, blog with 3 essays,
  contact with form + data table + row_inserted email-admins workflow,
  login) in essayist voice.
- required_icon_packs=["lucide"]; RECOMMENDED_FONTS/ICONS updated.

Motion tier: CSS-first (progress underline, marginalia fade-in;
prefers-reduced-motion honored). Verified: make + ninja plugin verify +
check-safety all exit 0. Visual quality UNVERIFIED until Wave B.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:22:07 +08:00

2.4 KiB

Editorial — fonts

This theme bundles a Latin subset of two open-licence (SIL OFL 1.1) serif families, so it looks correct the moment it is activated — no admin setup required. The bundled faces are declared in fonts.json and land in the typography picker as source=template rows.

Slot Bundled family Weights / styles Role
Heading Playfair Display 700 normal, 900 italic Didone display face for the masthead wordmark and headlines
Body Source Serif 4 400 normal, 400 italic Long-read body type; italic for marginalia and captions

The bundled woff2 files and their licence record live in assets/fonts/ (OFL.txt). Every font-family in the theme is routed through var(--font-heading, ...), var(--font-body, ...) and var(--font-mono, ...) with a fallback stack, so the layout is usable before any font is assigned and the admin font picker stays in full control.

Assigning the bundled faces

  1. In the BlockNinja admin, open Settings → Theme → Typography.
  2. In the Bundled tab, assign Playfair Display to the Heading slot.
  3. Assign Source Serif 4 to the Body slot.
  4. Leave Mono as the system default, or assign JetBrains Mono from the Google Fonts tab — it is used for the masthead kicker, datelines and the colophon ISSN.

Optional upgrades (Google Fonts tab)

  • Spectral (google:Spectral) — a refined small-caps serif. Assign it to the body slot if you want the small-caps section labels to carry true small-caps rather than the synthesized font-variant: small-caps fallback.
  • Newsreader (google:Newsreader) — an alternative reading serif for the body slot; swaps in without any CSS change via var(--font-body).
  • JetBrains Mono (google:JetBrains Mono) — for the Mono slot (kicker / dateline / ISSN).

Bundling a commercial display face (out of scope for Wave A)

If the brand locks in a licensed display face (e.g. Migra or PP Editorial New) in place of Playfair Display, licence and subset the woff2 set, drop it in assets/fonts/, and add the family + variants to fonts.json. The CSS does not change — the same var(--font-heading) consumer picks up whichever face an admin assigns.