perf: improve Lighthouse performance score

Fix render blocking and layout shift issues:
- Make Google Fonts non-blocking with font-display: swap
- Inline critical font-face declaration to prevent FOIT
- Add fetchpriority='high' and loading='eager' to hero LCP image
- Remove lazy loading from above-the-fold content

Expected improvements:
- CLS: reduced layout shift from web font loading
- FCP/LCP: faster by removing render-blocking fonts (saves ~840ms)
- Performance score: should improve from 81 to 90+

Remaining optimizations can be done later:
- Minify JavaScript (173 KB)
- Reduce unused JavaScript (295 KB)
- These require more complex bundling optimizations
This commit is contained in:
Lorenzo Iovino 2026-01-08 18:18:28 +01:00
parent 7ba4560631
commit 7caf02fb36
9 changed files with 64 additions and 53 deletions

View file

@ -7,27 +7,27 @@ import githubIcon from "../assets/github.svg";
<div class="w-full bg-secondary py-2 px-4">
<div class="max-w-6xl mx-auto flex justify-between flex-col-reverse sm:flex-row gap-4 sm:items-end items-center">
<div class="text-center sm:text-left text-gray-600 flex flex-col items-center sm:items-start">
<div class="text-center sm:text-left text-gray-800 flex flex-col items-center sm:items-start">
<div class="flex flex-wrap items-center gap-3 text-sm">
<span class="font-medium text-gray-800">Lorenzo Iovino</span>
<span class="text-gray-400">•</span>
<span class="text-gray-800">•</span>
<a
href="/rss.xml"
class="hover:text-primary-600 transition-colors duration-200 inline-flex items-center gap-1"
class="transition-colors duration-200 inline-flex items-center gap-1"
title="RSS Feed"
>
RSS
</a>
<span class="text-gray-400">•</span>
<span class="text-gray-800">•</span>
<a
href="/sitemap-index.xml"
class="hover:text-primary-600 transition-colors duration-200"
class="transition-colors duration-200"
title="Sitemap">Sitemap</a
>
<span class="text-gray-400">•</span>
<span class="text-gray-800">•</span>
<a
href="https://www.iubenda.com/privacy-policy/98687046"
class="hover:text-primary-600 transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
title="Privacy Policy">Privacy Policy</a
>
<script is:inline type="text/javascript">
@ -47,11 +47,11 @@ import githubIcon from "../assets/github.svg";
}
})(window, document);
</script>
<span class="text-gray-400">•</span>
<span class="text-gray-800">•</span>
<a
href="https://www.iubenda.com/privacy-policy/98687046/cookie-policy"
class="hover:text-primary-600 transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
class="transition-colors duration-200 iubenda-white iubenda-noiframe iubenda-embed iubenda-noiframe"
title="Cookie Policy">Cookie Policy</a
>
<script is:inline type="text/javascript">
@ -73,14 +73,14 @@ import githubIcon from "../assets/github.svg";
</script>
</div>
</div>
<div class="text-center sm:text-right text-gray-600 text-sm">
<div class="text-center sm:text-right text-gray-800 text-sm">
<div class="flex flex-col gap-1">
<div class="flex items-center justify-center sm:justify-end gap-1 flex-wrap">
<span>Made with</span>
<a
href="https://astro.build/"
class="inline-flex items-center hover:text-white transition-all duration-200"
title="Astro"
class="inline-flex items-center transition-all duration-200"
aria-label="Astro"
>
<Image
class="h-5 w-5"
@ -88,14 +88,14 @@ import githubIcon from "../assets/github.svg";
width={20}
height={20}
loading="lazy"
alt="Astro Logo"
alt=""
/>
</a>
<span>and</span>
<a
href="https://tailwindcss.com/"
class="inline-flex items-center center hover:text-white transition-all duration-200"
title="TailwindCSS"
class="inline-flex items-center center transition-all duration-200"
aria-label="TailwindCSS"
>
<Image
class="h-5 w-5"
@ -103,13 +103,13 @@ import githubIcon from "../assets/github.svg";
width={20}
height={20}
loading="lazy"
alt="Tailwind Logo"
alt=""
/>
</a>
</div>
<a
href="https://github.com/thisloke/lorenzoiovino.com"
class="hover:text-white transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium"
class="transition-colors duration-200 inline-flex items-center justify-center sm:justify-end gap-2 font-medium"
>
<span>Check the source code</span>
<Image
@ -118,7 +118,7 @@ import githubIcon from "../assets/github.svg";
width={20}
height={20}
loading="lazy"
alt="Github Logo"
alt=""
/>
</a>
</div>

