From 257af5d22e4cd8ba965f53387ef4b0213e7dbf20 Mon Sep 17 00:00:00 2001 From: Jonathon Wright Date: Mon, 29 Jun 2026 18:53:35 +1000 Subject: [PATCH] home: add Writing section; fix scroll-progress snap + nav underline reverse - Scroll-progress bar: drive scaleX from scroll progress via callback form; the animation form snapped the bar to 0 at the bottom (progress=1 boundary) - Nav active-underline: recompute active section from scroll position each frame so it tracks upward scroll too (inView-enter died on the way back up) - Add a Writing section to the homepage with recent posts + a blog CTA --- src/pages/index.astro | 21 ++++++++++++++++++- src/scripts/anim.ts | 47 ++++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/pages/index.astro b/src/pages/index.astro index d18e900..56ad64a 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -5,13 +5,16 @@ import Section from "../components/Section.astro"; import About from "../components/About.astro"; import SkillGroup from "../components/SkillGroup.astro"; 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 { 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); --- @@ -36,7 +39,18 @@ const featuredProjects = (await getCollection("projects", (p) => p.data.featured

-
+
+

+ Lessons from edge Kubernetes, GPUs, and running infrastructure like it matters — + written up as I go. +

+ +

+ Read the blog → +

+
+ +
@@ -56,4 +70,9 @@ const featuredProjects = (await getCollection("projects", (p) => p.data.featured margin-top: var(--space-6); text-align: center; } + .writing__intro { + max-width: 52ch; + margin-bottom: var(--space-5); + color: var(--text-dim); + } diff --git a/src/scripts/anim.ts b/src/scripts/anim.ts index 2aba363..666e21a 100644 --- a/src/scripts/anim.ts +++ b/src/scripts/anim.ts @@ -67,9 +67,15 @@ function run(): void { } /* 4. Top scroll-progress bar — cross-browser (replaces the Chromium-only CSS - * animation-timeline). scroll() scrubs the animation by page progress. */ + * animation-timeline). Drive scaleX STRAIGHT from scroll progress via the + * callback form: scroll(animate(...)) snapped the bar back to 0 at the very + * bottom (the progress=1 boundary); the callback maps 0→1 cleanly both ways. */ const bar = document.querySelector(".scroll-progress"); - if (bar) scroll(animate(bar, { scaleX: [0, 1] }, { ease: "linear" })); + if (bar) { + scroll((progress: number) => { + bar.style.transform = `scaleX(${progress})`; + }); + } /* 5. Nav — animated active-section underline + tactile hover. */ initNav(); @@ -97,20 +103,29 @@ function initNav(): void { a.addEventListener("pointerleave", () => under(a, a === active)); } - // Light up the link whose section is in view (homepage anchors only). - for (const a of links) { - const id = a.dataset.navId; - const sec = id ? document.getElementById(id) : null; - if (!sec) continue; - inView( - sec, - () => { - if (active && active !== a) under(active, false); - active = a; - under(a, true); - }, - { amount: 0.5 }, - ); + // Light up the link whose section is currently being read. Driven straight off + // scroll position (recomputed every frame) so it tracks BOTH directions — the + // old per-section inView(enter) fired only on the way DOWN and went dead on the + // way back up. Homepage anchor links only (Projects/Blog have no section here). + const sectioned = links.filter( + (a) => a.dataset.navId && document.getElementById(a.dataset.navId), + ); + if (sectioned.length) { + const setActive = (): void => { + const line = window.innerHeight * 0.4; // section crossing the upper area 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 (current !== active) { + if (active) under(active, false); + active = current; + if (current) under(current, true); + } + }; + scroll(setActive); + setActive(); } }