feat(editorial): magazine-grade redesign with embedded WOFF2 fonts#49
Merged
Conversation
Full rewrite of templates/styles/editorial.html.j2. Same Style enum,
same renderer entrypoint, same NarrativeOutput contract — new visual
identity:
• oklch paper/ink tokens; no radius, no gradient, no accent colors
• Source Serif 4 italic-top / roman-bottom display title (variable
opsz axis), drop cap on the first paragraph, mono "eyebrows"
• Two-column desktop grid with marginalia sidebar (THE FACTS /
THE PATH / ELEVATION), statically positioned for PDF-friendliness
• Photo float-right hero, mid-body full-bleed, after-quote variants
(deduplicated — each base64 photo embedded exactly once)
• Reading-progress bar, 3-way EN/RU/DE toggle (←/→), paragraph
nav (J/K), cheatsheet (?), localStorage persistence, soft fade
swap with paragraph-anchored scroll preservation, photo blur-up
• Print rules collapse to single-column inline marginalia
Fonts: six WOFF2 subsets under templates/fonts/editorial/ (Source
Serif 4 italic + roman variable × latin + cyrillic + JetBrains Mono
variable × latin + cyrillic, ~476 KB total). Loaded into the
template context via a new _editorial_fonts() helper in
trailstory.renderers.html (lru_cache, editorial-only) and embedded
as base64 data URIs — the rendered page works fully offline, honoring
ADR-001's self-contained guarantee.
Newsreader (the family from the original brief) ships no Cyrillic
on Google Fonts; Source Serif 4 stands in with the same opsz axis,
italic + roman pair, and full Latin + Cyrillic coverage. License
notes in templates/fonts/editorial/LICENSE.md.
The log and encyclopedia styles are untouched.
test_styles.py's "keeps existing visual identity" test is renamed
and rewritten to lock in the new markers (font names, oklch tokens,
.display / .eyebrow / .margin / .quote classes, 3 data-lang buttons).
Editorial golden refreshed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
examples/wax_saints/render.py extracts photos from a local source HTML, deduplicates by SHA-256 (the source reuses the same image across hero + photo card slots), fabricates a small GPX track along the Isar, and renders the editorial template against the example's tri-lingual text. Lets us dogfood template changes without paying for a live LLM call. The photos/ directory is gitignored (examples/*/photos/* in .gitignore, with a .gitkeep placeholder) — personal images extracted on the operator's machine must not land in a public repo. Anyone reproducing the demo provides their own source HTML. pyproject.toml gains a per-file ruff ignore for RUF001 under examples/**/*.py — the demo is dense with Russian narrative and ruff would otherwise flag every Cyrillic character as an "ambiguous confusable." CHANGELOG updates: editorial redesign + embedded fonts + static marginalia + Wax Saints demo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewrites the
editorialstyle end-to-end. SameStyle.editorialenum value, same renderer entrypoint, sameNarrativeOutputcontract — the template gains a magazine-grade visual identity that survives offline delivery (the project's primary share path).Two commits, bisectable:
feat(editorial)— fonts + renderer + template + tests + golden refreshchore(examples)— Wax Saints demo + supporting configWhat the user sees
A new editorial layout: oklch paper/ink tokens (no radius, no gradient, no accent colors), Source Serif 4 italic-top / roman-bottom display title, drop cap on the first paragraph, two-column desktop grid with a marginalia sidebar (THE FACTS / THE PATH / ELEVATION), photo float-right hero + mid-body full-bleed + after-quote variants, hairline rules, mono "eyebrows," reading-progress bar, 3-way EN/RU/DE toggle with keyboard shortcuts (
←/→cycles language,J/Kjumps paragraph,?opens a cheatsheet).The
logandencyclopediastyles are untouched.Design substitutions vs. the original brief
opszvariable axis, italic + roman pair, and editorial feel with full Latin + Cyrillic.Chapter[]data shape with per-chapter photo binding. OurNarrativeOutputhas flat paragraphs — placing photos at the hero / mid-body / after-quote boundaries gives a strong visual rhythm without requiring a schema change.Fonts: embedded, ~476 KB on disk
Six WOFF2 subsets under
templates/fonts/editorial/:LICENSE.mddocuments OFL 1.1 attribution and the Newsreader → Source Serif 4 substitutionLoaded into the template via a new
_editorial_fonts()helper intrailstory.renderers.html(@lru_cache, only invoked whenmemory.style == Style.editorial). Embedded as base64 data URIs in@font-facedeclarations so the rendered HTML works fully offline — honors ADR-001's "single self-contained HTML, no CDN" guarantee.Page size impact: ~180 KB → ~820 KB per memory (with 7 photos). Loads instantly on WiFi, comfortable over 4G, fully offline.
CI
make cipasses locally and via the pre-push hook:ruff check .examples/**/*.py— the demo is dense with intentional Russian)ruff format --check .mypy trailstory/ web/pytest --cov-fail-under=80Tests
tests/test_styles.py::test_editorial_style_keeps_existing_visual_identityrenamed totest_editorial_style_keeps_magazine_visual_identityand rewritten to lock in the new structural markers (@font-face,Editorial Serif/Editorial Mono,--paper/--inkoklch tokens,.display/.eyebrow/.margin/.quoteclasses, threedata-langbuttons).tests/golden/test-render-editorial.htmlrefreshed.Wax Saints demo
examples/wax_saints/render.py— standalone dogfood script. Extracts photos from a local source HTML, deduplicates by SHA-256 (the source HTML reuses some images), fabricates a small GPX track along the Isar, and renders the editorial template against tri-lingual example text. Useful for previewing template changes without paying for a live LLM call.examples/*/photos/*is gitignored (with.gitkeepplaceholders) — personal photos extracted on the operator's machine never land in the public repo. The script regenerates them at runtime.Test plan
make cipasses (301 / 301 tests, 95.19% coverage)--fake-llmPOST /memory/{slug}/carousel)logandencyclopediastyles render identically to before (verified by their own golden tests)🤖 Generated with Claude Code