Skills/web-vitals-performance/SKILL.md

125 lines
4.7 KiB
Markdown

---
name: web-vitals-performance
description: >-
Optimize real-world web performance according to Google Core Web Vitals (INP <= 200ms, LCP <= 2.5s, CLS <= 0.1).
Enforces main thread budgets, scheduler.yield() task chunking, AVIF media pipelines,
font metric overrides, and third-party script isolation.
---
# Web Vitals & Real-World Performance Engineering
Ce skill garantit que les interfaces atteignent les seuils d'excellence des **Core Web Vitals** officiels (LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1) mesurés sur le terrain (Google Chrome User Experience Report - CrUX).
---
## 🎯 Déclencheurs d'Activation
Activez ce skill dès que :
1. Vous chargez des médias lourds (images d'en-tête, bannières, galeries, vidéos).
2. Vous intégrez des polices typographiques web (Google Fonts, polices locales).
3. Vous développez des interactions utilisateur riches susceptibles de bloquer le thread principal (recherche instantanée, filtrage de tableau de données, calculs lourds côté client).
4. Vous auditez ou optimisez les scores de performance d'une application avant son déploiement.
---
## ⚡ 1. Optimisation de l'INP (Interaction to Next Paint ≤ 200ms)
L'INP a définitivement remplacé le FID. Il mesure le délai entre une action utilisateur (clic, frappe, tap) et le moment exact où le navigateur affiche la prochaine image.
### Règle d'Or : Budget du Thread Principal < 50ms
Toute tâche JS dépassant 50ms est une « Long Task » qui dégrade l'INP.
### Découpage des Tâches Longues avec `scheduler.yield()`
```typescript
// Utilitaire moderne pour rendre la main au navigateur pendant un traitement lourd
async function yieldToMain() {
if ('scheduler' in window && 'yield' in (window as any).scheduler) {
await (window as any).scheduler.yield();
} else {
// Fallback standard
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
export async function processLargeDataset(items: Array<any>) {
for (let i = 0; i < items.length; i++) {
// Traitement de l'élément
computeHeavyItem(items[i]);
// Toutes les 50 itérations, on cède le contrôle au thread principal pour afficher les clics/peintures
if (i % 50 === 0) {
await yieldToMain();
}
}
}
```
---
## 🚀 2. Optimisation du LCP (Largest Contentful Paint ≤ 2.5s)
L'élément LCP (souvent l'image du Hero ou le titre H1 principal) doit être peint immédiatement :
1. **`fetchpriority="high"` Obligatoire** sur l'image LCP :
```html
<img
src="/images/hero.avif"
alt="Tableau de bord de design"
fetchpriority="high"
loading="eager"
decoding="async"
width="1280"
height="720"
/>
```
2. **Préchargement des Polices Critiques** :
```html
<link
rel="preload"
href="/fonts/geist-sans.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
```
3. **Format AVIF Prioritaire** : Toujours servir l'AVIF en premier (gain de 20 à 35% de bande passante par rapport au WebP).
---
## 📐 3. Élimination Totale du CLS (Cumulative Layout Shift ≤ 0.1)
Le décalage de mise en page survient lorsque des éléments changent de position pendant le chargement.
### Solutions Anti-CLS :
1. **Ratio d'aspect explicite** : Tout conteneur d'image ou vidéo doit avoir une propriété CSS `aspect-ratio` ou des attributs `width` et `height`.
2. **Espace réservé pour la typographie** : Utilisez `@font-face` avec des métriques de substitution pour éviter le saut entre police système et police web :
```css
@font-face {
font-family: 'Geist-Fallback';
src: local('Arial');
ascent-override: 92%;
descent-override: 24%;
size-adjust: 102%;
}
```
3. **Squelettes de chargement (Skeletons)** : Réservez précisément la hauteur des blocs de contenu asynchrones.
---
## 📦 4. Gestion des Scripts Tiers (Analytics & Trackers)
Ne jamais insérer de balise `<script src="...">` synchrone dans le `<head>` pour un outil d'analyse :
- Utilisez `strategy="lazyOnload"` ou `strategy="worker"` (via Partytown pour déporter l'exécution dans un Web Worker).
- Isolez les iframes et widgets d'assistance avec l'attribut `loading="lazy"`.
---
## 📋 Checklist de Conformité Web Vitals
- [ ] L'image principale LCP possède `fetchpriority="high"` et `loading="eager"`.
- [ ] Aucun média ne manque de dimensions ou d'attribut CSS `aspect-ratio`.
- [ ] Les polices web utilisent `font-display: swap` et sont préchargées en WOFF2 local.
- [ ] Les écouteurs d'événements lourds (`scroll`, `input`, `resize`) sont découpés via `scheduler.yield()` ou temporisés (*debounced*).
- [ ] Le score CLS en navigation simulée est inférieur à 0.05.
- [ ] Les images sont servies au format AVIF avec fallback WebP.