10 KiB
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 withgit -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.cssmust not change - Theme directory:
user/themes/intotheeast/ - Active trip slug:
config.site.active_trip(set inuser/config/site.yaml) system.yamlhome.aliasredirect must be removed —/becomes a real pageconfig.site.active_tripinsite.yamlmust 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/<slug>/ |
existing | trip.html.twig (update existing) |
/trips/<slug>/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
{% 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/<slug>/) - Date range:
date_start–date_endfrom trip page frontmatter (show "Ongoing" if nodate_end) - Entry count: journal entries + story entries counted separately
{% 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/<slug>/)
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-<slug>) and Stories (same) - Each item in the sidebar is a jump-link to
#entry-<slug>anchor on the feed card - Feed comes from
dailies.children+stories.childrenmerged (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:
{% 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)
<article class="entry-card" id="entry-{{ item.page.slug }}" data-lat="{{ item.page.header.lat }}" data-lng="{{ item.page.header.lng }}">
<!-- existing card markup -->
</article>
Add id and data-lat/data-lng attributes for sidebar jump-links and map sync.
Story card (new)
<article class="entry-card entry-card--story" id="entry-{{ item.page.slug }}">
<a class="entry-card-inner" href="{{ item.page.url }}">
{% if hero %}
<div class="entry-card-photo entry-card-photo--story">
<img src="{{ hero.cropResize(720, 405).url }}" alt="{{ item.page.title }}" loading="lazy">
</div>
{% endif %}
<div class="entry-card-body">
<span class="story-badge">✦ Story</span>
<h2 class="entry-title">{{ item.page.title }}</h2>
</div>
</a>
</article>
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/<slug>/stories/<story-slug>) 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:
← Backfixed top-left, links topage.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 |