# 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 |