home: fix nav active-section at page bottom + exact progress bar; add blog tags to Writing
build-and-deploy / build (push) Failing after 10m38s
build-and-deploy / build (push) Failing after 10m38s
- 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/<tag>/
This commit is contained in:
+24
-3
@@ -8,13 +8,15 @@ import ProjectCard from "../components/ProjectCard.astro";
|
|||||||
import PostList from "../components/PostList.astro";
|
import PostList from "../components/PostList.astro";
|
||||||
import Contact from "../components/Contact.astro";
|
import Contact from "../components/Contact.astro";
|
||||||
import { skills } from "../data/skills";
|
import { skills } from "../data/skills";
|
||||||
import { getPosts } from "../lib/blog";
|
import { getPosts, allTags } from "../lib/blog";
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
|
|
||||||
const featuredProjects = (await getCollection("projects", (p) => p.data.featured)).sort(
|
const featuredProjects = (await getCollection("projects", (p) => p.data.featured)).sort(
|
||||||
(a, b) => a.data.order - b.data.order,
|
(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);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout path="/">
|
<Layout path="/">
|
||||||
@@ -44,6 +46,14 @@ const recentPosts = (await getPosts()).slice(0, 3);
|
|||||||
Lessons from edge Kubernetes, GPUs, and running infrastructure like it matters —
|
Lessons from edge Kubernetes, GPUs, and running infrastructure like it matters —
|
||||||
written up as I go.
|
written up as I go.
|
||||||
</p>
|
</p>
|
||||||
|
{postTags.length > 0 && (
|
||||||
|
<nav class="writing__tags" aria-label="Browse the blog by tag" data-reveal>
|
||||||
|
<span class="mono writing__tags-label">tags:</span>
|
||||||
|
{postTags.map((t) => (
|
||||||
|
<a class="tag" href={`/blog/tags/${t}/`}>{t}</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
<PostList posts={recentPosts} />
|
<PostList posts={recentPosts} />
|
||||||
<p class="projects__more" data-reveal>
|
<p class="projects__more" data-reveal>
|
||||||
<a class="btn" href="/blog/">Read the blog →</a>
|
<a class="btn" href="/blog/">Read the blog →</a>
|
||||||
@@ -72,7 +82,18 @@ const recentPosts = (await getPosts()).slice(0, 3);
|
|||||||
}
|
}
|
||||||
.writing__intro {
|
.writing__intro {
|
||||||
max-width: 52ch;
|
max-width: 52ch;
|
||||||
margin-bottom: var(--space-5);
|
margin-bottom: var(--space-4);
|
||||||
color: var(--text-dim);
|
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);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+18
-5
@@ -72,9 +72,15 @@ function run(): void {
|
|||||||
* bottom (the progress=1 boundary); the callback maps 0→1 cleanly both ways. */
|
* bottom (the progress=1 boundary); the callback maps 0→1 cleanly both ways. */
|
||||||
const bar = document.querySelector<HTMLElement>(".scroll-progress");
|
const bar = document.querySelector<HTMLElement>(".scroll-progress");
|
||||||
if (bar) {
|
if (bar) {
|
||||||
scroll((progress: number) => {
|
// Compute progress straight from scroll position so it reliably spans 0→1
|
||||||
bar.style.transform = `scaleX(${progress})`;
|
// (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. */
|
/* 5. Nav — animated active-section underline + tactile hover. */
|
||||||
@@ -112,11 +118,18 @@ function initNav(): void {
|
|||||||
);
|
);
|
||||||
if (sectioned.length) {
|
if (sectioned.length) {
|
||||||
const setActive = (): void => {
|
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;
|
let current: HTMLAnchorElement | null = null;
|
||||||
for (const a of sectioned) {
|
for (const a of sectioned) {
|
||||||
const sec = document.getElementById(a.dataset.navId as string);
|
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 (current !== active) {
|
||||||
if (active) under(active, false);
|
if (active) under(active, false);
|
||||||
|
|||||||
Reference in New Issue
Block a user