diff --git a/docs/working/specs/2026-06-23-template-refactor-design.md b/docs/working/specs/2026-06-23-template-refactor-design.md new file mode 100644 index 0000000..078ad7a --- /dev/null +++ b/docs/working/specs/2026-06-23-template-refactor-design.md @@ -0,0 +1,115 @@ +# Template Refactor Design + +**Date:** 2026-06-23 +**Status:** Approved for implementation + +## Problem + +`trip.html.twig` (386 lines) mixes three concerns: 50 lines of stats computation, 80 lines of panel HTML, and 130 lines of map/JS logic. Changes to stats logic or panel structure require reading through all three to understand what's where. + +Four templates also duplicate the `map_entries` collection loop, and date range formatting is independently reimplemented in `story.html.twig` and `stories.html.twig`. Two inactive templates (`map.html.twig`, `feed-map.html.twig`) have bugs that would surface as soon as they're activated. + +## Goals + +- Reduce `trip.html.twig` from 386 to ~260 lines by extracting stats computation and panel HTML to macros +- Extract date range formatting to one macro used by both story templates +- Fix two latent bugs in inactive templates (wrong DOMContentLoaded timing, CSS not making it into ``) +- Zero visual change — this is structural only + +## Non-goals + +- Moving stats computation to PHP (performance difference at 60-80 entries is ~5-20ms, only on uncached loads; not worth a plugin) +- Extracting `map_entries` loops to a macro (Twig macros output HTML, not data; they can't return arrays) +- Changing any JS logic — DOMContentLoaded wrappers are structural fixes only (no logic changes) +- Fixing `dailies.html.twig`'s CDN PhotoSwipe — Milestone 1 leftover, inactive page, separate concern +- Any visual layout changes + +## Key constraint: Twig macros vs data + +Twig macros produce rendered HTML output, not return values. This means: +- **Stats + cycling panels** → good macro candidates (compute internally, render HTML) +- **Date range string** → good macro candidate (outputs text) +- **`map_entries` array** → cannot be a macro; each template's loop stays inline + +## Architecture + +### New macros + +**`templates/macros/stats.html.twig`** + +Signature: `stats_panel(journal_entries, page, journal_count, has_gpx)` + +Computes internally: `days_on_road`, `country_display[]`, `city_display[]`, `temp_min`, `temp_max`. Outputs the full `
` HTML with Twig-computed values baked in and JS placeholder IDs (`id="stat-distance"`) for the distance stat filled by JS after page load. + +**`templates/macros/cycling.html.twig`** + +Signature: `cycling_panel()` + +No computation needed — all values are JS placeholders (`id="cyc-distance"` etc.). Outputs `
` HTML. + +**`templates/macros/date-range.html.twig`** + +Signature: `format_date_range(start_date, end_date)` + +Condensed smart formatting extracted from `story.html.twig`: same month → `12–15 Jun 2026`; same year → `12 Jun – 3 Jul 2026`; different years → `28 Dec 2025 – 3 Jan 2026`. If `end_date` is empty or equals `start_date`, outputs a single date. Used by both `story.html.twig` and `stories.html.twig` (unifies the two currently divergent implementations). + +### Data flow in trip.html.twig + +```twig +{% import 'macros/stats.html.twig' as m %} +{% import 'macros/cycling.html.twig' as mc %} + +{# Data building stays inline (feeds both HTML and JS) #} +{% set journal_entries = ... %} +{% set gpx_urls = [...] %} +{% set gps_points = [...] %} {# for STATS_GPS in +``` + +### Latent bug fixes + +**Bug 1 — CSS not reaching `` in feed-map consumers** + +`feed-map.html.twig` calls `{% do assets.addCss('map.css') %}` inside `{% block content %}`, but `base.html.twig` renders `{{ assets.css() }}` in `` before content. The CSS is added too late and never appears in ``. + +Fix: remove `assets.addCss/addJs` from `feed-map.html.twig`. Add `{% block map_assets %}` to `dailies.html.twig` and `stories.html.twig` — this block is declared at line 11 of `base.html.twig`, before `{{ assets.css() }}`, so it registers correctly. + +**Bug 2 — `maplibregl is not defined` in `map.html.twig` and `feed-map.html.twig`** + +These templates call `new maplibregl.Map()` in inline `