Files
intotheeast-com/docs/working/plans/2026-06-24-frontend-polish.md
T

423 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.