From 3c77d6cdad859f0d142535f0e0e54f4832dd6ad1 Mon Sep 17 00:00:00 2001 From: Mischa Date: Sun, 21 Jun 2026 14:16:33 +0200 Subject: [PATCH] docs: remove stale docs/ content migrated to main repo docs/ on 2026-06-19 --- docs/bugs-and-fixes.md | 136 -- docs/design/design-spec.md | 368 ----- docs/milestone-1-spec.md | 193 --- docs/milestone-2-spec.md | 166 -- docs/milestone-3-spec.md | 182 --- docs/milestone-4-spec.md | 91 -- docs/pm-analysis.md | 161 -- docs/posting-pipeline.md | 123 -- docs/qa-results.md | 217 --- docs/qa-test-plan.md | 628 -------- docs/research-findpenguins.md | 141 -- docs/research-polarsteps.md | 137 -- docs/summary.md | 89 -- .../plans/2026-06-18-ui-redesign.md | 1386 ----------------- 14 files changed, 4018 deletions(-) delete mode 100644 docs/bugs-and-fixes.md delete mode 100644 docs/design/design-spec.md delete mode 100644 docs/milestone-1-spec.md delete mode 100644 docs/milestone-2-spec.md delete mode 100644 docs/milestone-3-spec.md delete mode 100644 docs/milestone-4-spec.md delete mode 100644 docs/pm-analysis.md delete mode 100644 docs/posting-pipeline.md delete mode 100644 docs/qa-results.md delete mode 100644 docs/qa-test-plan.md delete mode 100644 docs/research-findpenguins.md delete mode 100644 docs/research-polarsteps.md delete mode 100644 docs/summary.md delete mode 100644 docs/superpowers/plans/2026-06-18-ui-redesign.md diff --git a/docs/bugs-and-fixes.md b/docs/bugs-and-fixes.md deleted file mode 100644 index d93b94e..0000000 --- a/docs/bugs-and-fixes.md +++ /dev/null @@ -1,136 +0,0 @@ -# Bugs & Fixes - -Backlog of confirmed bugs with root cause analysis and implementation spec for the fix. - ---- - -## BUG-001 — New entry not visible after form submission - -**Status:** fixed 2026-06-18 -**Reported:** 2026-06-18 - -### Symptom - -After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/tracker` feed or in the Grav Admin panel until the cache is manually flushed. - -### Root cause - -Grav's page-tree cache (`cache/doctrine/`) is not invalidated when `add-page-by-form` writes a new page to disk. The tracker template uses `page.children`, which Grav serves from cache — so the new child page is invisible until the cache is cleared. - -### Workaround (manual) - -Run in terminal after each submission: - -```bash -docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache" -``` - -### Fix spec - -Wire cache-clear into the form process so it happens automatically on every successful submission. - -**Approach — custom Grav plugin event hook:** - -1. Create a small plugin `user/plugins/cache-on-save/` with one event listener: - - Listen on `onFormProcessed` - - When the form name is `new-entry`, call `$this->grav['cache']->deleteAll()` (note: `clear()` does not exist on `Grav\Common\Cache` in Grav 1.7) -2. Enable the plugin in `user/config/plugins/cache-on-save.yaml` - -This is the cleanest approach: it fires exactly once per successful submission, requires no changes to `post-form.md`, and works for any future forms too. - -**Alternative — disable page cache entirely:** - -Set `cache: { enabled: false }` in `system.yaml`. Simpler but degrades frontend performance; not recommended for production. - -### Files to create/modify - -| File | Change | -|------|--------| -| `user/plugins/cache-on-save/cache-on-save.php` | New plugin, ~30 lines | -| `user/plugins/cache-on-save/cache-on-save.yaml` | Plugin manifest, enabled: true | -| `user/config/plugins/cache-on-save.yaml` | Runtime config, enabled: true | - -### Acceptance criteria - -1. Submit a new post via `/post` -2. Navigate to `/tracker` — the new entry is visible immediately, no manual cache flush needed -3. Grav Admin also shows the new page immediately - ---- - -## BUG-002 — Stale Twig cache after theme file changes - -**Status:** fixed 2026-06-18 -**Reported:** 2026-06-18 - -### Symptom - -After theme template files are added or modified (e.g., creating `partials/base.html.twig`), Grav's Twig compiled-template cache still holds the old compiled version. Pages that extend the changed file throw 500 errors like "Template partials/base.html.twig is not defined" even though the file exists on disk. - -### Root cause - -Grav caches compiled Twig templates in `cache/twig/`. When a new file is added, existing templates that reference it don't know to recompile — their cache entries are still valid from their own mtime perspective. - -### Workaround (manual) - -Run after any theme file is added or changed: - -```bash -docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache" -``` - -### Fix spec - -Disable Twig template caching in development via `user/config/system.yaml`: - -```yaml -twig: - cache: false -``` - -Acceptable for a single-user dev setup — eliminates both BUG-001's side-effect and this bug entirely. Performance cost is negligible at one-user scale. On production, leave Twig cache enabled (it's fine there because template files don't change at runtime). - -**Files to change:** - -| File | Change | -|------|--------| -| `user/config/system.yaml` | Add `twig: { cache: false }` under development section | - -### Acceptance criteria - -1. Add a new theme template file -2. Reload any page — no 500 error, template works immediately without manual cache flush - ---- - -## BUG-003 — One post per day limit; silent failure on duplicate date - -**Status:** fixed 2026-06-18 -**Reported:** 2026-06-18 - -### Symptom - -Submitting a second post with the same date as an existing entry shows "Entry posted successfully!" but creates no file. The user's post is silently discarded. - -### Root cause - -The `add-page-by-form` plugin built the page slug from date only (`Y-m-d`), producing folder names like `2026-06-18.entry`. With `overwrite_mode: false`, if that folder already exists the plugin skips page creation but does not abort — the `message` process step runs regardless, showing a false success. - -### Fix - -Change the slug template in `user/pages/02.post/post-form.md` to include time and title: - -```twig -{{ form.value.date|date('Y-m-d-Hi') }}-{{ form.value.title|lower|regex_replace('/[^a-z0-9]+/', '-')|trim('-') }} -``` - -Example: title "Arrived in Tokyo" at 14:30 on 2026-06-18 → `2026-06-18-1430-arrived-in-tokyo` - -The slug is locked at creation time. Renaming the title afterwards does not change the URL. - -### Acceptance criteria - -1. Submit two posts on the same day with different times or titles — both appear in `/tracker` as separate entries -2. Renaming a post's title in the frontmatter does not break its URL - ---- diff --git a/docs/design/design-spec.md b/docs/design/design-spec.md deleted file mode 100644 index b6519d9..0000000 --- a/docs/design/design-spec.md +++ /dev/null @@ -1,368 +0,0 @@ -# Into the East — Design Spec - -**Date:** 2026-06-18 -**Status:** Approved for implementation - ---- - -## 1. Direction - -**The brief:** A personal travel journal, sole author, trip to East Asia. Three weeks to implement before departure. Audience is both friends/family and the occasional curious stranger. - -**The position:** Neither Polarsteps nor FindPenguins. Both optimize for social sharing of travel data. This site optimizes for **the story** — and should feel like reading a well-edited travel journal, not using an app. - -**What we steal from each:** -- Polarsteps: photography-first hierarchy, airy whitespace, map as the emotional spine of the trip -- FindPenguins: typography as brand identity, stats as trophy case, hierarchical trip → entry structure - -**What we do better than both:** -- Web-native: fast, linkable, no install, works on any browser -- Single author = pure editorial voice, no social noise -- Full CSS control = real typographic identity, not generic app chrome -- Editorial feel: more travel magazine, less productivity dashboard - -**Aesthetic direction:** Field notes. The kind of journal a thoughtful traveler would carry — clean, direct, lets the photography speak. Sophisticated without effort. - -**The one aesthetic risk:** Full-bleed hero photography with a translucent date+location overlay at the bottom of each card. The photo IS the entry card — not a thumbnail beside text. This is the single element that distinguishes this design from both reference apps and from typical blog layouts. - ---- - -## 2. Color System - -### Palette - -| Token | Hex | Usage | -|---|---|---| -| `--color-ink` | `#17171A` | Primary text (near-black with cool undertone, like ink) | -| `--color-ink-2` | `#4A4850` | Secondary text, body paragraphs | -| `--color-ink-muted` | `#9896A0` | Labels, timestamps, captions, placeholder text | -| `--color-paper` | `#F7F5F2` | Page background (warm paper white, not blue-white) | -| `--color-canvas` | `#FFFFFF` | Card backgrounds, modals, form surfaces | -| `--color-border` | `#E8E6E3` | Standard dividers, card borders | -| `--color-border-soft` | `#F0EDEA` | Subtle section dividers | -| `--color-accent` | `#1F6B5A` | Deep teal — brand color, links, CTAs, active states | -| `--color-accent-hover` | `#185647` | Darkened accent for hover/pressed states | -| `--color-accent-light` | `#EBF5F2` | Pale teal for highlight backgrounds | -| `--color-accent-on` | `#FFFFFF` | Text on accent-colored surfaces | - -### Rationale for accent color - -Deep teal `#1F6B5A` was chosen over: -- Blue (#0066cc current): too generic, too tech -- Orange/saffron: clichéd for "Asia" travel design -- Terracotta/cream: the most common default for lifestyle/travel blogs - -Teal evokes bamboo, celadon porcelain, ancient jade, the color of temple gardens — all without being literal or kitsch. It works cleanly against both the warm paper background and white card surfaces. - ---- - -## 3. Typography - -### Fonts - -| Role | Family | Fallback | Source | -|---|---|---|---| -| Display / Headings | DM Serif Display | Georgia, serif | Google Fonts | -| UI / Body / Labels | DM Sans | -apple-system, BlinkMacSystemFont, sans-serif | Google Fonts | - -**Google Fonts URL:** -``` -https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,400&family=DM+Serif+Display:ital@0;1&display=swap -``` - -**Why this pairing:** -DM Serif Display has a calligraphic quality — slightly editorial, authoritative but not stiff. Paired with DM Sans (its designed companion) the system is cohesive. DM Sans is neutral and highly legible at all sizes. Both are under-used relative to Inter/Lato/Playfair, so the combination has a distinctive voice without being trendy. - -### Type Scale - -| Token | Size | Line Height | Usage | -|---|---|---|---| -| `--text-xs` | 0.75rem (12px) | 1.5 | Badges, captions | -| `--text-sm` | 0.875rem (14px) | 1.5 | Meta, timestamps, labels | -| `--text-base` | 1rem (16px) | 1.65 | Body paragraphs | -| `--text-md` | 1.125rem (18px) | 1.55 | Lead text, intro paragraphs | -| `--text-lg` | 1.375rem (22px) | 1.35 | Subheadings, card titles (mobile) | -| `--text-xl` | 1.75rem (28px) | 1.25 | Entry card titles | -| `--text-2xl` | 2.25rem (36px) | 1.2 | Page headings, entry titles (desktop) | -| `--text-3xl` | 3rem (48px) | 1.1 | Hero entry title | - -### Usage rules - -- Entry titles: `--font-display`, `--text-xl` (mobile) / `--text-2xl` (desktop) -- Site title in header: `--font-display`, `--text-lg` -- All other UI text: `--font-ui` -- Body paragraphs: `--font-ui`, `--text-base`, `--leading-normal` -- Timestamps/badges: `--font-ui`, `--text-xs`, uppercase, `letter-spacing: 0.07em` - ---- - -## 4. Spacing & Layout - -### Spacing scale (4px base unit) - -| Token | Value | -|---|---| -| `--space-1` | 0.25rem (4px) | -| `--space-2` | 0.5rem (8px) | -| `--space-3` | 0.75rem (12px) | -| `--space-4` | 1rem (16px) | -| `--space-5` | 1.25rem (20px) | -| `--space-6` | 1.5rem (24px) | -| `--space-8` | 2rem (32px) | -| `--space-10` | 2.5rem (40px) | -| `--space-12` | 3rem (48px) | -| `--space-16` | 4rem (64px) | - -### Layout - -- Content max-width: `720px` (comfortable reading at any font size) -- Page horizontal padding: `1.25rem` (mobile), `1.5rem` (desktop ≥520px) -- Header height: `60px` (fixed, for JS offset calculations) -- Map page: full viewport, no content max-width constraint - -### Border radius - -| Token | Value | Usage | -|---|---|---| -| `--radius-sm` | 4px | Photo corners, small chips | -| `--radius-md` | 8px | Cards, buttons, inputs | -| `--radius-lg` | 12px | Large cards, modals | -| `--radius-full` | 9999px | Pills, badges | - -### Shadows - -| Token | Value | Usage | -|---|---|---| -| `--shadow-sm` | `0 1px 3px rgba(0,0,0,0.08)` | Stat blocks, subtle elevation | -| `--shadow-md` | `0 4px 12px rgba(0,0,0,0.10)` | Cards on hover, dropdowns | -| `--shadow-lg` | `0 8px 24px rgba(0,0,0,0.14)` | Lightbox, modals | - ---- - -## 5. Component Inventory - -### 5.1 Site Header - -``` -[ into the east ] [ Journal Map Stats ] -← accent bar across top (3px) ─────────────────────────────── -``` - -- Top border: `3px solid var(--color-accent)` — thin accent bar signals the brand color without decorating -- Site title: DM Serif Display, `--text-lg`, no decoration -- Nav links: DM Sans, `--text-sm`, weight 500, `--color-ink-2` -- Active nav link: `--color-accent`, weight 600 -- Mobile: same layout, title slightly smaller, nav links compact -- Background: `--color-canvas` (white), bottom border `1px solid var(--color-border)` - -### 5.2 Entry Feed Card — With Photo - -``` -┌─────────────────────────────────────┐ -│ │ -│ [photo] │ ← full-width, 16:9, rounded corners -│ │ -│ 18 JUN · 📍 Kyoto, Japan │ ← overlaid at bottom, gradient mask -└─────────────────────────────────────┘ - Arrived in Tokyo ← DM Serif Display, --text-xl - After 14 hours of flying I finally ← body excerpt, --color-ink-2 - set foot on Japanese soil... - Read entry → ← --color-accent, --text-sm -``` - -- Photo: `aspect-ratio: 16/9`, `object-fit: cover`, `border-radius: var(--radius-md)` -- Photo has a `linear-gradient(to top, rgba(0,0,0,0.55), transparent)` overlay at the bottom 40% -- Date + location sit on top of gradient in white text (`rgba(255,255,255,0.92)`) -- On hover: photo scales to 1.03 (subtle zoom, 0.4s ease) -- Title below photo: DM Serif Display, hover turns `--color-accent` -- Card separation: `padding-bottom: var(--space-12)` + `border-bottom: 1px solid var(--color-border)` - -### 5.3 Entry Feed Card — No Photo - -When no photo is available, fall back to a text-only layout: - -``` - 18 JUN 2026 · 📍 Kyoto, Japan ← meta row, --text-sm, --color-ink-muted - - Arrived in Tokyo ← DM Serif Display, --text-xl - After 14 hours of flying... - Read entry → -``` - -- No photo container -- Meta (date + location) on one line above title, small + muted - -### 5.4 Single Entry Page - -``` - Wednesday, 18 June 2026 ← --text-sm, --color-ink-muted, uppercase - 📍 Kyoto, Japan · ⛅ Partly cloudy · 22°C - - Arrived in Tokyo ← DM Serif Display, --text-2xl / --text-3xl - ───────────────────────────────────── - Body text content... ← --font-ui, --text-base/md - - [Photo gallery — 2 or 3 col grid] - - ← Back to journal -``` - -- The entry title uses `--font-display` at largest scale -- A thin `--color-border` rule separates the header from the body -- Body text is `--text-md` (18px) for comfortable long-form reading -- Full-bleed hero option: if a `hero_image` is set, it spans the full content width with a bottom margin - -### 5.5 Post Form (Author View) - -``` - New Entry - - Title * [________________________] - Date & Time [2026-06-18 14:30 ] - What happened [ ] - today? [ ] - [ ] - - Photos [ + Add photos (max 4) ] - - City [________________________] - Country [________________________] - - [ 📍 Get Location ] [ 🌤 Get Weather ] - ✓ Location captured: Kyoto, Japan ← status line - - [ Post Entry ] -``` - -UX changes from current: -- Lat/lng inputs **hidden from the UI** (remain in the form as `display:none` for data capture, filled by JS) -- Location status shows captured city/country + coordinates in a single line (not separate status paragraphs) -- Photo upload area: larger touch target, visual indication of count -- "Post Entry" button: `--color-accent` background, full-width on mobile, `min-height: 52px` -- Form fields: `--radius-md` corners, `--color-border` border, focus ring in `--color-accent` -- Section spacing: generous vertical rhythm on mobile - -### 5.6 Stats Page - -``` - ┌────────────┐ ┌────────────┐ - │ 42 │ │ 18 │ - │ days on │ │ entries │ - │ the road │ │ posted │ - └────────────┘ └────────────┘ - ┌────────────┐ ┌────────────┐ - │ 6 │ │ ~14,200 │ - │ countries │ │ km │ - │ visited │ │ traveled │ - └────────────┘ └────────────┘ - - Countries visited - Japan · South Korea · Mongolia · Russia · Finland · Estonia -``` - -- Numbers: `--font-display`, `--text-3xl`, `--color-accent` -- Labels: `--font-ui`, `--text-xs`, uppercase, `--color-ink-muted` -- Cards: white, `--shadow-sm`, `--radius-md`, centered - -### 5.7 Map Page - -Minimal changes — the map itself is good. Style improvements: -- Leaflet popups: match the new design (DM Sans, `--radius-md`, `--shadow-md`) -- Markers: keep current circle style, update color to `--color-accent` -- Feed mini-map wrapper: match `--radius-md`, `--border` - ---- - -## 6. UX Flows - -### 6.1 Reader — First Visit - -1. Land on `/tracker` (journal feed) -2. See mini-map above fold (if entries exist) — route tells the geographic story at a glance -3. First entry card: full-bleed hero photo with date/location overlay — immediate emotional pull -4. Scroll through chronological entries -5. Tap/click entry → entry detail page -6. Navigate back via "← Back to journal" - -**Key principle:** The reader should understand the journey spatially (mini-map) and emotionally (hero photo) before reading a single word. - -### 6.2 Reader — Navigation - -- Journal: primary destination, the feed -- Map: geographic exploration mode -- Stats: quick numbers, satisfying progress indicator -- No account required, no social friction, no login prompt for readers - -### 6.3 Author — Posting from Mobile - -1. Navigate to `/post` (bookmark on home screen) -2. Already logged in (Grav session persists) — form loads directly -3. **Title**: tap → type (autofocused) -4. **Date & Time**: auto-filled to now, adjust if needed -5. **Content**: write what happened -6. **Photos**: tap "Add photos" → camera or gallery → select up to 4 -7. **Location**: tap "📍 Get Location" → GPS fires → status shows "Kyoto, Japan · 34.985, 135.758" in one line -8. **Weather**: tap "🌤 Get Weather" (works only if location was captured) → status shows "Partly cloudy · 22°C" -9. **City/Country**: auto-populated from GPS is a nice-to-have for v2; in v1 type manually if needed -10. Tap "Post Entry" → success message → 2-second pause → redirect to /tracker (new entry visible at top) - -**Key principles:** -- One-thumb operation for all critical actions on mobile -- Location/weather are conveniences, not blockers — can skip both -- Visual feedback is immediate (status line updates on GPS response) -- After submit: don't leave author on a success message page; redirect to see their new post - ---- - -## 7. Mobile Specifics - -### Touch targets -- All interactive elements: `min-height: 44px`, `min-width: 44px` (Apple HIG standard) -- Form buttons: `min-height: 52px` on the post form (primary CTA) -- Nav links: `padding: 0.5rem 0.75rem` - -### Viewport concerns -- Map page: `height: calc(100vh - 60px)`, `touch-action: none` on map container — prevents scroll trap -- Photo lightbox: full viewport overlay, swipe-friendly (keyboard + click already implemented) -- Form on mobile: single-column, generous input padding `0.875rem 1rem`, `font-size: 1rem` (prevents iOS zoom on focus) - -### Performance -- Google Fonts: loaded with `preconnect` hints -- Images: `loading="lazy"` on all non-above-fold images (already in place) -- Leaflet: loaded from CDN, only on pages that need it -- No new JS frameworks — vanilla JS throughout - ---- - -## 8. Tech Stack Decision - -**Keep Grav CMS.** With a 3-week timeline, replacing it would consume all available time on migration rather than design improvements. - -| Layer | Decision | Rationale | -|---|---|---| -| Backend | Grav CMS (PHP, Twig) — unchanged | Works, flat-file, no DB | -| CSS | Vanilla CSS + custom properties (design tokens) | No build step, full control, ships as one file | -| JS | Vanilla JS — unchanged | Current JS is well-structured, scope doesn't justify a framework | -| Icons | Unicode + emoji (current) | No dependency, works everywhere | -| Fonts | Google Fonts via CDN | Two fonts, display-swap, negligible impact | -| Maps | Leaflet.js (current) | Already in use, no reason to change | -| Build | None — no build pipeline | Grav's asset pipeline handles minification if needed | - -**No Alpine.js, no TypeScript, no Tailwind.** The site has clean vanilla JS and CSS today; a redesign is about visual quality, not framework migration. Introducing a build pipeline on a 3-week timeline is a distraction. - ---- - -## 9. What Changes From Current Design - -| Area | Current | New | -|---|---|---| -| Typography | System sans-serif only | DM Serif Display for headings + DM Sans for UI | -| Accent color | `#0066cc` (generic blue) | `#1F6B5A` (deep teal) | -| Background | `#ffffff` (pure white) | `#F7F5F2` (warm paper) | -| Entry cards | Thumbnail + text below | Full-bleed 16:9 photo with overlay | -| Header | No visual identity | Accent top-border, typographic title | -| Design tokens | Hardcoded values throughout | CSS custom properties throughout | -| Post form | Lat/lng visible inputs | Lat/lng hidden, single status line | -| Font loading | None | Google Fonts DM pairing | -| Hover states | Minimal | Photo zoom, title color change | -| Stat numbers | `#0066cc` | `--color-accent` (#1F6B5A) | diff --git a/docs/milestone-1-spec.md b/docs/milestone-1-spec.md deleted file mode 100644 index 6413fff..0000000 --- a/docs/milestone-1-spec.md +++ /dev/null @@ -1,193 +0,0 @@ -# Milestone 1 Spec — Entry Enrichment - -**Goal:** Every entry is richer out of the box — location name shown, weather auto-captured, photos in a proper gallery, hero image visible on the feed. - ---- - -## User Stories - -- As a traveler (Mischa), when I submit the post form, I want my current weather conditions auto-filled so I don't have to look them up manually. -- As a traveler, I want to type my city and country once and have it appear on the entry and in the feed card, so readers know where I am without reading the whole post. -- As a reader, when I scan the feed, I want to see a thumbnail photo and location for each entry so I can quickly get a sense of where Mischa is and whether to read the full entry. -- As a reader, when I open an entry, I want to see all uploaded photos in a gallery I can browse, not a wall of raw images. -- As a traveler, when I submit a form without photos, the entry should still display cleanly with no broken image placeholders. - ---- - -## Feature Details - -### 1.1 — Location Name Field on Post Form - -**What:** Add two text fields to the post form: `location_city` and `location_country`. - -**Behavior:** -- Both are optional (GPS coordinates are also optional) -- Placeholder text: "e.g. Kyoto" and "e.g. Japan" -- Displayed below the lat/lng fields -- On submit, stored in entry frontmatter as `location_city` and `location_country` -- On the form, shown as a single labeled group "Location Name" with two side-by-side inputs on desktop, stacked on mobile - -**Edge cases:** -- If left blank: entry shows no location badge. No error, no broken UI. -- Long city names (e.g. "Ulaanbaatar") must not overflow card layout. -- Special characters (accents, non-Latin) must render correctly. - -**Mobile behavior:** Both fields full-width, stacked, 44px min touch targets. - ---- - -### 1.2 — Weather Auto-Fetch on Post Form - -**What:** A "Get Weather" button on the post form that calls the Open-Meteo free API (no API key) using the lat/lng already entered, and fills hidden weather fields. - -**Fields to fetch and store:** -- `weather_temp_c` — temperature in Celsius (integer) -- `weather_desc` — short description: one of: Sunny, Partly cloudy, Cloudy, Foggy, Drizzle, Rain, Snow, Thunderstorm (derived from WMO weather code) - -**WMO code mapping (Open-Meteo uses WMO codes):** -- 0 → Sunny -- 1,2 → Partly cloudy -- 3 → Cloudy -- 45,48 → Foggy -- 51,53,55,56,57 → Drizzle -- 61,63,65,66,67,80,81,82 → Rain -- 71,73,75,77,85,86 → Snow -- 95,96,99 → Thunderstorm - -**API call:** -``` -https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lng}¤t=temperature_2m,weather_code&temperature_unit=celsius -``` - -**UX flow:** -1. User fills in lat/lng (manually or via "Get Location" button) -2. User taps "Get Weather" button -3. Button shows "Fetching…" while loading -4. On success: fills temp and desc fields (visible, editable text inputs) -5. On failure (no network, no lat/lng): shows inline error "Could not fetch weather — enter manually" - -**Edge cases:** -- If lat/lng not filled when button tapped: show inline error "Enter coordinates first" -- Weather fields are always editable manually (auto-fill is a convenience, not mandatory) -- If weather fields left blank: entry shows no weather badge. No broken UI. -- Open-Meteo returns current conditions, not historical — this is fine for posting in real time - -**Mobile behavior:** "Get Weather" button is full-width, 44px height, placed immediately below the lat/lng + location name fields. - ---- - -### 1.3 — Weather Display on Entry Page - -**What:** If `weather_temp_c` or `weather_desc` is present in frontmatter, display a weather badge on the entry page. - -**Display format:** `☀️ Sunny · 28°C` (icon + description + temperature) -- Icon chosen from a small set based on `weather_desc`: - - Sunny → ☀️ - - Partly cloudy → ⛅ - - Cloudy → ☁️ - - Foggy → 🌫️ - - Drizzle → 🌦️ - - Rain → 🌧️ - - Snow → ❄️ - - Thunderstorm → ⛈️ - -**Placement:** In the entry header, between the date and the body text. Same line as GPS coordinates if those are shown. - -**Edge cases:** -- Only temp, no desc → show temp only -- Only desc, no temp → show desc only -- Neither → hide weather section entirely -- Temperature should always be integer (round if float) - ---- - -### 1.4 — Location Badge on Feed Cards and Entry Page - -**What:** Display `location_city, location_country` as a small badge on tracker feed cards and at the top of entry pages. - -**Feed card:** Below the date, above the excerpt. Format: `📍 Kyoto, Japan` - -**Entry page:** In the header below the date, above the content. Format: `📍 Kyoto, Japan` - -**Edge cases:** -- Only city, no country → `📍 Kyoto` -- Only country, no city → `📍 Japan` -- Neither → location badge hidden entirely -- Long location names: truncate with ellipsis at 30 chars on cards (full text on entry page) - ---- - -### 1.5 — Photo Gallery on Entry Page - -**What:** Photos uploaded to an entry should display in a responsive grid gallery with lightbox (click to enlarge). - -**Implementation approach:** Use Grav's native media collection for the entry page. Each `.entry` folder contains its photos. Render them in a grid in `entry.html.twig`. Use a minimal vanilla JS lightbox — no external framework. - -**Gallery behavior:** -- Photos displayed in a 2-column grid on mobile, 3-column on desktop -- Each thumbnail is square-cropped, 150px on mobile -- Clicking/tapping a thumbnail opens a lightbox overlay -- Lightbox: dark overlay, full-size image centered, tap/click outside or press Escape to close -- Left/right navigation arrows in lightbox (swipe on mobile) -- No captions needed for v1 - -**Edge cases:** -- 0 photos: gallery section hidden entirely -- 1 photo: still uses grid (single item), lightbox works -- Many photos (>10): gallery still renders (no hard limit on display) -- Non-image files in the media folder: skip them (only render jpg, jpeg, png, webp, gif) - ---- - -### 1.6 — Hero Image on Tracker Feed Cards - -**What:** If an entry has photos, the first photo (or the one named in `hero_image` frontmatter) appears as a thumbnail on the tracker feed card. - -**Implementation:** In `tracker.html.twig`, for each entry: -1. If `entry.header.hero_image` is set, use `entry.media[entry.header.hero_image]` -2. Else, use the first image in `entry.media` sorted by name -3. Render as a 16:9 aspect-ratio thumbnail, full width of card, above the title - -**Edge cases:** -- No photos: card shows no image, just text. No broken `` tag. -- `hero_image` set but file missing: fall back to first media file, or no image -- Very tall/wide images: CSS `object-fit: cover` maintains card aspect ratio - ---- - -## Out of Scope (Milestone 1) - -- Map features (Milestone 2) -- Statistics page (Milestone 3) -- Video support -- Comments or reactions -- Automated reverse geocoding (city name comes from form input, not auto-detected) -- Altitude display (data may not be present) -- Historical weather (Open-Meteo current endpoint only) - ---- - -## Acceptance Criteria - -1. Post form has `location_city` and `location_country` fields that save to entry frontmatter -2. Post form has "Get Weather" button that fills `weather_temp_c` and `weather_desc` via Open-Meteo when lat/lng are provided -3. Entry page shows weather badge when weather fields are present; hidden when absent -4. Entry page shows location badge `📍 City, Country` when location fields are present; hidden when absent -5. Tracker feed card shows location badge when present -6. Tracker feed card shows a hero image when photos exist for an entry -7. Entry page shows a 2-col (mobile) / 3-col (desktop) photo grid -8. Clicking any photo opens a full-screen lightbox with prev/next navigation -9. Pressing Escape or clicking outside lightbox closes it -10. All fields are optional — empty values produce no broken UI elements -11. All interactive elements meet 44px minimum touch target on mobile -12. Form submits correctly with all new fields populated or all blank - ---- - -## Design Notes - -- Weather and location badges should be subtle — small text, muted color, not the visual focus -- Use emoji icons for weather — universal, no icon font dependency -- Gallery grid: `gap: 4px` between thumbs, no borders, square crops -- Lightbox: `background: rgba(0,0,0,0.92)`, image centered with `max-height: 90vh` -- Feed card image: `aspect-ratio: 16/9`, `object-fit: cover`, rounded top corners matching card diff --git a/docs/milestone-2-spec.md b/docs/milestone-2-spec.md deleted file mode 100644 index 2e7337b..0000000 --- a/docs/milestone-2-spec.md +++ /dev/null @@ -1,166 +0,0 @@ -# Milestone 2 Spec — Interactive Map - -**Goal:** A `/map` page shows all entries as markers on an interactive Leaflet.js map, connected by a chronological route line, with popups linking to entries. - ---- - -## User Stories - -- As a reader, I want to see a world map showing where Mischa has been so I can understand the journey at a glance without reading every entry. -- As a reader, I want to click a map marker and see the entry date, title, and a thumbnail — and be able to click through to the full entry. -- As a reader on mobile, I want to pan and pinch-zoom the map with my fingers without the page scrolling underneath. -- As a traveler (Mischa), I want the map to automatically include every entry that has lat/lng data — I should not need to do any manual map maintenance. -- As a reader, I want the map to show the route line connecting stops in the order they were visited, so the journey makes narrative sense. - ---- - -## Feature Details - -### 2.1 — Map Page - -**Route:** `/map` - -**Template:** `map.html.twig` — extends `partials/base.html.twig` - -**Page file:** `user/pages/03.map/map.md` - -**Content:** -- Full-viewport-height map container below the site header -- Leaflet.js loaded from CDN (jsDelivr): `https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js` -- Leaflet CSS from same CDN -- Tile layer: OpenStreetMap (free, no API key): `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png` -- Attribution: "© OpenStreetMap contributors" - -**Map initialization:** -- Default zoom: auto-fit to bounds of all markers (use `map.fitBounds()`) -- If no entries with GPS data: show world view, zoom 2, centered at 0,0 with a message "No locations yet" -- Min zoom: 2, Max zoom: 18 - ---- - -### 2.2 — Entry Data Serialization - -**How entries reach the map JS:** - -In `map.html.twig`, Grav's Twig will iterate all published entries under `/tracker` and serialize them to a JSON array embedded in a ` -{% endblock %} -``` - -- [ ] **Step 3: Replace the Post form CSS section** - -Find `/* ── Post form ──` in style.css. Replace it entirely with: - -```css -/* ── Post form ──────────────────────────────────────────────────────────────── */ - -.post-form-wrap h1 { - font-family: var(--font-display); - font-size: var(--text-xl); - font-weight: 400; - margin-bottom: var(--space-6); - color: var(--color-ink); -} - -/* Hide GPS coordinate fields — filled by JS, not user-facing */ -.gps-hidden-field { display: none !important; } - -/* Grav form field wrappers */ -.form-field { margin-bottom: var(--space-5); } -.form-label label { - display: block; - font-size: var(--text-sm); - font-weight: 600; - color: var(--color-ink); - margin-bottom: var(--space-2); -} - -.form-field input[type="text"], -.form-field input[type="email"], -.form-field input[type="datetime-local"], -.form-field textarea { - width: 100%; - font-family: var(--font-ui); - font-size: var(--text-base); - padding: 0.875rem 1rem; - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - background: var(--color-canvas); - color: var(--color-ink); - min-height: 44px; - transition: border-color 0.15s; - -webkit-appearance: none; -} - -.form-field input:focus, -.form-field textarea:focus { - outline: 2px solid var(--color-accent); - outline-offset: 1px; - border-color: var(--color-accent); -} - -.form-field textarea { resize: vertical; min-height: 160px; line-height: var(--leading-normal); } - -/* Submit button — Grav renders it as .btn or input[type=submit] */ -.form-actions input[type="submit"], -.form-actions .btn, -.form-actions button[type="submit"] { - display: block; - width: 100%; - padding: 1rem; - min-height: 52px; - background: var(--color-accent); - color: var(--color-accent-on); - border: none; - border-radius: var(--radius-md); - font-family: var(--font-ui); - font-size: var(--text-base); - font-weight: 600; - cursor: pointer; - transition: background 0.15s; - margin-top: var(--space-6); -} - -.form-actions input[type="submit"]:hover, -.form-actions button[type="submit"]:hover { background: var(--color-accent-hover); } - -/* Action buttons row (Get Location, Get Weather) */ -.form-action-row { - display: flex; - gap: var(--space-3); - margin-top: var(--space-5); -} - -.btn-action { - flex: 1; - padding: 0.75rem var(--space-3); - min-height: 44px; - background: var(--color-canvas); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - font-family: var(--font-ui); - font-size: var(--text-sm); - font-weight: 500; - cursor: pointer; - color: var(--color-ink); - transition: background 0.15s, border-color 0.15s; -} - -.btn-action:hover { background: var(--color-paper); border-color: var(--color-accent); } - -/* Status feedback lines */ -.form-status { - font-size: var(--text-sm); - color: var(--color-ink-muted); - margin-top: var(--space-2); - min-height: 1.4em; -} - -.form-status--ok { color: var(--color-accent); } -.form-status--err { color: #B44A2A; } -``` - -- [ ] **Step 4: Verify post form** - -Open `http://100.96.115.96:8081/post` (logged in). Verify: -- Lat/lng inputs not visible (`.gps-hidden-field` hidden via CSS) -- Inputs have rounded corners, proper padding, focus ring in teal -- "Get Location" and "Get Weather" buttons side by side, same width -- "Post Entry" (or whatever the submit label is) in teal, full-width -- Tap "Get Location" → status line shows "✓ Location captured · lat, lng" in teal -- Mobile at 375px: all inputs and buttons are thumb-friendly - -- [ ] **Step 5: Commit** - -```bash -cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/user -git add pages/02.post/post-form.md themes/intotheeast/templates/post-form.html.twig themes/intotheeast/css/style.css -git commit -m "feat: redesign post form — hide GPS fields, teal CTA, better mobile UX" -``` - ---- - -### Task 7: Stats + map + mini-map styling - -**Files:** -- Modify: `user/themes/intotheeast/templates/stats.html.twig` -- Modify: `user/themes/intotheeast/css/style.css` (Map, Stats, Mini-map sections) - -**Interfaces:** -- Produces: styled stats page and map page using design tokens - -- [ ] **Step 1: Update stats page heading** - -In `user/themes/intotheeast/templates/stats.html.twig`, replace the `

