diff --git a/docs/superpowers/specs/2026-06-21-homepage-redesign.md b/docs/superpowers/specs/2026-06-21-homepage-redesign.md new file mode 100644 index 0000000..4b81006 --- /dev/null +++ b/docs/superpowers/specs/2026-06-21-homepage-redesign.md @@ -0,0 +1,205 @@ +# Homepage Redesign Spec + +**Date:** 2026-06-21 +**Goal:** Make the homepage context-aware: a persistent two-column map+feed layout that switches its right column between an active-trip feed and a curated highlights grid depending on whether Mischa is currently travelling. + +--- + +## 1. Mode switch + +A `travelling` toggle in `user/config/site.yaml` controls which mode the homepage renders. It is exposed in Admin2's Site Configuration panel via a new site config blueprint. + +| `travelling` | Homepage mode | +|---|---| +| `true` | Active trip — map + live feed | +| `false` | Between trips — map + highlights grid | + +The `active_trip` value changes format: it now stores the full page route (`/trips/italy-2026-demo`) instead of the bare slug (`italy-2026-demo`), because it will be managed via a `type: pages` dropdown in Admin2 rather than a free-text field. + +--- + +## 2. Data model changes + +### 2a. New file: `user/blueprints/config/site.yaml` + +Exposes site config fields in Admin2: + +```yaml +active_trip: + type: pages + label: Active Trip + start_route: '/trips' + show_root: false + show_slug: true + +travelling: + type: toggle + label: Currently Travelling + highlight: 1 + default: false +``` + +### 2b. `user/config/site.yaml` — value format update + +```yaml +# Before +active_trip: italy-2026-demo + +# After +active_trip: /trips/italy-2026-demo +travelling: false +``` + +### 2c. Trip page blueprint (`user/themes/intotheeast/blueprints/trip.yaml`) + +Add one field: + +```yaml +tagline: + type: text + label: Tagline + help: Short description shown on homepage highlight cards (e.g. "6 weeks from Venice to Sicily by train") +``` + +### 2d. Entry blueprint (`user/themes/intotheeast/blueprints/entry.yaml`) + +Add one field: + +```yaml +featured: + type: toggle + label: Featured highlight + help: Show this entry as a homepage highlight when not travelling + default: false +``` + +### 2e. Story blueprint (`user/themes/intotheeast/blueprints/story.yaml`) + +Add the same `featured` toggle (identical definition). Stories are not auto-included — they opt in the same way as journal entries. + +--- + +## 3. Layout + +The two-column structure is always present regardless of mode. + +``` +┌────────────────────────┬────────────────────────────────┐ +│ │ │ +│ MapLibre map │ Right column │ +│ (sticky, │ (switches by mode) │ +│ always visible) │ │ +│ │ │ +└────────────────────────┴────────────────────────────────┘ +``` + +- Map: left column, ~45% width, `position: sticky; top: 0; height: 100vh` +- Right column: ~55% width, scrollable +- Mobile: map stacks on top at `40vh`, right column scrolls below + +--- + +## 4. Active trip mode (`travelling: true`) + +### Right column + +Chronological feed, newest first. Merges journal entries and story cards from the active trip's `/dailies` and `/stories` sub-pages. This is the existing feed behaviour — no changes to card markup or order logic. + +Trip title and entry counts shown above the feed. + +### Map + +- Marker per journal entry with `lat`/`lng` in frontmatter +- Journey line connecting markers in order +- GPX route files loaded from the active trip page media (same pattern as `map.html.twig`, including the smart connector-suppression logic from the GPX connector spec) +- Clicking a marker scrolls to that entry card in the feed + +### Template change (`home.html.twig`) + +The slug-based path construction is replaced with direct route usage: + +```twig +{# Before #} +{% set slug = config.site.active_trip %} +{% set trip = grav.pages.find('/trips/' ~ slug) %} +{% set dailies_page = grav.pages.find('/trips/' ~ slug ~ '/dailies') %} +{% set stories_page = grav.pages.find('/trips/' ~ slug ~ '/stories') %} + +{# After #} +{% set trip_route = config.site.active_trip %} +{% set trip = grav.pages.find(trip_route) %} +{% set dailies_page = grav.pages.find(trip_route ~ '/dailies') %} +{% set stories_page = grav.pages.find(trip_route ~ '/stories') %} +``` + +--- + +## 5. Between-trips mode (`travelling: false`) + +### Highlight selection logic + +1. Collect all published trip pages from `/trips` +2. For each trip, collect all published children from `/dailies` and `/stories` where `featured: true` +3. From each trip's candidates, pick one at random (`random()`) +4. Gather the per-trip picks into a pool; if more than 6 trips have candidates, randomly discard down to 6 +5. Shuffle the final pool so cards appear in random order (not grouped by trip) + +### Right column + +A grid of highlight cards. Below the grid, a "Explore all past trips →" CTA linking to `/trips`. + +**Grid layout:** 3 columns on desktop, 2 on tablet, 1 on mobile. + +### Highlight card anatomy + +``` +┌──────────────────────────┐ +│ [hero image] │ +├──────────────────────────┤ +│ ✦ Story / ◎ Journal │ ← type badge +│ Entry title │ ← links to entry page +│ Italy 2025 │ ← trip title +│ "tagline from trip" │ ← trip tagline +│ → View trip │ ← links to trip page +└──────────────────────────┘ +``` + +- Hero image: `entry.media.images|first` if no `hero_image` frontmatter field; cropped to 16:9 +- Type badge: `✦ Story` (accent colour) or `◎ Journal` (muted) +- Entry title: full clickable link to the entry URL +- Trip title + tagline: small secondary text; trip title links to the trip page +- "→ View trip": explicit CTA link to the trip page + +Cards with no hero image still render but without an image block. + +### Map + +- Marker per highlighted entry that has `lat`/`lng` in frontmatter +- No journey line between markers (entries are from different trips) +- No GPX data loaded +- Map fits bounds across all markers; falls back to a world-level zoom if no entries have coordinates +- Clicking a marker scrolls to that highlight card + +--- + +## 6. Files changed + +| File | Change | +|---|---| +| `user/blueprints/config/site.yaml` | **Create** — exposes `active_trip` (pages) + `travelling` (toggle) in Admin2 | +| `user/config/site.yaml` | **Update** — `active_trip` value to full route; add `travelling: false` | +| `user/themes/intotheeast/blueprints/trip.yaml` | **Update** — add `tagline` text field | +| `user/themes/intotheeast/blueprints/entry.yaml` | **Update** — add `featured` toggle | +| `user/themes/intotheeast/blueprints/story.yaml` | **Update** — add `featured` toggle | +| `user/themes/intotheeast/templates/home.html.twig` | **Update** — mode branch, route-based lookup, highlights logic, GPX loading | +| `user/themes/intotheeast/css/style.css` | **Update** — highlight card styles, grid layout | + +No new plugins. No build pipeline. All changes in `user/`. + +--- + +## 7. Constraints + +- `post-form.md` (`pageconfig.parent`) remains manually synced with `active_trip` — this is unchanged behaviour documented in CLAUDE.md +- The `type: pages` field in Admin2 is confirmed present in the bundle but untested in a user site config blueprint; if it does not render, fall back to `type: select` with static trip slug options (one-minute fix, no other code changes needed) +- Random selection uses Twig's `random()` — order varies per page load; this is intentional