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

View file

@ -23,6 +23,8 @@ import githubIcon from "../assets/github.svg";
height={300} height={300}
quality={90} quality={90}
format="webp" 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>
<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 <Image
src={linkedinIcon} src={linkedinIcon}
alt="LinkedIn" alt=""
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity" class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width={24} width={24}
height={24} height={24}
@ -78,7 +80,7 @@ import githubIcon from "../assets/github.svg";
> >
<Image <Image
src={githubIcon} src={githubIcon}
alt="GitHub" alt=""
class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity" class="h-6 w-6 opacity-70 group-hover:opacity-100 transition-opacity"
width={24} width={24}
height={24} height={24}

View file

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

View file

@ -16,7 +16,7 @@ const {
} = Astro.props; } = Astro.props;
const bgClass = backgroundColor === "light" ? "bg-white" : "bg-secondary"; 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]"; 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.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <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" <style>
rel="stylesheet" @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="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" /> <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="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="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
<div class="mb-16 text-center"> <div class="mb-16 text-center">
<h1 class="text-white mb-4">{bioEntry.data.title}</h1> <h1 class="text-gray-800 mb-4">{bioEntry.data.title}</h1>
<p class="text-xl text-white/90 max-w-2xl mx-auto"> <p class="text-xl text-gray-700 max-w-2xl mx-auto">
{bioEntry.data.description} {bioEntry.data.description}
</p> </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>
<div class="bg-white rounded-2xl shadow-soft-lg p-8 md:p-12 lg:p-16"> <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 <Image
src={linkedinIcon} src={linkedinIcon}
alt="LinkedIn" alt=""
class="h-6 w-6 mr-3 opacity-80" class="h-6 w-6 mr-3 opacity-80"
width={24} width={24}
height={24} height={24}
@ -75,7 +75,7 @@ const { Content } = await bioEntry.render();
> >
<Image <Image
src={githubIcon} src={githubIcon}
alt="GitHub" alt=""
class="h-6 w-6 mr-3 opacity-80" class="h-6 w-6 mr-3 opacity-80"
width={24} width={24}
height={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="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="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-20">
<div class="mb-16 text-center"> <div class="mb-16 text-center">
<h1 class="text-white mb-4">Blog</h1> <h1 class="text-gray-800 mb-4">Blog</h1>
<p class="text-xl text-white/90 max-w-2xl mx-auto"> <p class="text-xl text-gray-800/90 max-w-2xl mx-auto">
Thoughts, experiences, and insights about software engineering, technology, and life Thoughts, experiences, and insights about software engineering, technology, and life
</p> </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>
<div class="mb-12"> <div class="mb-12">
@ -55,7 +55,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<svg <svg
xmlns="http://www.w3.org/2000/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" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
@ -144,7 +144,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
{post.data.tags.slice(0, 3).map((tag: string) => ( {post.data.tags.slice(0, 3).map((tag: string) => (
<a <a
href={`/blog/tag/${tag}`} 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} {tag}
</a> </a>
@ -154,13 +154,13 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
)} )}
</div> </div>
<a href={`/blog/${post.slug}`} class="block group"> <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} {post.data.title}
</h2> </h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4"> <p class="text-gray-700 text-lg leading-relaxed mb-4">
{post.data.description} {post.data.description}
</p> </p>
<div class="flex items-center text-secondary font-medium"> <div class="flex items-center text-secondary-800 font-medium">
Read more Read more
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -237,7 +237,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
.slice(0, 3) .slice(0, 3)
.map( .map(
(tag) => (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("")} .join("")}
</div> </div>
@ -262,7 +262,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
${tagsHTML} ${tagsHTML}
</div> </div>
<a href="/blog/${post.slug}" class="block group"> <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} ${post.data.title}
</h2> </h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4"> <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" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-gray-600 hover:text-gray-900 transition-colors" 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"> <svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<path <path
@ -129,6 +130,7 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-gray-600 hover:text-gray-900 transition-colors" 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"> <svg class="w-5 h-5 md:w-6 md:h-6" fill="currentColor" viewBox="0 0 24 24">
<path <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 ${ class={`px-2 py-1 text-xs rounded-lg font-medium transition-all duration-200 ${
postTag === tag postTag === tag
? "bg-secondary text-white" ? "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} {postTag}
@ -135,13 +135,13 @@ const displayTag: string = tag.charAt(0).toUpperCase() + tag.slice(1);
)} )}
</div> </div>
<a href={`/blog/${post.slug}`} class="block group"> <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} {post.data.title}
</h2> </h2>
<p class="text-gray-700 text-lg leading-relaxed mb-4"> <p class="text-gray-700 text-lg leading-relaxed mb-4">
{post.data.description} {post.data.description}
</p> </p>
<div class="flex items-center text-secondary font-medium"> <div class="flex items-center text-secondary-700 font-medium">
Read more Read more
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"