Skip to content

feat(editorial): magazine-grade redesign with embedded WOFF2 fonts#49

Merged
ditvor merged 2 commits into
developfrom
claude/sweet-jackson-6e60ed
May 24, 2026
Merged

feat(editorial): magazine-grade redesign with embedded WOFF2 fonts#49
ditvor merged 2 commits into
developfrom
claude/sweet-jackson-6e60ed

Conversation

@ditvor
Copy link
Copy Markdown
Owner

@ditvor ditvor commented May 24, 2026

Summary

Rewrites the editorial style end-to-end. Same Style.editorial enum value, same renderer entrypoint, same NarrativeOutput contract — 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 refresh
  • chore(examples) — Wax Saints demo + supporting config

What 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/K jumps paragraph, ? opens a cheatsheet).

The log and encyclopedia styles are untouched.

Design substitutions vs. the original brief

  1. Source Serif 4 instead of Newsreader. Newsreader has no Cyrillic subset on Google Fonts; RU readers (a primary audience for this project per the project context) would have fallen back to system serif. Source Serif 4 carries the same opsz variable axis, italic + roman pair, and editorial feel with full Latin + Cyrillic.
  2. Static marginalia (not sticky). Sticky positioning interacts badly with headless PDF capture (Puppeteer, wkhtmltopdf) and with the user's primary share path of "save the page, send the file." Static keeps print-to-PDF and any headless renderer producing the same layout that's visible on screen.
  3. Photos at fixed structural positions, not per-chapter. The original brief assumed a Chapter[] data shape with per-chapter photo binding. Our NarrativeOutput has flat paragraphs — placing photos at the hero / mid-body / after-quote boundaries gives a strong visual rhythm without requiring a schema change.
  4. Facts panel shows what we have (Where, When, Distance, Ascent, Time, Summit) — REGION, WEATHER, PARTY would require schema changes.
  5. Share row keeps the three working actions (Copy link, WhatsApp, Save for Instagram) restyled to the new aesthetic. 9:16 stories / 4:5 poster / Reel · Ken Burns from the brief are not wired (new backend work, out of scope).

Fonts: embedded, ~476 KB on disk

Six WOFF2 subsets under templates/fonts/editorial/:

  • Source Serif 4 italic + roman variable axes × latin + cyrillic
  • JetBrains Mono variable × latin + cyrillic
  • LICENSE.md documents OFL 1.1 attribution and the Newsreader → Source Serif 4 substitution

Loaded into the template via a new _editorial_fonts() helper in trailstory.renderers.html (@lru_cache, only invoked when memory.style == Style.editorial). Embedded as base64 data URIs in @font-face declarations 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 ci passes locally and via the pre-push hook:

Check Result
ruff check . clean (per-file ignore for RUF001 under examples/**/*.py — the demo is dense with intentional Russian)
ruff format --check . 51 files formatted
mypy trailstory/ web/ no issues in 22 source files
pytest --cov-fail-under=80 301 passed, 95.19% coverage

Tests

  • tests/test_styles.py::test_editorial_style_keeps_existing_visual_identity renamed to test_editorial_style_keeps_magazine_visual_identity and rewritten to lock in the new structural markers (@font-face, Editorial Serif/Editorial Mono, --paper/--ink oklch tokens, .display / .eyebrow / .margin / .quote classes, three data-lang buttons).
  • tests/golden/test-render-editorial.html refreshed.
  • All other tests (301 total) untouched and passing.

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 .gitkeep placeholders) — personal photos extracted on the operator's machine never land in the public repo. The script regenerates them at runtime.

Test plan

  • make ci passes (301 / 301 tests, 95.19% coverage)
  • Web builder boots and the editorial style renders end-to-end via --fake-llm
  • All three languages (EN/RU/DE) cycle cleanly on screen + the keyboard shortcuts work
  • Carousel POST endpoint still wired (POST /memory/{slug}/carousel)
  • Photos appear exactly once each in the rendered HTML (no base64 duplication)
  • Marginalia does not overlap photos
  • log and encyclopedia styles render identically to before (verified by their own golden tests)
  • Eyeball the live URL once Fly redeploys

🤖 Generated with Claude Code

ditvor and others added 2 commits May 24, 2026 19:16
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>
@ditvor ditvor merged commit d62957f into develop May 24, 2026
5 checks passed
@ditvor ditvor deleted the claude/sweet-jackson-6e60ed branch May 24, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant