feat: optimize performance and fix CLS issues

This commit is contained in:
Lorenzo Iovino 2026-01-09 16:40:37 +01:00
parent df04844ca2
commit 52f112568c
7 changed files with 88 additions and 17 deletions

View file

@ -1,7 +1,5 @@
--- ---
import { Image } from "astro:assets"; import { Image } from "astro:assets";
import astroIcon from "../assets/astro.svg";
import tailwindIcon from "../assets/tailwind.svg";
import githubIcon from "../assets/github.svg"; import githubIcon from "../assets/github.svg";
--- ---

View file

@ -10,24 +10,26 @@ import githubIcon from "../assets/github.svg";
<div class="mx-auto max-w-5xl"> <div class="mx-auto max-w-5xl">
<div <div
class="relative overflow-hidden rounded-3xl bg-white dark:bg-gray-800 shadow-soft-lg hover:shadow-soft-lg transition-all duration-300" class="relative overflow-hidden rounded-3xl bg-white dark:bg-gray-800 shadow-soft-lg hover:shadow-soft-lg transition-all duration-300"
style="min-height: 300px;"
> >
<div class="relative flex flex-col md:flex-row items-center md:items-end"> <div class="relative flex flex-col md:flex-row items-center md:items-end">
<!-- Photo Section --> <!-- Photo Section -->
<div class="w-full md:w-auto flex-shrink-0 order-2 md:order-none"> <div class="w-full md:w-auto flex-shrink-0 order-2 md:order-none">
<div class="relative overflow-hidden md:rounded-l-3xl flex justify-center md:block"> <div
class="relative overflow-hidden md:rounded-l-3xl flex justify-center md:block md:w-[300px]"
>
<Image <Image
src={mePhoto} src={mePhoto}
alt="Photo of Lorenzo Iovino" alt="Photo of Lorenzo Iovino"
class="w-48 h-auto md:w-full object-cover object-center" class="w-48 h-48 md:w-[300px] md:h-[300px] object-cover object-center"
width={300} width={300}
height={300} height={300}
quality={90} quality={90}
format="webp" format="webp"
loading="eager" loading="eager"
fetchpriority="high" fetchpriority="high"
decoding="sync"
/> />
<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> </div>
</div> </div>
@ -36,7 +38,7 @@ import githubIcon from "../assets/github.svg";
<div class="space-y-4 md:space-y-6"> <div class="space-y-4 md:space-y-6">
<!-- Greeting --> <!-- Greeting -->
<div class="text-center md:text-left"> <div class="text-center md:text-left">
<h1 class="mb-3 text-gray-900 dark:text-gray-100"> <h1 class="mb-3 text-gray-900 dark:text-gray-100" style="min-height: 2.5rem;">
Hey, I'm <span class="text-gray-900 dark:text-gray-100 font-extrabold" Hey, I'm <span class="text-gray-900 dark:text-gray-100 font-extrabold"
>Lorenzo</span >Lorenzo</span
>! >!

View file

@ -134,6 +134,7 @@ const currentPath = Astro.url.pathname;
<div <div
id="mobile-menu" id="mobile-menu"
class="hidden md:hidden bg-secondary dark:bg-gray-800 transition-colors duration-200" class="hidden md:hidden bg-secondary dark:bg-gray-800 transition-colors duration-200"
style="max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out;"
> >
<div class="px-4 py-3 space-y-3"> <div class="px-4 py-3 space-y-3">
<a <a
@ -176,7 +177,15 @@ const currentPath = Astro.url.pathname;
const mobileMenu = document.getElementById("mobile-menu"); const mobileMenu = document.getElementById("mobile-menu");
mobileMenuButton?.addEventListener("click", () => { mobileMenuButton?.addEventListener("click", () => {
mobileMenu?.classList.toggle("hidden"); if (mobileMenu?.classList.contains("hidden")) {
mobileMenu.classList.remove("hidden");
mobileMenu.style.maxHeight = "500px";
} else if (mobileMenu) {
mobileMenu.style.maxHeight = "0";
setTimeout(() => {
mobileMenu?.classList.add("hidden");
}, 300);
}
}); });
// Theme toggle functionality // Theme toggle functionality

View file