` tag with: - -```twig -
-

Trip Statistics

-``` - -(Remove the inline `style` attribute from the existing h1.) - -- [ ] **Step 2: Replace Stats + Map + Mini-map CSS sections** - -Find `/* ── Map page ──` through end of `/* ── Mini-map on tracker feed ──`. Replace all three sections with: - -```css -/* ── Map page ───────────────────────────────────────────────────────────────── */ - -.map-page .site-main { max-width: none; padding: 0; } - -.map-container { - height: calc(100vh - var(--site-header-height)); - width: 100%; -} - -.map-empty { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - color: var(--color-ink-muted); - font-style: italic; -} - -/* ── Stats page ─────────────────────────────────────────────────────────────── */ - -.stats-heading { - font-family: var(--font-display); - font-size: var(--text-2xl); - font-weight: 400; - margin-bottom: var(--space-8); - color: var(--color-ink); -} - -.stats-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--space-4); - margin-bottom: var(--space-8); -} - -.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); -} - -.stat-value { - display: block; - font-family: var(--font-display); - font-size: var(--text-3xl); - font-weight: 400; - color: var(--color-accent); - line-height: 1.1; - margin-bottom: var(--space-2); -} - -.stat-label { - display: block; - font-size: var(--text-xs); - font-weight: 600; - color: var(--color-ink-muted); - text-transform: uppercase; - letter-spacing: 0.07em; -} - -.stats-countries { - font-size: var(--text-sm); - color: var(--color-ink-2); - text-align: center; - line-height: 1.9; -} - -.stats-countries-label { - font-weight: 600; - display: block; - margin-bottom: var(--space-2); - color: var(--color-ink); - text-transform: uppercase; - font-size: var(--text-xs); - letter-spacing: 0.07em; -} - -.stats-note { - font-size: var(--text-xs); - color: var(--color-ink-muted); - text-align: center; - margin-top: var(--space-6); -} - -/* ── Mini-map on tracker feed ────────────────────────────────────────────────── */ - -.feed-map-wrap { - margin-bottom: var(--space-10); - border-radius: var(--radius-md); - overflow: hidden; - border: 1px solid var(--color-border); - box-shadow: var(--shadow-sm); -} - -.feed-map { - height: 240px; - width: 100%; -} - -@media (min-width: 520px) { - .feed-map { height: 300px; } -} - -.feed-map-link { - display: block; - text-align: right; - font-size: var(--text-xs); - font-weight: 500; - color: var(--color-accent); - text-decoration: none; - padding: var(--space-2) var(--space-4); - background: var(--color-paper); - border-top: 1px solid var(--color-border); -} - -.feed-map-link:hover { color: var(--color-accent-hover); } -``` - -- [ ] **Step 3: Update Leaflet marker colors** - -In `tracker.html.twig`, find the JS that sets marker colors. Update from `#0066cc` to `#1F6B5A` and from `#0044aa` to `#155244`: - -```js -var color = isLatest ? '#155244' : '#1F6B5A'; -``` - -Also update the polyline color: -```js -L.polyline(latLngs, { color: '#1F6B5A', weight: 3, opacity: 0.7 }).addTo(map); -``` - -Do the same in `map.html.twig` — update any hardcoded `#0066cc` colors to `#1F6B5A`. - -- [ ] **Step 4: Verify stats and map pages** - -Open `http://100.96.115.96:8081/stats`. Verify: -- Heading in DM Serif Display -- Numbers in DM Serif Display, teal color -- Stat cards on warm white background with subtle shadow -- Labels uppercase, muted gray - -Open `http://100.96.115.96:8081/map`. Verify: -- Map fills viewport below header -- Markers are teal circles -- Route line is teal -- No horizontal scroll or layout issues - -- [ ] **Step 5: Commit** - -```bash -cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/user -git add themes/intotheeast/templates/stats.html.twig themes/intotheeast/templates/tracker.html.twig themes/intotheeast/templates/map.html.twig themes/intotheeast/css/style.css -git commit -m "feat: apply design tokens to stats, map, and mini-map" -``` - ---- - -### Task 8: Mobile polish + reduced motion + final QA - -**Files:** -- Modify: `user/themes/intotheeast/css/style.css` (add responsive + motion CSS) - -**Interfaces:** -- Produces: fully responsive, accessible design at 320px–1440px viewport widths - -- [ ] **Step 1: Add reduced-motion support** - -At the bottom of `style.css`, append: - -```css -/* ── Accessibility ───────────────────────────────────────────────────────────── */ - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} - -/* Keyboard focus ring: visible on all interactive elements */ -:focus-visible { - outline: 2px solid var(--color-accent); - outline-offset: 2px; -} -``` - -- [ ] **Step 2: Verify header on 320px (smallest phone)** - -Set browser devtools to 320px viewport. Verify: -- Site title and nav links both visible and not overlapping -- If title overflows, add this to style.css: - ```css - @media (max-width: 380px) { - .site-title { font-size: var(--text-md); } - .site-nav a { padding: var(--space-2); font-size: 0.8rem; } - } - ``` - -- [ ] **Step 3: Verify post form on mobile** - -On a real phone (or devtools 375px), open `/post` and verify: -- Inputs do not trigger zoom on focus (font-size is 1rem ≥ 16px — already set) -- "Post Entry" button is thumb-reachable (full-width, 52px min height) -- Get Location / Get Weather buttons are side-by-side, each at least 44px tall -- Status feedback visible after tapping location/weather - -- [ ] **Step 4: Cross-page smoke test checklist** - -Check each of these manually in the browser: - -| Page | Check | -|---|---| -| `/tracker` | Feed loads, entry cards show hero photos with overlays | -| `/tracker` | Text-only cards (no photo) show date+location meta above title | -| `/tracker` | Mini-map renders, teal markers and route | -| `/map` | Full-height map, teal markers, route polyline | -| `/map` | Tap marker → popup with date, title, "Read entry →" link | -| `/stats` | 2×2 grid of stat blocks, teal numbers, correct counts | -| `/tracker/` | Hero image full-width at top (if photos exist) | -| `/tracker/` | Title in DM Serif Display, large | -| `/tracker/` | Photo gallery (2/3-col grid), lightbox opens on tap | -| `/post` | Lat/lng fields NOT visible | -| `/post` | Tap Post Entry → success message → redirects to /tracker | -| Mobile 375px | All pages usable without horizontal scroll | -| Mobile 375px | No font size < 14px for readable text | - -- [ ] **Step 5: Final commit** - -```bash -cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/user -git add themes/intotheeast/css/style.css -git commit -m "feat: add reduced-motion support, keyboard focus, mobile polish" -``` - ---- - -### Task 9: Update documentation - -**Files:** -- Modify: `user/docs/qa-test-plan.md` (add visual QA section) - -- [ ] **Step 1: Add visual design QA section to qa-test-plan.md** - -Append a new section to `user/docs/qa-test-plan.md`: - -```markdown -## Visual Design QA — Redesign Checklist - -**Design spec:** `user/docs/design/design-spec.md` - -### Typography -- [ ] DM Serif Display loads on entry titles, page headings, stats numbers, site title -- [ ] DM Sans loads on all body text, nav, labels, form fields -- [ ] No fallback font (Georgia/system-sans) visible in place of custom fonts - -### Colors -- [ ] Page background is warm paper (#F7F5F2), not pure white -- [ ] All links and CTAs use teal (#1F6B5A), not blue (#0066cc) -- [ ] Active nav link is teal -- [ ] Map markers and route polyline are teal - -### Entry cards -- [ ] Cards with photos show full-bleed 16:9 image -- [ ] Date + location overlay visible on photo gradient -- [ ] Entry title below photo in DM Serif Display -- [ ] Cards without photos show date/location meta row above title -- [ ] Photo zoom on hover (desktop only) - -### Header -- [ ] 3px teal bar at top of header -- [ ] "into the east" title in DM Serif Display -- [ ] Sticky on scroll - -### Post form -- [ ] Lat/lng inputs not visible -- [ ] "✓ Location captured" feedback in teal on success -- [ ] Submit button full-width, teal, 52px+ height - -### Mobile -- [ ] All interactive elements ≥ 44px touch target -- [ ] No horizontal scroll at 375px -- [ ] iOS: no font-size zoom on input focus -``` - -- [ ] **Step 2: Commit documentation** - -```bash -cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/user -git add docs/qa-test-plan.md docs/design/design-spec.md docs/superpowers/plans/2026-06-18-ui-redesign.md -git commit -m "docs: add UI redesign spec, plan, and visual QA checklist" -```