From dc8b7f58d20bd66ce52e11b6038ea50ed31b2273 Mon Sep 17 00:00:00 2001 From: Mischa Date: Fri, 19 Jun 2026 15:07:10 +0200 Subject: [PATCH] docs: add home page & content flow design spec --- .../2026-06-19-home-and-trip-pages-design.md | 226 ++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-19-home-and-trip-pages-design.md diff --git a/docs/superpowers/specs/2026-06-19-home-and-trip-pages-design.md b/docs/superpowers/specs/2026-06-19-home-and-trip-pages-design.md new file mode 100644 index 0000000..123f6ca --- /dev/null +++ b/docs/superpowers/specs/2026-06-19-home-and-trip-pages-design.md @@ -0,0 +1,226 @@ +# Home Page & Content Flow Design Spec + +**Goal:** Replace the redirect-based home page with a real home page showing the active trip's feed and map side by side, add a proper past-trips archive, enrich the trip page with a sticky sidebar index, and introduce story cards into all feeds. + +**Architecture:** Pure Twig + CSS changes on top of the existing Grav stack. The home page is a new Grav page (`00.home/home.md`) with a new `home.html.twig` template. Feeds (home + dailies) are extended to merge journal entries and story entries into one chronological collection, with stories rendered as visually distinct cards. No new plugins, no build pipeline. + +**Tech Stack:** Grav CMS (PHP/Twig), Vanilla CSS, Leaflet.js (already loaded in `dailies.html.twig`) + +--- + +## Global Constraints + +- All changes in `user/` — commit with `git -C user` +- No new Grav plugins +- No JS framework — all interactivity is vanilla JS +- No build pipeline — CSS shipped as plain files +- Existing token names in `tokens.css` must not change +- Theme directory: `user/themes/intotheeast/` +- Active trip slug: `config.site.active_trip` (set in `user/config/site.yaml`) +- `system.yaml` `home.alias` redirect must be removed — `/` becomes a real page +- `config.site.active_trip` in `site.yaml` must always be set to a trip slug (even between trips, point it at the last trip) — the home page template has no fallback if this value is empty + +--- + +## 1. URL Structure + +| URL | Page file | Template | +|---|---|---| +| `/` | `user/pages/00.home/home.md` | `home.html.twig` (new) | +| `/trips` | `user/pages/01.trips/trips.md` | `trips.html.twig` (update existing) | +| `/trips//` | existing | `trip.html.twig` (update existing) | +| `/trips//dailies` | existing | `dailies.html.twig` (update existing) | + +**system.yaml change:** Remove `home: alias: /trips/japan-korea-2026/dailies`. Set `home: alias: /` (or remove the alias entirely so Grav serves the `00.home` page at `/`). + +--- + +## 2. Home Page (`/`) + +### Layout + +Two-column CSS grid on desktop. Map left (~45%), entry feed right (~55%). + +``` +┌─────────────────────────────────────────────────────────┐ +│ [Trip name] · 31 journal entries · 4 stories │ +├────────────────────────┬────────────────────────────────┤ +│ │ [story card] │ +│ Leaflet map │ [journal card] │ +│ (sticky, │ [journal card] │ +│ 45% width) │ [story card] │ +│ │ ... │ +└────────────────────────┴────────────────────────────────┘ +``` + +- Map is `position: sticky; top: 0; height: 100vh` +- Entry feed is scrollable, sorted descending by date +- Feed contains both journal entries and story entries merged (see §5) + +### Data + +```twig +{% set slug = config.site.active_trip %} +{% set trip = grav.pages.find('/trips/' ~ slug) %} +{% set dailies = grav.pages.find('/trips/' ~ slug ~ '/dailies') %} +{% set stories_page = grav.pages.find('/trips/' ~ slug ~ '/stories') %} +{% set journal_entries = dailies ? dailies.children.published().order('date', 'desc') : [] %} +{% set story_entries = stories_page ? stories_page.children.published() : [] %} +{# merge and sort handled in template — see §5 #} +``` + +### Map + +Reuse the existing Leaflet setup from `dailies.html.twig` (`feed-map`). Markers come from journal entries with `lat`/`lng` in frontmatter. GPX route line loaded from trip page media if present (same pattern as `map.html.twig`). Clicking a marker scrolls to that entry card in the feed (use `data-entry-id` on cards + `scrollIntoView`). + +### Mobile + +Stack vertically: map on top at `height: 40vh`, feed below. No hamburger needed — simpler than the dedicated map page. + +--- + +## 3. Past Trips Archive (`/trips`) + +Update `trips.html.twig`. Show each trip as a card, sorted newest first. + +Each card contains: +- Trip title (links to `/trips//`) +- Date range: `date_start` – `date_end` from trip page frontmatter (show "Ongoing" if no `date_end`) +- Entry count: journal entries + story entries counted separately + +```twig +{% set journal_count = grav.pages.find(trip.route ~ '/dailies').children.published()|length %} +{% set story_count = grav.pages.find(trip.route ~ '/stories').children.published()|length %} +``` + +Display: **31 journal entries · 4 stories** + +The active trip appears as the first card. No special treatment needed beyond chronological ordering — it naturally sits at the top. + +--- + +## 4. Trip Page (`/trips//`) + +Update `trip.html.twig`. Current state: shows title, dates, nav links, 3 recent entries. Target state: + +### Header (update existing `.trip-hero`) +``` +Japan & Korea 2026 +Jun 2026 – Aug 2026 · 31 journal entries · 4 stories +``` + +Add entry counts below the date line (small, secondary text). + +### Two-column layout + +Add a right sidebar alongside the existing content: + +``` +┌──────────────────────────────────┬──────────────────────┐ +│ [full chronological feed] │ Journal │ +│ (centered, existing max-width) │ Jun 19 Kyoto │ +│ │ Jun 18 Osaka │ +│ │ ... │ +│ │ │ +│ │ Stories │ +│ │ The night train │ +│ │ First ramen │ +└──────────────────────────────────┴──────────────────────┘ +``` + +- Right sidebar: `position: sticky; top: 1rem` +- Two sections: **Journal** (list of entry titles as jump-links via `#entry-`) and **Stories** (same) +- Each item in the sidebar is a jump-link to `#entry-` anchor on the feed card +- Feed comes from `dailies.children` + `stories.children` merged (see §5) +- On mobile: sidebar collapses to hidden (toggle-able or just hidden — defer this decision to implementation) + +### Remove the current "Recent entries" section + +The right-sidebar index replaces it. The full merged feed is the main content. + +--- + +## 5. Story Cards in Feeds (home + trip page) + +Feeds in both `home.html.twig` and `trip.html.twig` show a merged chronological list of journal entries and story entries. + +### Merging collections in Twig + +Grav doesn't natively merge two page collections and sort them. Use a Twig loop to build a combined array: + +```twig +{% set all_items = [] %} +{% for e in journal_entries %} + {% set all_items = all_items|merge([{'type': 'journal', 'page': e, 'date': e.date}]) %} +{% endfor %} +{% for s in story_entries %} + {% set all_items = all_items|merge([{'type': 'story', 'page': s, 'date': s.date}]) %} +{% endfor %} +{# Sort descending by date #} +{% set all_items = all_items|sort((a, b) => a.date < b.date ? 1 : -1) %} +``` + +### Journal card (existing format, unchanged) + +```html +
+ +
+``` + +Add `id` and `data-lat`/`data-lng` attributes for sidebar jump-links and map sync. + +### Story card (new) + +```html + +``` + +**Visual treatment:** `entry-card--story` gets a teal left border (3px, `var(--color-accent)`) and no excerpt text. The `✦ Story` badge is small-caps, accent color. + +### Story page (full-screen) + +Story pages (`/trips//stories/`) use `stories.html.twig` (already exists). That template should: +- Override `{% block nav %}` to render **only** a fixed escape link — not an empty block, not the global nav +- Escape link: `← Back` fixed top-left, links to `page.parent.parent.url` (the trip page) + +Implementation of the Snowfall-style scroll-snap interior is **deferred to Milestone 3** — this spec only covers the story card in the feed and the escape link on the story page. + +--- + +## 6. Navigation + +Update `base.html.twig` nav. Current: single "Journal" link pointing to active trip dailies. New: + +- **Home** → `/` +- **Past Trips** → `/trips` + +The per-trip sub-nav (Journal / Map / Stats / Stories) stays on the trip page — it is not in the global nav. + +--- + +## 7. Files Changed + +| File | Change | +|---|---| +| `user/pages/00.home/home.md` | **Create** — new home page, `template: home` | +| `user/themes/intotheeast/templates/home.html.twig` | **Create** — side-by-side map + feed | +| `user/themes/intotheeast/templates/trips.html.twig` | **Update** — trip cards with counts | +| `user/themes/intotheeast/templates/trip.html.twig` | **Update** — counts in header, two-column + sidebar | +| `user/themes/intotheeast/templates/dailies.html.twig` | **Update** — merge stories into feed, story cards, add `id`/`data-` attrs | +| `user/themes/intotheeast/templates/stories.html.twig` | **Update** — add escape link, remove global nav | +| `user/themes/intotheeast/templates/partials/base.html.twig` | **Update** — new nav links | +| `user/themes/intotheeast/css/style.css` | **Update** — home layout, story card styles, sidebar styles | +| `user/config/system.yaml` | **Update** — remove `home.alias` redirect |