@ -170,13 +170,32 @@ 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
rel="preload"
as="font"
type="font/woff2"
href="https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2"
crossorigin
/>
<style> <style>
/* Fallback font ottimizzato per ridurre layout shift */
@font-face {
font-family: "Inter-fallback";
font-style: normal;
font-weight: 400 800;
src: local("Arial");
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
size-adjust: 107%;
}
@font-face { @font-face {
font-family: "Inter"; font-family: "Inter";
font-style: normal; font-style: normal;
font-weight: 400 800; font-weight: 400 800;
font-display: swap; font-display: optional;
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2) src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiA.woff2)
format("woff2"); format("woff2");
unicode-range: unicode-range:
@ -184,6 +203,17 @@ const fullCanonicalUrl = canonicalUrl || `${siteUrl}${Astro.url.pathname}`;
U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
U+FFFD; U+FFFD;
} }
/* Previeni flash of unstyled content */
body {
font-family:
"Inter",
"Inter-fallback",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
sans-serif;
}
</style> </style>
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />

View file

@ -108,7 +108,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<div id="posts-container" class="space-y-8"> <div id="posts-container" class="space-y-8">
{ {
initialPosts.map((post: BlogPost) => ( initialPosts.map((post: BlogPost, index: number) => (
<article class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-all duration-300"> <article class="bg-white dark:bg-gray-800 rounded-2xl shadow-soft-lg overflow-hidden hover:shadow-soft-lg transition-all duration-300">
<div class="block"> <div class="block">
{post.data.heroImage && ( {post.data.heroImage && (
@ -118,10 +118,12 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
src={post.data.heroImage} src={post.data.heroImage}
alt={post.data.title} alt={post.data.title}
class="w-full h-full object-cover hover:scale-105 transition-transform duration-300" class="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
width={1200} width={760}
height={630} height={570}
quality={70} quality={80}
format="webp" format="webp"
loading={index === 0 ? "eager" : "lazy"}
fetchpriority={index === 0 ? "high" : undefined}
/> />
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" /> <div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
</div> </div>
@ -159,7 +161,7 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
<p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4"> <p class="text-gray-700 dark:text-gray-300 text-lg leading-relaxed mb-4">
{post.data.description} {post.data.description}
</p> </p>
<div class="flex items-center text-secondary-800 dark:text-primary font-medium"> <div class="flex items-center text-secondary-700 dark:text-primary font-medium">
Read more Read more
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -220,6 +222,9 @@ const initialPosts: BlogPost[] = sortedPosts.slice(0, 6);
src="${post.data.heroImage}" src="${post.data.heroImage}"
alt="${post.data.title}" alt="${post.data.title}"
class="w-full h-full object-cover hover:scale-105 transition-transform duration-300" class="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
loading="lazy"
width="760"
height="570"
/> />
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div> <div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
</div> </div>

View file

@ -82,10 +82,12 @@ const heroImageSrc = entry.data.heroImage?.src || mePhoto.src;
src={entry.data.heroImage} src={entry.data.heroImage}
alt={entry.data.title} alt={entry.data.title}
class="w-full h-full object-cover" class="w-full h-full object-cover"
width={1200} width={824}
height={630} height={618}
quality={70} quality={80}
format="webp" format="webp"
loading="eager"
fetchpriority="high"
/> />
</div> </div>
</div> </div>

View file

@ -8,6 +8,15 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
font-family:
"Inter",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif;
} }
/* Typography base */ /* Typography base */
@ -17,6 +26,9 @@
"kern" 1, "kern" 1,
"liga" 1, "liga" 1,
"calt" 1; "calt" 1;
/* Previene layout shift durante il caricamento del font */
font-synthesis: none;
-webkit-text-size-adjust: 100%;
} }
/* Headings ottimizzati */ /* Headings ottimizzati */
@ -32,6 +44,8 @@
"liga" 1, "liga" 1,
"calt" 1, "calt" 1,
"ss01" 1; "ss01" 1;
/* Contenimento del layout per prevenire shift */
contain: layout style paint;
} }
h1 { h1 {
@ -85,4 +99,15 @@
.text-pretty { .text-pretty {
text-wrap: pretty; text-wrap: pretty;
} }
/* Previeni layout shift per elementi con contenuto dinamico */
.prevent-shift {
contain: layout style paint;
content-visibility: auto;
}
/* Ottimizza rendering immagini */
img {
contain: size layout paint;
}
} }