From 7c300faec8bd858878099b4f14bb15c638fe3399 Mon Sep 17 00:00:00 2001 From: Jonathon Wright Date: Mon, 29 Jun 2026 19:08:17 +1000 Subject: [PATCH] home: fix nav active-section at page bottom + exact progress bar; add blog tags to Writing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Nav underline highlighted the wrong section at the bottom (the short last section never crossed the threshold) — force the last section when scrolled to the bottom so Contact lights up instead of Skills - Scroll-progress bar: compute progress from scrollY/maxScroll so it spans a true 0→1 (Motion's default topped out ~0.89 at the bottom) - Writing section: add blog tag chips linking to /blog/tags// --- src/pages/index.astro | 27 ++++++++++++++++++++++++--- src/scripts/anim.ts | 23 ++++++++++++++++++----- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/pages/index.astro b/src/pages/index.astro index 56ad64a..91a4a49 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -8,13 +8,15 @@ import ProjectCard from "../components/ProjectCard.astro"; import PostList from "../components/PostList.astro"; import Contact from "../components/Contact.astro"; import { skills } from "../data/skills"; -import { getPosts } from "../lib/blog"; +import { getPosts, allTags } from "../lib/blog"; import { getCollection } from "astro:content"; const featuredProjects = (await getCollection("projects", (p) => p.data.featured)).sort( (a, b) => a.data.order - b.data.order, ); -const recentPosts = (await getPosts()).slice(0, 3); +const allPosts = await getPosts(); +const recentPosts = allPosts.slice(0, 3); +const postTags = allTags(allPosts); --- @@ -44,6 +46,14 @@ const recentPosts = (await getPosts()).slice(0, 3); Lessons from edge Kubernetes, GPUs, and running infrastructure like it matters — written up as I go.

+ {postTags.length > 0 && ( + + )}

Read the blog → @@ -72,7 +82,18 @@ const recentPosts = (await getPosts()).slice(0, 3); } .writing__intro { max-width: 52ch; - margin-bottom: var(--space-5); + margin-bottom: var(--space-4); color: var(--text-dim); } + .writing__tags { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + margin-bottom: var(--space-6); + } + .writing__tags-label { + font-size: var(--step--1); + color: var(--text-faint); + } diff --git a/src/scripts/anim.ts b/src/scripts/anim.ts index 666e21a..9493fa9 100644 --- a/src/scripts/anim.ts +++ b/src/scripts/anim.ts @@ -72,9 +72,15 @@ function run(): void { * bottom (the progress=1 boundary); the callback maps 0→1 cleanly both ways. */ const bar = document.querySelector(".scroll-progress"); if (bar) { - scroll((progress: number) => { - bar.style.transform = `scaleX(${progress})`; - }); + // Compute progress straight from scroll position so it reliably spans 0→1 + // (Motion's default scroll progress topped out ~0.89 at the true bottom). + const setBar = (): void => { + const max = document.documentElement.scrollHeight - window.innerHeight; + const p = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0; + bar.style.transform = `scaleX(${p})`; + }; + scroll(setBar); + setBar(); } /* 5. Nav — animated active-section underline + tactile hover. */ @@ -112,11 +118,18 @@ function initNav(): void { ); if (sectioned.length) { const setActive = (): void => { - const line = window.innerHeight * 0.4; // section crossing the upper area is "active" + const vh = window.innerHeight; + const ref = vh * 0.45; // the section whose top has passed this reading line is "active" let current: HTMLAnchorElement | null = null; for (const a of sectioned) { const sec = document.getElementById(a.dataset.navId as string); - if (sec && sec.getBoundingClientRect().top <= line) current = a; + if (sec && sec.getBoundingClientRect().top <= ref) current = a; + } + // At the very bottom the last (often short) section may never push its top + // above the ref line — force it, so Contact lights up at the end instead of + // the highlight getting stuck on the previous section (Skills). + if (window.scrollY + vh >= document.documentElement.scrollHeight - 4) { + current = sectioned[sectioned.length - 1]; } if (current !== active) { if (active) under(active, false);