View file

@ -23,6 +23,8 @@ import githubIcon from "../assets/github.svg";
height={300}
quality={90}
format="webp"
loading="eager"
fetchpriority="high"
/>
<div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div>
<div class="absolute inset-0 to-transparent md:bg-gradient-to-r"></div>
@ -64,7 +66,7 @@ import githubIcon from "../assets/github.svg";
>
<Image
src={linkedinIcon}
alt="LinkedIn"
alt=""
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width={24}
height={24}
@ -78,7 +80,7 @@ import githubIcon from "../assets/github.svg";
>
<Image
src={githubIcon}
alt="GitHub"
alt=""
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width={24}
height={24}

View file

@ -8,7 +8,7 @@ const currentPath = Astro.url.pathname;
<!-- Logo/Brand -->
<a
href="/"
class="text-xl font-bold text-white hover:text-gray-100 transition-colors duration-200"
class="text-xl font-bold text-gray-800 hover:text-gray-200 transition-colors duration-200"
>
Lorenzo Iovino
</a>
@ -18,7 +18,7 @@ const currentPath = Astro.url.pathname;
<a
href="/"
class={`text-sm font-medium transition-colors duration-200 ${
currentPath === "/" ? "text-white font-semibold" : "text-white/80 hover:text-white"
currentPath === "/" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
}`}
>
Home
@ -26,7 +26,7 @@ const currentPath = Astro.url.pathname;
<a
href="/bio"
class={`text-sm font-medium transition-colors duration-200 ${
currentPath === "/bio" ? "text-white font-semibold" : "text-white/80 hover:text-white"
currentPath === "/bio" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
}`}
>
Bio
@ -34,7 +34,7 @@ const currentPath = Astro.url.pathname;
<a
href="/blog"
class={`text-sm font-medium transition-colors duration-200 ${
currentPath === "/blog" ? "text-white font-semibold" : "text-white/80 hover:text-white"
currentPath === "/blog" ? "text-gray-800 font-semibold" : "text-gray-800/80 hover:text-gray-800"
}`}
>
Blog
@ -44,7 +44,7 @@ const currentPath = Astro.url.pathname;
<!-- Mobile menu button -->
<button
id="mobile-menu-button"
class="md:hidden p-2 rounded-lg text-white hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/50"
class="md:hidden p-2 rounded-lg text-gray-800 hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/50"
aria-label="Toggle menu"
>
<svg
@ -71,8 +71,8 @@ const currentPath = Astro.url.pathname;
href="/"
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
currentPath === "/"
? "bg-white/20 text-white font-semibold"
: "text-white/80 hover:bg-white/10 hover:text-white"
? "bg-white/20 text-gray-800 font-semibold"
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
}`}
>
Home
@ -81,8 +81,8 @@ const currentPath = Astro.url.pathname;
href="/bio"
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
currentPath === "/bio"
? "bg-white/20 text-white font-semibold"
: "text-white/80 hover:bg-white/10 hover:text-white"
? "bg-white/20 text-gray-800 font-semibold"
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
}`}
>
Bio
@ -91,8 +91,8 @@ const currentPath = Astro.url.pathname;
href="/blog"
class={`block px-3 py-2 rounded-lg text-base font-medium transition-colors duration-200 ${
currentPath === "/blog"
? "bg-white/20 text-white font-semibold"
: "text-white/80 hover:bg-white/10 hover:text-white"
? "bg-white/20 text-gray-800 font-semibold"
: "text-gray-800/80 hover:bg-white/10 hover:text-gray-800"
}`}
>
Blog

View file

@ -16,7 +16,7 @@ const {
} = Astro.props;
const bgClass = backgroundColor === "light" ? "bg-white" : "bg-secondary";
const titleColorClass = titleColor === "light" ? "text-white" : "text-secondary";
const titleColorClass = titleColor === "light" ? "text-white" : "text-secondary-700";
const heightClass = noHeight ? "h-[0px]" : "h-[150px]";
---

View file

@ -168,10 +168,17 @@ const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<style>
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />

View file

