docs: add trip page filter bar implementation plan
This commit is contained in:
@@ -0,0 +1,472 @@
|
|||||||
|
# Trip Page Filter Bar — 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.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
**Architecture:** Pure client-side. `data-type` attributes on article cards let vanilla JS show/hide by content type. Stats computation is inlined into `trip.html.twig` from `stats.html.twig`. No new files, no Grav config changes, no page navigation.
|
||||||
|
|
||||||
|
**Tech Stack:** Twig (Grav 2.0), vanilla JS (ES5), CSS custom properties
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- CSS variables only — no raw hex values; use tokens from `tokens.css`
|
||||||
|
- ES5 JS — no arrow functions, no `const`/`let`, no template literals (inline script in Twig)
|
||||||
|
- Touch the minimum: only `trip.html.twig` and `style.css`
|
||||||
|
- Do not modify `stats.html.twig`, `dailies.html.twig`, or any sub-page template
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Story card border + data-type attributes
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `user/themes/intotheeast/templates/trip.html.twig:77,119`
|
||||||
|
- Modify: `user/themes/intotheeast/css/style.css:816-819`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Produces: `data-type="journal"` and `data-type="story"` attributes on all article cards — consumed by Tasks 3 and 4
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add data-type to journal article (trip.html.twig line 77)**
|
||||||
|
|
||||||
|
Find this line:
|
||||||
|
```twig
|
||||||
|
<article class="entry-card" id="entry-{{ entry.slug }}" data-lat="{{ entry.header.lat }}" data-lng="{{ entry.header.lng }}">
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```twig
|
||||||
|
<article class="entry-card" id="entry-{{ entry.slug }}" data-type="journal" data-lat="{{ entry.header.lat }}" data-lng="{{ entry.header.lng }}">
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add data-type to story article (trip.html.twig line 119)**
|
||||||
|
|
||||||
|
Find this line:
|
||||||
|
```twig
|
||||||
|
<article class="entry-card entry-card--story" id="entry-{{ entry.slug }}">
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```twig
|
||||||
|
<article class="entry-card entry-card--story" id="entry-{{ entry.slug }}" data-type="story">
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Replace story card border in style.css**
|
||||||
|
|
||||||
|
Find the existing `.entry-card--story` rule (around line 816):
|
||||||
|
```css
|
||||||
|
.entry-card--story {
|
||||||
|
border-left: 3px solid var(--color-accent);
|
||||||
|
padding-left: var(--space-5);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```css
|
||||||
|
.entry-card--story {
|
||||||
|
border: 2px solid var(--color-accent);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-6);
|
||||||
|
background: var(--color-canvas);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify visually**
|
||||||
|
|
||||||
|
Open the trip page in the browser. In DevTools:
|
||||||
|
- Select a journal article → confirm it has `data-type="journal"`
|
||||||
|
- Select a story article → confirm it has `data-type="story"`
|
||||||
|
- Story cards should now appear as a boxed card with a full teal border and rounded corners instead of a left-only bar
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add user/themes/intotheeast/templates/trip.html.twig user/themes/intotheeast/css/style.css
|
||||||
|
git commit -m "feat: add data-type attributes to feed cards; restyle story card with full border"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Filter bar markup + CSS (static, no JS yet)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `user/themes/intotheeast/templates/trip.html.twig:58-62`
|
||||||
|
- Modify: `user/themes/intotheeast/css/style.css` (append after `.home-trip-counts` block, around line 694)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: nothing from prior tasks (static HTML)
|
||||||
|
- Produces: `.trip-filter-bar`, `.trip-filter-btn`, `.trip-stats-btn` CSS classes consumed by Task 3
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace nav with filter bar in trip.html.twig**
|
||||||
|
|
||||||
|
Find the existing nav block (lines 58–62):
|
||||||
|
```twig
|
||||||
|
<nav class="trip-nav">
|
||||||
|
<a href="{{ page.route }}/dailies">Journal</a>
|
||||||
|
<a href="{{ page.route }}/stats">Stats</a>
|
||||||
|
<a href="{{ page.route }}/stories">Stories</a>
|
||||||
|
</nav>
|
||||||
|
```
|
||||||
|
Replace with:
|
||||||
|
```twig
|
||||||
|
<div class="trip-filter-bar">
|
||||||
|
<div class="trip-filter-group">
|
||||||
|
<button class="trip-filter-btn is-active" data-filter="all">All content</button>
|
||||||
|
<button class="trip-filter-btn" data-filter="journal">Journal</button>
|
||||||
|
<button class="trip-filter-btn" data-filter="story">Stories</button>
|
||||||
|
</div>
|
||||||
|
<button class="trip-stats-btn" id="trip-stats-toggle">Stats</button>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add filter bar CSS to style.css**
|
||||||
|
|
||||||
|
After the `.home-trip-counts` rule (around line 694), append:
|
||||||
|
```css
|
||||||
|
/* ── Trip page filter bar ────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.trip-filter-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-3);
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-filter-group {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-filter-btn,
|
||||||
|
.trip-stats-btn {
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-ink-muted);
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
padding: var(--space-1) var(--space-4);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-filter-btn:hover,
|
||||||
|
.trip-stats-btn:hover {
|
||||||
|
color: var(--color-ink);
|
||||||
|
border-color: var(--color-ink-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-filter-btn.is-active,
|
||||||
|
.trip-stats-btn.is-active {
|
||||||
|
color: var(--color-accent);
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
background: var(--color-accent-light);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify visually**
|
||||||
|
|
||||||
|
Open the trip page. Confirm:
|
||||||
|
- Three filter pills (All content / Journal / Stories) and a Stats button appear below the trip title
|
||||||
|
- "All content" pill has teal active styling
|
||||||
|
- Other pills are muted/bordered
|
||||||
|
- Clicking the buttons does nothing yet (no JS)
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add user/themes/intotheeast/templates/trip.html.twig user/themes/intotheeast/css/style.css
|
||||||
|
git commit -m "feat: add filter bar markup and pill button styles to trip page"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Filter JS (show/hide cards by type)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `user/themes/intotheeast/templates/trip.html.twig` (append to the existing `<script>` block at the bottom, before `</script>`)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `data-type` on articles (Task 1); `.trip-filter-btn`, `data-filter` (Task 2)
|
||||||
|
- Produces: working filter interaction
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add an empty-state element to the feed**
|
||||||
|
|
||||||
|
In `trip.html.twig`, find the closing `</div>` of the `.feed` block (after the `{% else %}` empty message). Add a hidden filter-empty message right before `</div>`:
|
||||||
|
```twig
|
||||||
|
<p id="feed-filter-empty" class="feed-empty" style="display:none;"></p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
The full `.feed` block close should look like:
|
||||||
|
```twig
|
||||||
|
{% else %}
|
||||||
|
<p class="feed-empty">No entries yet. The journey is about to begin.</p>
|
||||||
|
{% endif %}
|
||||||
|
<p id="feed-filter-empty" class="feed-empty" style="display:none;"></p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Append filter JS to the existing script block**
|
||||||
|
|
||||||
|
In `trip.html.twig`, find the closing `</script>` tag at the bottom. Insert before it:
|
||||||
|
```javascript
|
||||||
|
(function() {
|
||||||
|
var filterBtns = document.querySelectorAll('.trip-filter-btn');
|
||||||
|
var cards = document.querySelectorAll('[data-type]');
|
||||||
|
var filterEmpty = document.getElementById('feed-filter-empty');
|
||||||
|
|
||||||
|
filterBtns.forEach(function(btn) {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
filterBtns.forEach(function(b) { b.classList.remove('is-active'); });
|
||||||
|
btn.classList.add('is-active');
|
||||||
|
|
||||||
|
var filter = btn.getAttribute('data-filter');
|
||||||
|
var visible = 0;
|
||||||
|
|
||||||
|
cards.forEach(function(card) {
|
||||||
|
var show = filter === 'all' || card.getAttribute('data-type') === filter;
|
||||||
|
card.style.display = show ? '' : 'none';
|
||||||
|
if (show) visible++;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filterEmpty) {
|
||||||
|
if (visible === 0) {
|
||||||
|
filterEmpty.textContent = filter === 'story'
|
||||||
|
? 'No stories yet for this trip.'
|
||||||
|
: 'No entries yet.';
|
||||||
|
filterEmpty.style.display = '';
|
||||||
|
} else {
|
||||||
|
filterEmpty.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify filter behavior**
|
||||||
|
|
||||||
|
Open the trip page. With demo entries loaded (run `make demo-load` if needed):
|
||||||
|
- Click **Journal** → only journal cards visible, story cards hidden
|
||||||
|
- Click **Stories** → only story cards visible, journal cards hidden
|
||||||
|
- Click **All content** → all cards visible again
|
||||||
|
- If no stories exist, clicking Stories shows "No stories yet for this trip."
|
||||||
|
- "All content" pill always has active state after clicking it
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add user/themes/intotheeast/templates/trip.html.twig
|
||||||
|
git commit -m "feat: wire up feed filter — All content / Journal / Stories"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Inline stats block (Twig computation + HTML + toggle JS)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `user/themes/intotheeast/templates/trip.html.twig`
|
||||||
|
- Modify: `user/themes/intotheeast/css/style.css` (append after filter bar CSS from Task 2)
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `.trip-stats-btn#trip-stats-toggle` (Task 2); `journal_entries` variable already set at top of template
|
||||||
|
- Produces: expandable stats block; `STATS_GPS` JS variable for haversine distance
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add stats Twig computation at the top of the template**
|
||||||
|
|
||||||
|
In `trip.html.twig`, after line 19 (`{% set story_count = story_entries|length %}`), add:
|
||||||
|
```twig
|
||||||
|
{# Stats computation #}
|
||||||
|
{% set days_on_road = 0 %}
|
||||||
|
{% set first_ts = null %}
|
||||||
|
{% for entry in journal_entries %}
|
||||||
|
{% set ts = entry.date|date('U') %}
|
||||||
|
{% if first_ts is null or ts < first_ts %}
|
||||||
|
{% set first_ts = ts %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if first_ts is not null %}
|
||||||
|
{% set now_ts = "now"|date('U') %}
|
||||||
|
{% set diff_seconds = now_ts - first_ts %}
|
||||||
|
{% set days_raw = (diff_seconds / 86400)|round(0, 'floor') %}
|
||||||
|
{% set days_on_road = days_raw < 1 ? 1 : days_raw %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% set seen_lower = [] %}
|
||||||
|
{% set country_display = [] %}
|
||||||
|
{% for entry in journal_entries %}
|
||||||
|
{% if entry.header.location_country is not empty %}
|
||||||
|
{% set lower = entry.header.location_country|trim|lower %}
|
||||||
|
{% if lower not in seen_lower %}
|
||||||
|
{% set seen_lower = seen_lower|merge([lower]) %}
|
||||||
|
{% set country_display = country_display|merge([entry.header.location_country|trim]) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% set gps_points = [] %}
|
||||||
|
{% for entry in journal_entries %}
|
||||||
|
{% if entry.header.lat is not empty and entry.header.lng is not empty %}
|
||||||
|
{% set gps_points = gps_points|merge([[entry.header.lat, entry.header.lng]]) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add stats block HTML between filter bar and feed**
|
||||||
|
|
||||||
|
In `trip.html.twig`, find the `<div class="feed">` line and insert the stats block immediately before it:
|
||||||
|
```twig
|
||||||
|
<div id="trip-stats-block" class="trip-stats-block" style="display:none">
|
||||||
|
<div class="trip-stats-grid">
|
||||||
|
<div class="stat-block">
|
||||||
|
<span class="stat-value">{{ days_on_road }}</span>
|
||||||
|
<span class="stat-label">{{ days_on_road == 1 ? 'day' : 'days' }} on the road</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-block">
|
||||||
|
<span class="stat-value">{{ journal_count }}</span>
|
||||||
|
<span class="stat-label">{{ journal_count == 1 ? 'entry' : 'entries' }} posted</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-block">
|
||||||
|
<span class="stat-value">{{ country_display|length }}</span>
|
||||||
|
<span class="stat-label">{{ country_display|length == 1 ? 'country' : 'countries' }} visited</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-block">
|
||||||
|
<span class="stat-value" id="stat-distance">—</span>
|
||||||
|
<span class="stat-label">km traveled</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if country_display|length > 0 %}
|
||||||
|
<p class="trip-stats-countries">{{ country_display|join(' · ') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<p class="trip-stats-note">Distance is approximate — straight lines between entry locations.</p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add stats block CSS to style.css**
|
||||||
|
|
||||||
|
Append after the filter bar CSS added in Task 2:
|
||||||
|
```css
|
||||||
|
/* ── Trip page inline stats block ───────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.trip-stats-block {
|
||||||
|
background: var(--color-canvas);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-6);
|
||||||
|
margin-bottom: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: var(--space-4);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.trip-stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-stats-countries {
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
color: var(--color-ink-2);
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trip-stats-note {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
color: var(--color-ink-muted);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `.stat-block`, `.stat-value`, `.stat-label` are reused from `stats.html.twig` and already have CSS defined. Do not add duplicate rules.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify those existing CSS classes exist**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
grep -n "\.stat-block\|\.stat-value\|\.stat-label" user/themes/intotheeast/css/style.css
|
||||||
|
```
|
||||||
|
Expected: at least 3 matches. If not found, copy from `stats.html.twig`'s inline `<style>` block if one exists.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Add stats toggle JS + haversine distance**
|
||||||
|
|
||||||
|
In `trip.html.twig`, append to the existing `<script>` block (before `</script>`):
|
||||||
|
```javascript
|
||||||
|
var STATS_GPS = {{ gps_points|json_encode|raw }};
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function haversine(lat1, lng1, lat2, lng2) {
|
||||||
|
var R = 6371;
|
||||||
|
var dLat = (lat2 - lat1) * Math.PI / 180;
|
||||||
|
var dLng = (lng2 - lng1) * Math.PI / 180;
|
||||||
|
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
|
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
||||||
|
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||||
|
return R * 2 * Math.asin(Math.sqrt(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalKm = 0;
|
||||||
|
for (var i = 1; i < STATS_GPS.length; i++) {
|
||||||
|
totalKm += haversine(
|
||||||
|
parseFloat(STATS_GPS[i - 1][0]), parseFloat(STATS_GPS[i - 1][1]),
|
||||||
|
parseFloat(STATS_GPS[i][0]), parseFloat(STATS_GPS[i][1])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var distEl = document.getElementById('stat-distance');
|
||||||
|
if (distEl) {
|
||||||
|
distEl.textContent = STATS_GPS.length < 2 ? '—' : '~' + Math.round(totalKm).toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var statsToggle = document.getElementById('trip-stats-toggle');
|
||||||
|
var statsBlock = document.getElementById('trip-stats-block');
|
||||||
|
if (statsToggle && statsBlock) {
|
||||||
|
statsToggle.addEventListener('click', function() {
|
||||||
|
var isOpen = statsBlock.style.display !== 'none';
|
||||||
|
statsBlock.style.display = isOpen ? 'none' : '';
|
||||||
|
statsToggle.classList.toggle('is-active', !isOpen);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Verify stats block**
|
||||||
|
|
||||||
|
Open the trip page with demo entries loaded:
|
||||||
|
- Click **Stats** → inline block expands between filter bar and feed; Stats button turns teal
|
||||||
|
- Block shows: days on road, entries count, countries count, km distance (or `—` if < 2 GPS points)
|
||||||
|
- Countries list shows below the grid if any entries have `location_country`
|
||||||
|
- Click **Stats** again → block collapses, button returns to default style
|
||||||
|
|
||||||
|
- [ ] **Step 7: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add user/themes/intotheeast/templates/trip.html.twig user/themes/intotheeast/css/style.css
|
||||||
|
git commit -m "feat: add inline stats block with toggle to trip page"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Review
|
||||||
|
|
||||||
|
**Spec coverage:**
|
||||||
|
- ✅ Filter bar with All / Journal / Stories (Task 2, 3)
|
||||||
|
- ✅ Mutually exclusive, one active at a time (Task 3 JS)
|
||||||
|
- ✅ JS show/hide via data-type (Task 1, 3)
|
||||||
|
- ✅ Empty state for Stories filter (Task 3)
|
||||||
|
- ✅ Stats as inline expansion (Task 4)
|
||||||
|
- ✅ Stats toggle with active state (Task 4)
|
||||||
|
- ✅ Story card full border (Task 1)
|
||||||
|
- ✅ Sub-pages untouched — no changes to stats.html.twig or dailies.html.twig
|
||||||
|
|
||||||
|
**Placeholder scan:** None — all steps contain exact code.
|
||||||
|
|
||||||
|
**Type consistency:**
|
||||||
|
- `data-filter="story"` on button matches `data-type="story"` on article — comparison in Task 3 JS: `card.getAttribute('data-type') === filter` ✅
|
||||||
|
- `id="trip-stats-toggle"` set in Task 2 HTML, read in Task 4 JS ✅
|
||||||
|
- `id="trip-stats-block"` set in Task 4 HTML, read in Task 4 JS ✅
|
||||||
|
- `id="feed-filter-empty"` set in Task 3 HTML, read in Task 3 JS ✅
|
||||||
|
- `id="stat-distance"` set in Task 4 HTML, read in Task 4 JS ✅
|
||||||
|
- `STATS_GPS` set in Task 4 JS, consumed in Task 4 haversine loop ✅
|
||||||
|
- `.stat-block` / `.stat-value` / `.stat-label` reused from existing CSS — Task 4 Step 4 verifies they exist ✅
|
||||||
Reference in New Issue
Block a user