docs: mark frontend polish plan as complete
This commit is contained in:
@@ -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 1–2 are pure CSS (style.css only). Task 3 touches one partial (emoji). Task 4 adds a blueprint field and updates one template. Tasks 5–6 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 5–6
|
||||||
|
- 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.
|
||||||
Reference in New Issue
Block a user