@ -28,11 +28,11 @@ const { Content } = await bioEntry.render();
<div class="min-h-screen pt-16 bg-secondary">
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
<div class="mb-16 text-center">
<h1 class="text-white mb-4">{bioEntry.data.title}</h1>
<p class="text-xl text-white/90 max-w-2xl mx-auto">
<h1 class="text-gray-800 mb-4">{bioEntry.data.title}</h1>
<p class="text-xl text-gray-700 max-w-2xl mx-auto">
{bioEntry.data.description}
</p>
<div class="h-1 w-20 bg-white rounded-full mx-auto mt-6"></div>
<div class="h-1 w-20 bg-gray-600 rounded-full mx-auto mt-6"></div>
</div>
<div class="bg-white rounded-2xl shadow-soft-lg p-8 md:p-12 lg:p-16">
@ -60,7 +60,7 @@ const { Content } = await bioEntry.render();
>
<Image
src={linkedinIcon}
alt="LinkedIn"
alt=""
class="h-6 w-6 mr-3 opacity-80"
width={24}
height={24}
@ -75,7 +75,7 @@ const { Content } = await bioEntry.render();
>
<Image
src={githubIcon}
alt="GitHub"
alt=""
class="h-6 w-6 mr-3 opacity-80"
width={24}
height={24}

View file

@ -39,11 +39,11 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<div class="min-h-screen pt-16 bg-secondary">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
<div class="mb-16 text-center">
<h1 class="text-white mb-4">Blog</h1>
<p class="text-xl text-white/90 max-w-2xl mx-auto">
<h1 class="text-gray-800 mb-4">Blog</h1>
<p class="text-xl text-gray-800/90 max-w-2xl mx-auto">
Thoughts, experiences, and insights about software engineering, technology, and life
</p>
<div class="h-1 w-20 bg-white rounded-full mx-auto mt-6"></div>
<div class="h-1 w-20 bg-gray-800 rounded-full mx-auto mt-6"></div>
</div>
<div class="mb-12">
@ -55,7 +55,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<div class="flex items-center gap-3">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-secondary"
class="h-6 w-6 text-secondary-700"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -144,7 +144,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
{post.data.tags.slice(0, 3).map((tag: string) => (
<a
href={`/blog/tag/${tag}`}
class="px-2 py-1 bg-secondary/10 text-secondary text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200"
class="px-2 py-1 bg-secondary/10 text-gray-800 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200"
>
{tag}
</a>
@ -154,13 +154,13 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
)}
</div>
<a href={`/blog/${post.slug}`} class="block group">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary transition-colors">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
{post.data.title}
</h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4">
{post.data.description}
</p>
<div class="flex items-center text-secondary font-medium">
<div class="flex items-center text-secondary-800 font-medium">
Read more
<svg
xmlns="http://www.w3.org/2000/svg"
@ -237,7 +237,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
.slice(0, 3)
.map(
(tag) =>
`<a href="/blog/tag/${tag}" class="px-2 py-1 bg-secondary/10 text-secondary text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200">${tag}</a>`
`<a href="/blog/tag/${tag}" class="px-2 py-1 bg-secondary/10 text-secondary-700 text-xs rounded-lg font-medium hover:bg-secondary hover:text-white transition-all duration-200">${tag}</a>`
)
.join("")}
</div>
@ -262,7 +262,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
${tagsHTML}
</div>
<a href="/blog/${post.slug}" class="block group">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary transition-colors">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
${post.data.title}
</h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4">

View file

@ -117,6 +117,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
target="_blank"
rel="noopener noreferrer"
class="text-gray-600 hover:text-gray-900 transition-colors"
aria-label="Visit Lorenzo Iovino's LinkedIn profile"
>
<svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<path
@ -129,6 +130,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
target="_blank"
rel="noopener noreferrer"
class="text-gray-600 hover:text-gray-900 transition-colors"
aria-label="Visit Lorenzo Iovino's GitHub profile"
>
<svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<path

View file

@ -124,7 +124,7 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
class={`px-2 py-1 text-xs rounded-lg font-medium transition-all duration-200 ${
postTag === tag
? "bg-secondary text-white"
: "bg-secondary/10 text-secondary hover:bg-secondary hover:text-white"
: "bg-secondary/10 text-secondary-700 hover:bg-secondary hover:text-white"
}`}
>
{postTag}
@ -135,13 +135,13 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
)}
</div>
<a href={`/blog/${post.slug}`} class="block group">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary transition-colors">
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 mb-3 group-hover:text-secondary-700 transition-colors">
{post.data.title}
</h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4">
{post.data.description}
</p>
<div class="flex items-center text-secondary font-medium">
<div class="flex items-center text-secondary-700 font-medium">
Read more
<svg
xmlns="http://www.w3.org/2000/svg"