From a0f84e12aef5c3f3451e6a42e871fece4cf19d48 Mon Sep 17 00:00:00 2001 From: Terencio Agozzino Date: Sat, 7 Nov 2026 06:50:20 +0100 Subject: [PATCH 1/2] fix: land menu links right under the navigation bar The page stopped 80px above each section while the bar is 73px tall on desktop and 61px on phones, so a strip of the previous section showed above the one asked for. The offset now matches the bar. Co-Authored-By: Claude Opus 5.5 --- src/styles/global.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/styles/global.css b/src/styles/global.css index 458084e..7f5d86d 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -105,7 +105,9 @@ html { scroll-behavior: smooth; - scroll-padding-top: 80px; + /* The navigation's height, its 1px border included (see Nav.astro), so a + link lands with its section starting right under the bar. */ + scroll-padding-top: 73px; /* What a browser shows past the page's edges: behind Safari's toolbar on iPhone, and when a scroll bounces. The theme's background at the top, the footer's green once the footer is on screen (set in Base.astro), so the @@ -276,6 +278,10 @@ h2 { } @media (max-width: 920px) { + html { + scroll-padding-top: 61px; + } + section { padding: 80px 0; } From 029be3dc3976ccc33b1e351263c885c36f770636 Mon Sep 17 00:00:00 2001 From: Terencio Agozzino Date: Sat, 7 Nov 2026 06:56:17 +0100 Subject: [PATCH 2/2] feat(experience): fold the earliest roles under a button SoccerLAB, Datavillage and the IDLab internship (2021-2022) fold under "Show earlier experience", so the timeline reads from current work and is a fifth shorter to scroll. They stay in the page, for search engines and without JavaScript; unfolding counts as a GoatCounter event. Co-Authored-By: Claude Opus 5.5 --- src/components/Experience.astro | 88 +++++++++++++++++++++++++++++++-- src/data/profile.ts | 7 +++ tests/site.spec.ts | 22 +++++++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/src/components/Experience.astro b/src/components/Experience.astro index ea81f57..85dd798 100644 --- a/src/components/Experience.astro +++ b/src/components/Experience.astro @@ -7,6 +7,15 @@ function split({ summary, link }: Mission): (string | NonNullable + missions.filter((mission) => earlier || mission.earlier), +); +const years = earlier.flatMap(({ period }) => period.match(/\d{4}/g) ?? []).sort(); +const span = years.length ? `${years[0]}–${years.at(-1)}` : ''; ---
@@ -16,9 +25,9 @@ function split({ summary, link }: Mission): (string | NonNullableWhere I've made an impact.

By employer, with the clients I worked for as a consultant.

-
    - {experience.map(({ name, period, summary, current, missions }) => ( -
  1. +
      + {experience.map(({ name, period, summary, current, earlier, missions }) => ( +
    1. {name} @@ -29,7 +38,7 @@ function split({ summary, link }: Mission): (string | NonNullable
        {missions.map((mission) => ( -
      1. +
      2. {mission.client ?? mission.role}

        {mission.client &&

        {mission.role}

        }

        @@ -57,14 +66,81 @@ function split({ summary, link }: Mission): (string | NonNullable ))}

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

+ + diff --git a/src/data/profile.ts b/src/data/profile.ts index 1ea0d80..5383792 100644 --- a/src/data/profile.ts +++ b/src/data/profile.ts @@ -137,6 +137,8 @@ export interface Mission { /** A phrase of the summary that downloads a document, such as a thesis. */ link?: { text: string; url: string }; stack: string[]; + /** Folded under "Show earlier experience" until asked for. */ + earlier?: boolean; } /** An employer, and the missions done while employed there, newest first. */ @@ -146,6 +148,8 @@ export interface Employer { /** What the employment was, when it is more than its missions. */ summary?: string; current?: boolean; + /** Folded, with all its missions, under "Show earlier experience". */ + earlier?: boolean; missions: Mission[]; } @@ -233,6 +237,7 @@ export const experience: Employer[] = [ { role: 'Software Engineer', client: 'SoccerLAB', + earlier: true, period: 'Aug – Sep 2022', place: 'Hasselt, Belgium', summary: 'A Svelte proof of concept against React and Angular, with a design system.', @@ -241,6 +246,7 @@ export const experience: Employer[] = [ { role: 'Data Scientist', client: 'Datavillage', + earlier: true, period: 'Jan – Apr 2022', place: 'Liège, Belgium', summary: @@ -251,6 +257,7 @@ export const experience: Employer[] = [ }, { name: 'IDLab – imec', + earlier: true, period: 'Feb – Jun 2021', missions: [ { diff --git a/tests/site.spec.ts b/tests/site.spec.ts index 8686d0b..eb113e4 100644 --- a/tests/site.spec.ts +++ b/tests/site.spec.ts @@ -277,6 +277,26 @@ test("paints past the page's edges in the theme's colour, then the footer's", as await expect(edge).toBeHidden(); }); +test('folds the earliest roles under a button, and unfolds them', async ({ page }) => { + await page.goto('/'); + const earlier = page.locator('#experience .earlier'); + // Found by place, as its name changes from "Show" to "Hide". + const more = page.locator('#experience .more'); + await expect(more).toHaveAttribute('aria-expanded', 'false'); + await expect(more).toContainText('3 roles · 2021–2022'); + for (const role of await earlier.all()) await expect(role).toBeHidden(); + await expect(page.getByRole('heading', { name: 'Partenamut' })).toBeVisible(); + + await more.click(); + await expect(more).toHaveAttribute('aria-expanded', 'true'); + await expect(more).toContainText('Hide earlier experience'); + await expect(page.getByRole('heading', { name: 'SoccerLAB' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'IDLab – imec' })).toBeVisible(); + + await more.click(); + for (const role of await earlier.all()) await expect(role).toBeHidden(); +}); + test('copies the email address in one click, and says so', async ({ page }) => { // A stand-in clipboard, the same in every browser, that records what it is given. await page.addInitScript(() => { @@ -312,6 +332,7 @@ test('counts the visit, and the CV, the thesis, an email and a link out', async await page.evaluate(() => document.addEventListener('click', (event) => event.preventDefault())); await page.locator('#top a[download]').click(); + await page.getByRole('button', { name: /Show earlier experience/ }).click(); await page.locator(`#experience a[href$="master-thesis.pdf"]`).click(); await page.locator('#contact a[href^="mailto:"]').click(); await page.locator('#contact a[href*="linkedin.com"]').click(); @@ -320,6 +341,7 @@ test('counts the visit, and the CV, the thesis, an email and a link out', async expect(await page.evaluate(() => (window as unknown as { counted: string[] }).counted)).toEqual([ '/', 'download terencio-agozzino-cv.pdf', + 'show earlier experience', 'download terencio-agozzino-master-thesis.pdf', 'email', 'out www.linkedin.com/in/terencioagozzino',