Compare commits

...
8 Commits
Author SHA1 Message Date
m038 65e18be92a docs: mark template-refactor plan as complete 2026-06-26 10:07:31 +02:00
m038 13fe204d30 docs: mark accessibility-audit plan as complete 2026-06-26 09:55:05 +02:00
m038 ae33c256a7 docs: mark pixelfed-import plan as complete 2026-06-26 09:43:15 +02:00
m038andClaude Sonnet 4.6 ef215cb4fc docs: backfill Status field on all plans
Assigns Status to the 18 plans that had none, based on checkbox
completion state, memory entries, and CLAUDE.md documentation:

- 16 marked Complete (dates approximate from git/memory)
- 1 marked Not started (template-refactor, now unblocked)
- 1 marked Deferred (pixelfed-import, superseded by travel-memories)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-26 09:37:56 +02:00
m038andClaude Sonnet 4.6 ac80a9ab18 docs: add plan status convention to CLAUDE.md
Defines Status field values (Not started / In progress / Deferred /
Complete / Abandoned) and scanning rules so any session knows which
plans are open vs done without reading every file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-26 09:37:22 +02:00
m038 82d6da48e1 test: add global teardown — sweep ui-test entries from 02.post and dailies 2026-06-25 07:32:32 +02:00
m038 7b93fbc5a3 docs: mark asset pipeline plan as complete 2026-06-25 00:13:25 +02:00
m038 41e303e6bc docs: mark frontend polish plan as complete 2026-06-25 00:05:27 +02:00
23 changed files with 570 additions and 38 deletions
+16
View File
@@ -146,3 +146,19 @@ Specs: `docs/working/specs/YYYY-MM-DD-<topic>-design.md`
Plans: `docs/working/plans/YYYY-MM-DD-<topic>.md` Plans: `docs/working/plans/YYYY-MM-DD-<topic>.md`
The brainstorming and writing-plans skills default to `docs/superpowers/`; these lines override that default. The brainstorming and writing-plans skills default to `docs/superpowers/`; these lines override that default.
### Plan status convention
Every plan in `docs/working/plans/` must have a `**Status:**` line immediately after the title heading:
| Status | Meaning |
|---|---|
| `📋 Not started` | Plan written; work not yet begun |
| `🔄 In progress — <note>` | Actively being worked on |
| `⏸️ Deferred — <reason>` | Intentionally postponed |
| `✅ Complete (YYYY-MM-DD)` | Done |
| `❌ Abandoned — <reason>` | Won't implement |
**When asked what's open:** surface `Not started` and `In progress` plans. Show `Deferred` plans but label them clearly. Omit `Complete` and `Abandoned` unless explicitly asked.
**When finishing a plan:** update the `**Status:**` field in the plan file to `✅ Complete (YYYY-MM-DD)` before closing the session. This applies whether execution was done by Claude directly, via the superpowers:executing-plans skill, or via superpowers:subagent-driven-development.
@@ -1,5 +1,7 @@
# Grav 2.0 Upgrade Implementation Plan # Grav 2.0 Upgrade Implementation Plan
**Status:** ✅ Complete (2026-06-18)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Upgrade the local dev Docker environment from linuxserver/grav 1.7 to getgrav/grav 2.0 RC, validate the full Milestone 1 posting workflow, and update the production install script for a fresh Grav 2.0 deploy. **Goal:** Upgrade the local dev Docker environment from linuxserver/grav 1.7 to getgrav/grav 2.0 RC, validate the full Milestone 1 posting workflow, and update the production install script for a fresh Grav 2.0 deploy.
@@ -1,5 +1,7 @@
# UI Redesign Implementation Plan # UI Redesign Implementation Plan
**Status:** ✅ Complete (2026-06-19)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Redesign the Into the East travel blog UI using the design spec in `user/docs/design/design-spec.md` — introducing DM Serif Display + DM Sans fonts, a deep teal design system, full-bleed entry card photos, and polished mobile UX across all pages. **Goal:** Redesign the Into the East travel blog UI using the design spec in `user/docs/design/design-spec.md` — introducing DM Serif Display + DM Sans fonts, a deep teal design system, full-bleed entry card photos, and polished mobile UX across all pages.
@@ -1,5 +1,7 @@
# GPX Manager Implementation Plan # GPX Manager Implementation Plan
**Status:** ✅ Complete (2026-06-19)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a protected admin page at `/gpx-manager` that lists all trip GPX files and supports upload and deletion via the Grav API. **Goal:** Build a protected admin page at `/gpx-manager` that lists all trip GPX files and supports upload and deletion via the Grav API.
@@ -1,5 +1,7 @@
# Home Page & Content Flow Implementation Plan # Home Page & Content Flow Implementation Plan
**Status:** ✅ Complete (2026-06-19)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace the redirect-based home page with a real side-by-side map + feed home page, add a past-trips archive, add story cards to all feeds, and enrich the trip page with a sticky sidebar index. **Goal:** Replace the redirect-based home page with a real side-by-side map + feed home page, add a past-trips archive, add story cards to all feeds, and enrich the trip page with a sticky sidebar index.
@@ -1,5 +1,7 @@
# Trip Entity Implementation Plan # Trip Entity Implementation Plan
**Status:** ✅ Complete (2026-06-19)
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task. > **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` to implement this plan task-by-task.
**Goal:** Restructure the site around a Trip entity — tracker/map/stats/stories become children of `/trips/japan-korea-2026/`, GPX route files live as media on the trip page, and `site.yaml` holds an `active_trip` slug so the nav can switch trips via config. **Goal:** Restructure the site around a Trip entity — tracker/map/stats/stories become children of `/trips/japan-korea-2026/`, GPX route files live as media on the trip page, and `site.yaml` holds an `active_trip` slug so the nav can switch trips via config.
@@ -1,5 +1,7 @@
# Trip Page Filter Bar — Implementation Plan # Trip Page Filter Bar — Implementation Plan
**Status:** ✅ Complete (2026-06-20)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace the three unstyled nav links on the trip page with an in-page filter bar (All content / Journal / Stories) and an inline Stats toggle — no page navigation needed. **Goal:** Replace the three unstyled nav links on the trip page with an in-page filter bar (All content / Journal / Stories) and an inline Stats toggle — no page navigation needed.
@@ -1,5 +1,7 @@
# Tuscany Demo Stories Implementation Plan # Tuscany Demo Stories Implementation Plan
**Status:** ✅ Complete (2026-06-20)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add three Italy 2025 Tuscany demo stories that showcase distinct story-mode composition patterns. **Goal:** Add three Italy 2025 Tuscany demo stories that showcase distinct story-mode composition patterns.
@@ -2,7 +2,7 @@
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Status:** 🔄 In progress — Task 1 complete (skip link), Tasks 26 open **Status:** ✅ Complete (2026-06-26)
**Goal:** Fix all eight WCAG 2.1 AA failures identified in the accessibility audit and add axe-core Playwright regression tests. **Goal:** Fix all eight WCAG 2.1 AA failures identified in the accessibility audit and add axe-core Playwright regression tests.
@@ -1,5 +1,7 @@
# Demo Data Redesign Implementation Plan # Demo Data Redesign Implementation Plan
**Status:** ✅ Complete (2026-06-20)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace patchwork demo content with a single high-quality `italy-2026-demo` trip following the real 8-day Tuscan cycling loop, with 12 journal entries (with photos) and 4 stories exercising all shortcode types. **Goal:** Replace patchwork demo content with a single high-quality `italy-2026-demo` trip following the real 8-day Tuscan cycling loop, with 12 journal entries (with photos) and 4 stories exercising all shortcode types.
@@ -1,5 +1,7 @@
# GPX Connector Logic Implementation Plan # GPX Connector Logic Implementation Plan
**Status:** ✅ Complete (2026-06-20)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Suppress the straight-line connector between adjacent map markers when a single GPX file covers both endpoints; keep connectors for uncovered gaps; add `force_connect` and `transport_mode` fields to entry/story blueprints. **Goal:** Suppress the straight-line connector between adjacent map markers when a single GPX file covers both endpoints; keep connectors for uncovered gaps; add `force_connect` and `transport_mode` fields to entry/story blueprints.
@@ -1,5 +1,7 @@
# Pixelfed Import & Demo Reorganisation Implementation Plan # Pixelfed Import & Demo Reorganisation Implementation Plan
**Status:** ✅ Complete (2026-06-26)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Import 36 Pixelfed posts into three permanent trips and reorganise the demo system so Italy demo content moves to a clearly-labelled `italy-2026-demo` trip and Japan demo content is retired. **Goal:** Import 36 Pixelfed posts into three permanent trips and reorganise the demo system so Italy demo content moves to a clearly-labelled `italy-2026-demo` trip and Japan demo content is retired.
@@ -1,5 +1,7 @@
# Documentation Restructure Implementation Plan # Documentation Restructure Implementation Plan
**Status:** ✅ Complete (2026-06-21)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
**Goal:** Restructure `docs/` from an organic flat layout into a type-first hierarchy (`guides/`, `reference/`, `working/`, `research/`) serving two personas: Mischa and Claude. **Goal:** Restructure `docs/` from an organic flat layout into a type-first hierarchy (`guides/`, `reference/`, `working/`, `research/`) serving two personas: Mischa and Claude.
@@ -1,5 +1,7 @@
# Entry Enrichment Implementation Plan # Entry Enrichment Implementation Plan
**Status:** ✅ Complete (2026-06-21)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Enrich all real trip journal entries with location, GPS coordinates, and approximate weather by generating per-trip Markdown review docs, letting the user correct them, then applying changes to YAML frontmatter. **Goal:** Enrich all real trip journal entries with location, GPS coordinates, and approximate weather by generating per-trip Markdown review docs, letting the user correct them, then applying changes to YAML frontmatter.
@@ -1,5 +1,7 @@
# Homepage Redesign Implementation Plan # Homepage Redesign Implementation Plan
**Status:** ✅ Complete (2026-06-21)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Context-aware homepage with a persistent two-column map+feed layout: active-trip mode shows the live feed + GPX on the home map; between-trips mode shows a curated highlights grid from all trips with markers-only on the map. **Goal:** Context-aware homepage with a persistent two-column map+feed layout: active-trip mode shows the live feed + GPX on the home map; between-trips mode shows a curated highlights grid from all trips with markers-only on the map.
@@ -1,5 +1,7 @@
# Playwright Tests — Improvement & Expansion # Playwright Tests — Improvement & Expansion
**Status:** ✅ Complete (2026-06-21)
> **STATUS: DONE** — merged to main 2026-06-21 (commits c703a09…1d29c30). All 6 tasks complete; 79 tests passing. See `docs/working/specs/2026-06-21-playwright-tests-design.md` for details. > **STATUS: DONE** — merged to main 2026-06-21 (commits c703a09…1d29c30). All 6 tasks complete; 79 tests passing. See `docs/working/specs/2026-06-21-playwright-tests-design.md` for details.
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
@@ -1,5 +1,7 @@
# travel-memories Implementation Plan # travel-memories Implementation Plan
**Status:** ✅ Complete (2026-06-23)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a local Flask web app that turns Immich photo albums into Grav CMS journal entries and story pages via a six-phase guided workflow. **Goal:** Build a local Flask web app that turns Immich photo albums into Grav CMS journal entries and story pages via a six-phase guided workflow.
@@ -1,5 +1,7 @@
# Feed-Map Alignment, Stories Map & E2E Tests # Feed-Map Alignment, Stories Map & E2E Tests
**Status:** ✅ Complete (2026-06-22)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Extract the shared feed mini-map into a reusable Twig partial, add the same map to the stories listing page, fix dailies attribution/marker-click bugs, document session learnings, and add regression tests for all new UX features. **Goal:** Extract the shared feed mini-map into a reusable Twig partial, add the same map to the stories listing page, fix dailies attribution/marker-click bugs, document session learnings, and add regression tests for all new UX features.
+39 -37
View File
@@ -1,6 +1,8 @@
# Asset Pipeline & Frontend Reliability Implementation Plan # Asset Pipeline & Frontend Reliability Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Status:** ✅ Complete (2026-06-25)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
**Goal:** Eliminate all CDN dependencies, self-host fonts, and deduplicate shared JS logic into versioned bundles built via Docker. **Goal:** Eliminate all CDN dependencies, self-host fonts, and deduplicate shared JS logic into versioned bundles built via Docker.
@@ -54,7 +56,7 @@ Set up `package.json`, the Docker `make build-assets` target, and gitignore. Ver
**Interfaces:** **Interfaces:**
- Produces: `make build-assets` command that runs `npm ci && npm run build` in Docker Node 20 Alpine - Produces: `make build-assets` command that runs `npm ci && npm run build` in Docker Node 20 Alpine
- [ ] **Step 1: Create package.json** - [x] **Step 1: Create package.json**
Create `user/themes/intotheeast/package.json`: Create `user/themes/intotheeast/package.json`:
@@ -78,7 +80,7 @@ Create `user/themes/intotheeast/package.json`:
} }
``` ```
- [ ] **Step 2: Add `build-assets` target to Makefile** - [x] **Step 2: Add `build-assets` target to Makefile**
Add after the existing `build:` target in `Makefile`: Add after the existing `build:` target in `Makefile`:
@@ -90,7 +92,7 @@ build-assets:
sh -c "npm install && npm run build" sh -c "npm install && npm run build"
``` ```
- [ ] **Step 3: Add node_modules to user/ gitignore** - [x] **Step 3: Add node_modules to user/ gitignore**
Add to `user/.gitignore`: Add to `user/.gitignore`:
@@ -98,7 +100,7 @@ Add to `user/.gitignore`:
/themes/intotheeast/node_modules/ /themes/intotheeast/node_modules/
``` ```
- [ ] **Step 4: Create placeholder source files so the build has something to process** - [x] **Step 4: Create placeholder source files so the build has something to process**
Create `user/themes/intotheeast/js/src/main.js`: Create `user/themes/intotheeast/js/src/main.js`:
```javascript ```javascript
@@ -110,7 +112,7 @@ Create `user/themes/intotheeast/js/src/map.js`:
// placeholder — replaced in Task 4 // placeholder — replaced in Task 4
``` ```
- [ ] **Step 5: Run the build and verify it completes** - [x] **Step 5: Run the build and verify it completes**
```bash ```bash
make build-assets make build-assets
@@ -122,7 +124,7 @@ ls user/themes/intotheeast/js/main.js
ls user/themes/intotheeast/js/map.js ls user/themes/intotheeast/js/map.js
``` ```
- [ ] **Step 6: Commit** - [x] **Step 6: Commit**
```bash ```bash
git -C user add themes/intotheeast/package.json themes/intotheeast/package-lock.json themes/intotheeast/js/src/main.js themes/intotheeast/js/src/map.js .gitignore git -C user add themes/intotheeast/package.json themes/intotheeast/package-lock.json themes/intotheeast/js/src/main.js themes/intotheeast/js/src/map.js .gitignore
@@ -147,7 +149,7 @@ Write the full `js/src/main.js`. This is the single source of truth for all dupl
- `css-compiled/main.css` — PhotoSwipe CSS + @font-face rules for DM Sans variable + DM Serif Display - `css-compiled/main.css` — PhotoSwipe CSS + @font-face rules for DM Sans variable + DM Serif Display
- `fonts/*.woff2` — copied from @fontsource packages by esbuild - `fonts/*.woff2` — copied from @fontsource packages by esbuild
- [ ] **Step 1: Write js/src/main.js** - [x] **Step 1: Write js/src/main.js**
Replace `user/themes/intotheeast/js/src/main.js` with: Replace `user/themes/intotheeast/js/src/main.js` with:
@@ -407,7 +409,7 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
``` ```
- [ ] **Step 2: Run build** - [x] **Step 2: Run build**
```bash ```bash
make build-assets make build-assets
@@ -422,7 +424,7 @@ ls user/themes/intotheeast/fonts/
`fonts/` should contain woff2 files from @fontsource packages. `fonts/` should contain woff2 files from @fontsource packages.
- [ ] **Step 3: Verify font output in CSS** - [x] **Step 3: Verify font output in CSS**
```bash ```bash
grep '@font-face' user/themes/intotheeast/css-compiled/main.css | head -5 grep '@font-face' user/themes/intotheeast/css-compiled/main.css | head -5
@@ -430,7 +432,7 @@ grep '@font-face' user/themes/intotheeast/css-compiled/main.css | head -5
Expected: multiple `@font-face` rules referencing `../fonts/*.woff2` paths. Expected: multiple `@font-face` rules referencing `../fonts/*.woff2` paths.
- [ ] **Step 4: Commit** - [x] **Step 4: Commit**
```bash ```bash
git -C user add themes/intotheeast/js/src/main.js themes/intotheeast/js/main.js themes/intotheeast/css-compiled/main.css themes/intotheeast/fonts/ git -C user add themes/intotheeast/js/src/main.js themes/intotheeast/js/main.js themes/intotheeast/css-compiled/main.css themes/intotheeast/fonts/
@@ -452,7 +454,7 @@ Move `parseGpxFiles` from `trip.html.twig` into the shared utility and expose `h
- `MapUtils.haversineKm(lat1, lng1, lat2, lng2)``number` (km) - `MapUtils.haversineKm(lat1, lng1, lat2, lng2)``number` (km)
- `MapUtils.parseGpxFiles(urls, callback)``urls: string[]`, `callback({ distance, eleGain, eleLoss, highest, lowest, movingTime, avgSpeed } | { error: string })``void` - `MapUtils.parseGpxFiles(urls, callback)``urls: string[]`, `callback({ distance, eleGain, eleLoss, highest, lowest, movingTime, avgSpeed } | { error: string })``void`
- [ ] **Step 1: Add parseGpxFiles function to maplibre-utils.js** - [x] **Step 1: Add parseGpxFiles function to maplibre-utils.js**
In `user/themes/intotheeast/js/maplibre-utils.js`, add the following block immediately before the `global.MapUtils = {` line (currently line 333): In `user/themes/intotheeast/js/maplibre-utils.js`, add the following block immediately before the `global.MapUtils = {` line (currently line 333):
@@ -545,7 +547,7 @@ In `user/themes/intotheeast/js/maplibre-utils.js`, add the following block immed
} }
``` ```
- [ ] **Step 2: Add haversineKm and parseGpxFiles to MapUtils exports** - [x] **Step 2: Add haversineKm and parseGpxFiles to MapUtils exports**
Find the `global.MapUtils = {` block (currently the last block in the file) and add both new entries: Find the `global.MapUtils = {` block (currently the last block in the file) and add both new entries:
@@ -564,7 +566,7 @@ Find the `global.MapUtils = {` block (currently the last block in the file) and
}; };
``` ```
- [ ] **Step 3: Commit** - [x] **Step 3: Commit**
```bash ```bash
git -C user add themes/intotheeast/js/maplibre-utils.js git -C user add themes/intotheeast/js/maplibre-utils.js
@@ -589,7 +591,7 @@ Write `js/src/map.js` to bundle MapLibre GL, toGeoJSON, and maplibre-utils as a
- `window.toGeoJSON` — toGeoJSON converter - `window.toGeoJSON` — toGeoJSON converter
- `window.MapUtils` — all MapUtils functions (set by maplibre-utils.js side effect) - `window.MapUtils` — all MapUtils functions (set by maplibre-utils.js side effect)
- [ ] **Step 1: Write js/src/map.js** - [x] **Step 1: Write js/src/map.js**
Replace `user/themes/intotheeast/js/src/map.js` with: Replace `user/themes/intotheeast/js/src/map.js` with:
@@ -605,7 +607,7 @@ window.maplibregl = maplibregl;
window.toGeoJSON = toGeoJSON; window.toGeoJSON = toGeoJSON;
``` ```
- [ ] **Step 2: Run build** - [x] **Step 2: Run build**
```bash ```bash
make build-assets make build-assets
@@ -619,7 +621,7 @@ ls user/themes/intotheeast/css-compiled/map.css
`css-compiled/map.css` should be non-empty (~100KB+) as it contains full MapLibre GL styles. `css-compiled/map.css` should be non-empty (~100KB+) as it contains full MapLibre GL styles.
- [ ] **Step 3: Commit** - [x] **Step 3: Commit**
```bash ```bash
git -C user add themes/intotheeast/js/src/map.js themes/intotheeast/js/map.js themes/intotheeast/css-compiled/map.css git -C user add themes/intotheeast/js/src/map.js themes/intotheeast/js/map.js themes/intotheeast/css-compiled/map.css
@@ -639,7 +641,7 @@ Register the universal bundle via Grav's Asset Manager, remove Google Fonts exte
**Interfaces:** **Interfaces:**
- Produces: `{% block map_assets %}{% endblock %}` — filled by trip.html.twig, feed-map.html.twig, map.html.twig in later tasks - Produces: `{% block map_assets %}{% endblock %}` — filled by trip.html.twig, feed-map.html.twig, map.html.twig in later tasks
- [ ] **Step 1: Update base.html.twig** - [x] **Step 1: Update base.html.twig**
Replace the entire file content of `user/themes/intotheeast/templates/partials/base.html.twig` with: Replace the entire file content of `user/themes/intotheeast/templates/partials/base.html.twig` with:
@@ -683,7 +685,7 @@ Key changes from original:
- Removed the `<script>` photo strip block (lines 3073) — now in `js/main.js` - Removed the `<script>` photo strip block (lines 3073) — now in `js/main.js`
- Added `{% block map_assets %}{% endblock %}` before `{{ assets.js('bottom')|raw }}` - Added `{% block map_assets %}{% endblock %}` before `{{ assets.js('bottom')|raw }}`
- [ ] **Step 2: Remove Google Fonts @import from style.css if present** - [x] **Step 2: Remove Google Fonts @import from style.css if present**
Check whether `css/style.css` contains a Google Fonts import: Check whether `css/style.css` contains a Google Fonts import:
@@ -693,13 +695,13 @@ grep -n 'googleapis\|fonts.g' user/themes/intotheeast/css/style.css
If any line is found, remove it. The font-family declarations using `--font-display` and `--font-ui` stay unchanged — they reference the CSS custom properties defined in `tokens.css`, which will work with the self-hosted fonts in `css-compiled/main.css`. If any line is found, remove it. The font-family declarations using `--font-display` and `--font-ui` stay unchanged — they reference the CSS custom properties defined in `tokens.css`, which will work with the self-hosted fonts in `css-compiled/main.css`.
- [ ] **Step 3: Load the dev server and verify the page renders** - [x] **Step 3: Load the dev server and verify the page renders**
Open `http://localhost:8081` in a browser. The page should load with correct fonts (DM Sans for body, DM Serif Display for headings). Open browser DevTools → Network tab → filter by "google" — no requests to `fonts.googleapis.com` or `fonts.gstatic.com` should appear. Open `http://localhost:8081` in a browser. The page should load with correct fonts (DM Sans for body, DM Serif Display for headings). Open browser DevTools → Network tab → filter by "google" — no requests to `fonts.googleapis.com` or `fonts.gstatic.com` should appear.
If fonts look wrong, check that `css-compiled/main.css` is served by opening `http://localhost:8081/user/themes/intotheeast/css-compiled/main.css`. If fonts look wrong, check that `css-compiled/main.css` is served by opening `http://localhost:8081/user/themes/intotheeast/css-compiled/main.css`.
- [ ] **Step 4: Commit** - [x] **Step 4: Commit**
```bash ```bash
git -C user add themes/intotheeast/templates/partials/base.html.twig themes/intotheeast/css/style.css git -C user add themes/intotheeast/templates/partials/base.html.twig themes/intotheeast/css/style.css
@@ -722,7 +724,7 @@ This is the largest template change. Remove all duplicated JS blocks, CDN tags,
- `window.MapUtils.haversineKm(lat1, lng1, lat2, lng2)` — from map bundle (Task 3) - `window.MapUtils.haversineKm(lat1, lng1, lat2, lng2)` — from map bundle (Task 3)
- `window.scrollama` — from `js/main.js` (Task 2, though not used on this page) - `window.scrollama` — from `js/main.js` (Task 2, though not used on this page)
- [ ] **Step 1: Remove the CDN script/link tags and PhotoSwipe CSS link** - [x] **Step 1: Remove the CDN script/link tags and PhotoSwipe CSS link**
Remove these lines from `trip.html.twig`: Remove these lines from `trip.html.twig`:
- Line 4: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css">` - Line 4: `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.css">`
@@ -734,7 +736,7 @@ Remove these lines from `trip.html.twig`:
<script src="{{ url('theme://js/maplibre-utils.js') }}"></script> <script src="{{ url('theme://js/maplibre-utils.js') }}"></script>
``` ```
- [ ] **Step 2: Add map_assets block immediately after `{% block content %}`** - [x] **Step 2: Add map_assets block immediately after `{% block content %}`**
After the opening `{% block content %}` line, add: After the opening `{% block content %}` line, add:
@@ -745,7 +747,7 @@ After the opening `{% block content %}` line, add:
{% endblock %} {% endblock %}
``` ```
- [ ] **Step 3: Remove the duplicated JS blocks** - [x] **Step 3: Remove the duplicated JS blocks**
Remove the following `<script>` blocks entirely from `trip.html.twig`. These are now handled by `js/main.js`: Remove the following `<script>` blocks entirely from `trip.html.twig`. These are now handled by `js/main.js`:
@@ -756,7 +758,7 @@ Remove the following `<script>` blocks entirely from `trip.html.twig`. These are
Note: the `makePanelToggle` code lives *inside* the GPX stats IIFE (which stays) — that is handled in Step 5, not here. Note: the `makePanelToggle` code lives *inside* the GPX stats IIFE (which stays) — that is handled in Step 5, not here.
- [ ] **Step 4: Update the GPX stats block to use MapUtils** - [x] **Step 4: Update the GPX stats block to use MapUtils**
In the remaining GPX stats IIFE (the block starting with `(function() {` that references `HAS_GPX`, `parseGpxFiles`, and `haversineKm`): In the remaining GPX stats IIFE (the block starting with `(function() {` that references `HAS_GPX`, `parseGpxFiles`, and `haversineKm`):
@@ -803,7 +805,7 @@ Replace with:
); );
``` ```
- [ ] **Step 5: Remove the makePanelToggle function from the GPX stats IIFE** - [x] **Step 5: Remove the makePanelToggle function from the GPX stats IIFE**
Inside the GPX stats IIFE, find and remove: Inside the GPX stats IIFE, find and remove:
- The `makePanelToggle` function definition - The `makePanelToggle` function definition
@@ -812,7 +814,7 @@ Inside the GPX stats IIFE, find and remove:
These are now handled by `initPanelToggles()` in `main.js`. These are now handled by `initPanelToggles()` in `main.js`.
- [ ] **Step 6: Open the trip page and verify all features** - [x] **Step 6: Open the trip page and verify all features**
Open `http://localhost:8081/trips/japan-korea-2026` (or the current active trip URL). Verify: Open `http://localhost:8081/trips/japan-korea-2026` (or the current active trip URL). Verify:
- Map loads and markers are visible - Map loads and markers are visible
@@ -827,7 +829,7 @@ Open `http://localhost:8081/trips/japan-korea-2026` (or the current active trip
Open DevTools → Network tab → reload. Filter by "cdn.jsdelivr" — zero results expected. Open DevTools → Network tab → reload. Filter by "cdn.jsdelivr" — zero results expected.
- [ ] **Step 7: Commit** - [x] **Step 7: Commit**
```bash ```bash
git -C user add themes/intotheeast/templates/trip.html.twig git -C user add themes/intotheeast/templates/trip.html.twig
@@ -845,7 +847,7 @@ Remove CDN tags from `feed-map.html.twig`, `map.html.twig`, and `story.html.twig
- Modify: `user/themes/intotheeast/templates/map.html.twig` - Modify: `user/themes/intotheeast/templates/map.html.twig`
- Modify: `user/themes/intotheeast/templates/story.html.twig` - Modify: `user/themes/intotheeast/templates/story.html.twig`
- [ ] **Step 1: feed-map.html.twig — remove CDN tags, add map_assets block** - [x] **Step 1: feed-map.html.twig — remove CDN tags, add map_assets block**
In `user/themes/intotheeast/templates/partials/feed-map.html.twig`: In `user/themes/intotheeast/templates/partials/feed-map.html.twig`:
@@ -865,7 +867,7 @@ Add the map assets block at the very top of the `{% if map_entries|length > 0 %}
{% endblock %} {% endblock %}
``` ```
- [ ] **Step 2: map.html.twig — remove CDN tags, add map_assets block** - [x] **Step 2: map.html.twig — remove CDN tags, add map_assets block**
In `user/themes/intotheeast/templates/map.html.twig`: In `user/themes/intotheeast/templates/map.html.twig`:
@@ -885,7 +887,7 @@ Add after `{% block content %}`:
{% endblock %} {% endblock %}
``` ```
- [ ] **Step 3: story.html.twig — remove Scrollama CDN tag** - [x] **Step 3: story.html.twig — remove Scrollama CDN tag**
In `user/themes/intotheeast/templates/story.html.twig`: In `user/themes/intotheeast/templates/story.html.twig`:
@@ -896,7 +898,7 @@ Remove line 72:
Scrollama is now bundled in `main.js` and exposed as `window.scrollama`. The existing inline script that calls `scrollama()` will work unchanged. Scrollama is now bundled in `main.js` and exposed as `window.scrollama`. The existing inline script that calls `scrollama()` will work unchanged.
- [ ] **Step 4: Commit** - [x] **Step 4: Commit**
```bash ```bash
git -C user add themes/intotheeast/templates/partials/feed-map.html.twig themes/intotheeast/templates/map.html.twig themes/intotheeast/templates/story.html.twig git -C user add themes/intotheeast/templates/partials/feed-map.html.twig themes/intotheeast/templates/map.html.twig themes/intotheeast/templates/story.html.twig
@@ -909,7 +911,7 @@ git -C user commit -m "refactor: remove CDN tags from feed-map, map, story templ
**Files:** None modified. Verification only. **Files:** None modified. Verification only.
- [ ] **Step 1: Verify zero external requests on the trip page** - [x] **Step 1: Verify zero external requests on the trip page**
Open `http://localhost:8081/trips/japan-korea-2026`. Open DevTools → Network tab → reload. Open `http://localhost:8081/trips/japan-korea-2026`. Open DevTools → Network tab → reload.
@@ -918,7 +920,7 @@ Check these domains appear zero times:
- `fonts.googleapis.com` - `fonts.googleapis.com`
- `fonts.gstatic.com` - `fonts.gstatic.com`
- [ ] **Step 2: Verify trip page features** - [x] **Step 2: Verify trip page features**
- Map renders with markers and GPX track - Map renders with markers and GPX track
- Marker click scrolls to entry card and flashes it - Marker click scrolls to entry card and flashes it
@@ -932,7 +934,7 @@ Check these domains appear zero times:
- Back-to-top button appears after scrolling down; click scrolls to top - Back-to-top button appears after scrolling down; click scrolls to top
- Journal photo strip: swipe/scroll dots sync; buttons navigate; expand button opens PhotoSwipe; arrow keys advance; click outside closes - Journal photo strip: swipe/scroll dots sync; buttons navigate; expand button opens PhotoSwipe; arrow keys advance; click outside closes
- [ ] **Step 3: Verify story page** - [x] **Step 3: Verify story page**
Open a story URL (e.g. `http://localhost:8081/trips/italy-2026-demo/stories/sorano-rock-and-time`): Open a story URL (e.g. `http://localhost:8081/trips/italy-2026-demo/stories/sorano-rock-and-time`):
- Hero image loads - Hero image loads
@@ -942,7 +944,7 @@ Open a story URL (e.g. `http://localhost:8081/trips/italy-2026-demo/stories/sora
- If page has `.scrolly` sections: they animate on scroll - If page has `.scrolly` sections: they animate on scroll
- No console errors - No console errors
- [ ] **Step 4: Check built file sizes** - [x] **Step 4: Check built file sizes**
```bash ```bash
ls -lh user/themes/intotheeast/js/main.js user/themes/intotheeast/js/map.js user/themes/intotheeast/css-compiled/main.css user/themes/intotheeast/css-compiled/map.css ls -lh user/themes/intotheeast/js/main.js user/themes/intotheeast/js/map.js user/themes/intotheeast/css-compiled/main.css user/themes/intotheeast/css-compiled/map.css
@@ -956,7 +958,7 @@ Rough expected sizes (minified):
If `map.js` is unexpectedly small (<100 KB), MapLibre GL may not have bundled — check that `import maplibregl from 'maplibre-gl'` is in `js/src/map.js`. If `map.js` is unexpectedly small (<100 KB), MapLibre GL may not have bundled — check that `import maplibregl from 'maplibre-gl'` is in `js/src/map.js`.
- [ ] **Step 5: Final commit** - [x] **Step 5: Final commit**
```bash ```bash
git -C user add -A git -C user add -A
@@ -1,5 +1,7 @@
# Template Refactor Implementation Plan # Template Refactor Implementation Plan
**Status:** ✅ Complete (2026-06-26)
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Extract stats computation and panel HTML into Twig macros, extract date range formatting into a macro, and fix two latent bugs in inactive templates — with zero visual change. **Goal:** Extract stats computation and panel HTML into Twig macros, extract date range formatting into a macro, and fix two latent bugs in inactive templates — with zero visual change.
@@ -0,0 +1,422 @@
# Frontend Polish Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
**Status:** ✅ Complete — implemented 2026-06-24
**Spec:** `docs/working/specs/2026-06-24-frontend-polish-design.md`
**Goal:** Visual polish across the five primary page templates: pill grammar, stats field-notes style, header identity, emoji replacement, trip card cover images, story progress bar, and story opening transition.
**Architecture:** Tasks 12 are pure CSS (style.css only). Task 3 touches one partial (emoji). Task 4 adds a blueprint field and updates one template. Tasks 56 each add CSS + a small Twig block to story.html.twig.
**Already done (this session):**
- `entry.html.twig` unified with feed partial — hero removed, PhotoSwipe replaces broken lightbox
- Dead CSS from old entry layout stripped from `style.css`
---
## Global Constraints
- All changes in `user/` — commit with `git -C user`, not main-repo git
- All new CSS uses token variables only — no hardcoded hex values
- Changes must degrade gracefully when optional data (cover image, location) is absent
- `prefers-reduced-motion` must be respected for any animations in Tasks 56
- Clear Grav cache after each template change: `make remote-cache-clear` or via Admin
---
## Task 1: Pure CSS — Pill grammar + Stats style + Header identity
**Files:**
- Modify: `user/themes/intotheeast/css/style.css`
**Interfaces:**
- Produces: visual changes to trip filter buttons, stats blocks, and site header across all pages
- [x] **Step 1: Pill grammar — change filter/sort buttons to rounded-rect**
Find `.trip-filter-btn,` selector block:
```css
.trip-filter-btn,
.trip-stats-btn {
...
border-radius: var(--radius-full);
...
}
```
Change only `border-radius` to `var(--radius-sm)`. Leave all other properties unchanged.
- [x] **Step 2: Stats — remove box, add left rule**
Find `.stat-block` rule:
```css
.stat-block {
background: var(--color-canvas);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-6) var(--space-5);
text-align: center;
box-shadow: var(--shadow-sm);
}
```
Replace with:
```css
.stat-block {
border-left: 2px solid var(--color-accent);
padding: var(--space-2) 0 var(--space-2) var(--space-4);
text-align: left;
}
```
- [x] **Step 3: Stats — change number color from accent to ink**
Find `.stat-value` rule. It contains `color: var(--color-accent)`. Change to `color: var(--color-ink)`. Leave all other properties unchanged.
- [x] **Step 4: Header — widen site title tracking and size**
Find `.site-title` rule:
```css
.site-title {
font-family: var(--font-display);
font-size: var(--text-lg);
font-weight: 400;
letter-spacing: -0.01em;
...
}
```
Change:
- `font-size: var(--text-lg)``font-size: var(--text-xl)`
- `letter-spacing: -0.01em``letter-spacing: 0.06em`
- [x] **Step 5: Header — thicken and gradient the accent stripe**
Find `.site-header` rule. It contains `border-top: 3px solid var(--color-accent)`.
Change to:
```css
border-top: 4px solid transparent;
border-image: linear-gradient(90deg, var(--color-accent), var(--color-accent-hover)) 1;
```
- [x] **Step 6: Visual smoke check**
Open browser and verify:
- `/trips` — trip filter buttons are square-cornered (not pill-shaped)
- `/trips/<any-trip>` — stats panel shows left accent stripe, cream numbers, no box border
- Header — "into the east" is slightly larger with wider tracking; accent stripe has gradient
- [x] **Step 7: Commit**
```bash
git -C user add themes/intotheeast/css/style.css
git -C user commit -m "style: pill grammar, stats field-notes, header identity"
```
---
## Task 2: Replace emoji icons in journal entry partial
**Files:**
- Modify: `user/themes/intotheeast/templates/partials/entry-journal.html.twig`
**Interfaces:**
- Affects: every journal entry in the home feed, trip feed, and standalone entry page
- [x] **Step 1: Replace location emoji with SVG pin**
Find in the partial:
```twig
· 📍
```
Replace with:
```twig
· <svg width="11" height="13" viewBox="0 0 12 14" fill="currentColor" aria-hidden="true" style="flex-shrink:0;vertical-align:-1px"><path d="M6 0C3.24 0 1 2.24 1 5c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 6.75A1.75 1.75 0 1 1 6 3.25a1.75 1.75 0 0 1 0 3.5z"/></svg>
```
- [x] **Step 2: Strip weather emoji prefix**
Find in the partial:
```twig
<span class="journal-post-weather">· {{ weather_icons[entry.header.weather_desc] ?? '' }} {{ entry.header.weather_desc }}</span>
```
Replace with:
```twig
<span class="journal-post-weather">· {{ entry.header.weather_desc }}</span>
```
The `weather_icons` map at the top of the partial can stay (removing it is optional cleanup); it will simply go unused.
- [x] **Step 3: Smoke check**
Open any trip page in browser. Confirm:
- Location shows small SVG pin instead of 📍
- Weather shows plain text (e.g. "· Sunny") with no emoji
- [x] **Step 4: Commit**
```bash
git -C user add themes/intotheeast/templates/partials/entry-journal.html.twig
git -C user commit -m "style: replace emoji icons with SVG pin and plain weather text"
```
---
## Task 3: Trip cards — cover image
**Files:**
- Modify: `user/themes/intotheeast/blueprints/trip.yaml`
- Modify: `user/themes/intotheeast/templates/trips.html.twig`
- Modify: `user/themes/intotheeast/css/style.css`
**Interfaces:**
- Produces: optional cover image banner on each trip card
- Consumes: `trip.header.cover_image` (new field) or first image from first published entry
- [x] **Step 1: Read the current trip blueprint**
```bash
cat user/themes/intotheeast/blueprints/trip.yaml
```
Locate the correct position to insert the new field (after `tagline` or near other media fields).
- [x] **Step 2: Add cover_image field to blueprint**
Insert in `trip.yaml` at an appropriate location:
```yaml
cover_image:
type: filepicker
label: Cover Image
preview_images: true
folder: '@self'
accept:
- image/*
```
- [x] **Step 3: Add cover image CSS to style.css**
In the `/* ── Past trips archive */` section, add after `.trip-card-counts`:
```css
.trip-card-cover {
aspect-ratio: 3 / 1;
overflow: hidden;
border-radius: var(--radius-md) var(--radius-md) 0 0;
background: var(--color-border);
margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-5);
}
.trip-card-cover img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 0.45s ease;
}
.trip-card:hover .trip-card-cover img { transform: scale(1.04); }
```
- [x] **Step 4: Update trips.html.twig to render cover image**
Inside the `{% for trip in trips %}` loop, before the `.trip-card-title` div, add:
```twig
{# Cover image: explicit field first, then first entry's first image #}
{% set cover = null %}
{% if trip.header.cover_image and trip.media[trip.header.cover_image] is defined %}
{% set cover = trip.media[trip.header.cover_image] %}
{% elseif dailies_page %}
{% set first_entry = dailies_page.children.published()|first %}
{% if first_entry and first_entry.media.images|length > 0 %}
{% set cover = first_entry.media.images|first %}
{% endif %}
{% endif %}
{% if cover %}
<div class="trip-card-cover">
<img src="{{ cover.cropResize(720, 240).url }}" alt="{{ trip.title }}" loading="lazy">
</div>
{% endif %}
```
- [x] **Step 5: Smoke check**
Open `/trips` in browser. Confirm:
- Trips with media show a 3:1 cover photo banner
- Trips without media show text-only card (no broken image element)
- Hover scales the image slightly
- [x] **Step 6: Commit**
```bash
git -C user add themes/intotheeast/blueprints/trip.yaml themes/intotheeast/templates/trips.html.twig themes/intotheeast/css/style.css
git -C user commit -m "feat: trip cards show cover image with 3:1 crop and hover zoom"
```
---
## Task 4: Story opening transition
**Files:**
- Modify: `user/themes/intotheeast/templates/story.html.twig`
- Modify: `user/themes/intotheeast/css/style.css`
**Interfaces:**
- Consumes: `date_str` and `location` already computed at the top of `story.html.twig`
- Produces: a centered location/date block at the top of `.story-body` with fade-in animation
- [x] **Step 1: Add story-opener CSS to style.css**
In the `/* ── Story pages */` section, after `.story-body p` rules, add:
```css
.story-opener {
text-align: center;
padding-bottom: var(--space-12);
margin-bottom: var(--space-12);
border-bottom: 1px solid var(--color-border);
opacity: 0;
animation: storyReveal 0.9s cubic-bezier(.16,1,.3,1) 0.8s both;
}
.story-opener__text {
font-family: var(--font-ui);
font-size: var(--text-sm);
color: var(--color-ink-muted);
letter-spacing: 0.06em;
text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) {
.story-opener { opacity: 1; animation: none; }
}
```
- [x] **Step 2: Add opener block to story.html.twig**
Inside `.story-body`, immediately before `{{ page.content|raw }}`, add:
```twig
{% if location or date_str %}
<div class="story-opener">
<span class="story-opener__text">
{{- date_str -}}
{%- if location and date_str %} · {% endif -%}
{{- location -}}
</span>
</div>
{% endif %}
```
- [x] **Step 3: Smoke check**
Open any published story in browser. Confirm:
- A small uppercase line showing date and location appears below the hero spacer
- It is separated from the prose by a thin horizontal rule
- It fades in after the hero title animation completes
- On a story with no location set: only date appears (or nothing if both are absent)
- [x] **Step 4: Commit**
```bash
git -C user add themes/intotheeast/templates/story.html.twig themes/intotheeast/css/style.css
git -C user commit -m "feat: story opening transition with location and date eyebrow"
```
---
## Task 5: Reading progress bar on story pages
**Files:**
- Modify: `user/themes/intotheeast/templates/story.html.twig`
- Modify: `user/themes/intotheeast/css/style.css`
**Interfaces:**
- Produces: 2px teal bar fixed at bottom of site header, progress tied to `.story-body` scroll position
- No bar rendered at all if `prefers-reduced-motion` is set (JS skips creating it)
- [x] **Step 1: Add progress bar CSS to style.css**
In the `/* ── Story pages */` section, add:
```css
.story-progress {
position: fixed;
top: var(--site-header-height);
left: 0;
height: 2px;
width: 0%;
background: var(--color-accent);
z-index: 200;
pointer-events: none;
will-change: width;
}
```
- [x] **Step 2: Add progress bar element and JS to story.html.twig**
Immediately after `{% block content %}` (before the hero markup), add:
```twig
<div class="story-progress" id="story-progress"></div>
```
In the `<script>` block at the bottom (after the existing scroll/reveal scripts), add:
```javascript
/* ── Reading progress bar ────────────────────────────────── */
(function () {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var bar = document.getElementById('story-progress');
var body = document.querySelector('.story-body');
if (!bar || !body) return;
function update() {
var rect = body.getBoundingClientRect();
var total = body.offsetHeight - window.innerHeight;
var scrolled = -rect.top;
var pct = total > 0 ? Math.min(100, Math.max(0, (scrolled / total) * 100)) : 0;
bar.style.width = pct.toFixed(1) + '%';
}
window.addEventListener('scroll', update, { passive: true });
update();
})();
```
- [x] **Step 3: Smoke check**
Open any published story in browser. Confirm:
- A thin teal line appears at the top of the content area (below the sticky header) as you scroll into the story body
- Bar is at 0% when the hero is visible, fills to 100% as you reach the story footer
- Bar is invisible (absent) on a device with `prefers-reduced-motion`
- [x] **Step 4: Commit**
```bash
git -C user add themes/intotheeast/templates/story.html.twig themes/intotheeast/css/style.css
git -C user commit -m "feat: reading progress bar on story pages"
```
---
## Final Verification
After all tasks complete:
1. Visual check list (browser):
- `/trips` — cover images on trip cards, hover scales; text-only fallback if no media
- `/trips/<any-trip>` — filter buttons square-cornered; stats left-rule style with cream numbers
- `/trips/<any-trip>/dailies/<any-entry>` (standalone URL) — SVG location pin, plain weather text, photo strip + PhotoSwipe, no hero image
- `/trips/<any-trip>/<any-story>` — opener block visible below hero; progress bar fills on scroll; no emoji anywhere
- Any page header — "into the east" wider-tracked; accent stripe slightly thicker with gradient
2. Reduced-motion check: simulate `prefers-reduced-motion: reduce` in browser devtools and confirm no animations fire on story pages (opener snaps visible immediately, progress bar absent).
3. Empty-data check: visit a trip with no media attached and confirm `/trips` degrades to text-only card without errors.
+1
View File
@@ -4,6 +4,7 @@ const { defineConfig, devices } = require('@playwright/test');
module.exports = defineConfig({ module.exports = defineConfig({
testDir: './tests/ui', testDir: './tests/ui',
globalSetup: './tests/global-setup.js', globalSetup: './tests/global-setup.js',
globalTeardown: './tests/global-teardown.js',
timeout: 30_000, timeout: 30_000,
retries: 0, retries: 0,
projects: [ projects: [
+57
View File
@@ -0,0 +1,57 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
function resolveUserDir() {
if (process.env.GRAV_USER_DIR) return process.env.GRAV_USER_DIR;
try {
const raw = execSync(
"docker inspect intotheeast_grav --format '{{range .Mounts}}{{if eq .Destination \"/var/www/html/user\"}}{{.Source}}{{end}}{{end}}'",
{ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }
).trim();
if (raw) return raw;
} catch (_) {}
return path.join(__dirname, '../user');
}
function sweepUiTestEntries(dir) {
if (!fs.existsSync(dir)) return 0;
const entries = fs.readdirSync(dir).filter(e => e.includes('ui-test'));
entries.forEach(e => fs.rmSync(path.join(dir, e), { recursive: true, force: true }));
return entries.length;
}
module.exports = async function globalTeardown() {
const userDir = resolveUserDir();
// Read active trip slug from post-form.md
const postFormPath = path.join(userDir, 'pages/02.post/post-form.md');
let dailiesDir = null;
if (fs.existsSync(postFormPath)) {
const content = fs.readFileSync(postFormPath, 'utf-8');
const m = content.match(/parent:\s*['"]?\/trips\/([^/'"]+)\/dailies/);
if (m) {
const tripSlug = m[1];
const tripsBase = path.join(userDir, 'pages/01.trips');
const tripFolder = fs.readdirSync(tripsBase).find(
f => f === tripSlug || f.endsWith('.' + tripSlug) || f.includes(tripSlug)
);
if (tripFolder) {
const dailiesBase = path.join(tripsBase, tripFolder);
const dailiesFolder = fs.readdirSync(dailiesBase).find(
f => f === 'dailies' || f === '01.dailies' || f.endsWith('.dailies')
);
if (dailiesFolder) dailiesDir = path.join(dailiesBase, dailiesFolder);
}
}
}
// Sweep both the post inbox and the active trip's dailies
const postInbox = path.join(userDir, 'pages/02.post');
const n1 = sweepUiTestEntries(postInbox);
const n2 = dailiesDir ? sweepUiTestEntries(dailiesDir) : 0;
if (n1 + n2 > 0) {
console.log(`[teardown] removed ${n1} ui-test entries from 02.post, ${n2} from dailies`);
}
};