Images publiques responsive
Inventaire et contrat
Section intitulée « Inventaire et contrat »Le ticket Web #220 consomme la médiathèque existante, sans ajouter de pipeline côté API.
Le backend expose image_metadata sur les appartements (y compris la recherche), services,
actualités, lieux et contenu de l’accueil depuis Backend #126 / PR #127.
Chaque référence publique fournit width, height, mime_type, state et les variantes
{ url, width, height, mime_type }. Le profil média backend propose les largeurs
480, 768, 1280 et 1920 px, avec options WebP/AVIF et sans agrandissement du fichier source.
Le Web utilise les variantes effectivement publiées, et ne construit pas des noms de fichiers supposés.
mapPublicImages conserve seulement les champs nécessaires, convertit les fichiers en URL BFF
/api/media/files/ et supprime paramètres, fragments et hôtes backend des nouvelles métadonnées.
Les collections privées ne sont pas acceptées. Le relais existant sert les médias publics sans
token navigateur avec public, max-age=3600, stale-while-revalidate=86400. Les refus restent
private, no-store ; ce ticket ne modifie pas cette politique de sécurité.
ResponsiveImage.svelte reçoit ces informations par un contexte alimenté par les loads serveur.
Les éléments source déclarent le format, srcset et sizes ; le navigateur choisit la largeur
en tenant compte du viewport et de sa densité. Le hero et la couverture de la fiche appartement
sont eager avec fetchpriority="high".
Les autres photos sont lazy, avec dimensions intrinsèques lorsque le contrat les fournit.
Les classes de taille, ratio et recadrage existantes sont conservées. Les source sont masqués
pour ne pas occuper de cellules dans les grilles lorsque picture utilise display: contents.
Les tuiles de galerie déclarent sizes="auto, …" pour utiliser leur largeur rendue lorsque le
navigateur le permet, avec des replis distincts pour la grande tuile, les vignettes de 237,5 px
et la dernière tuile de 485 px sur desktop. L’image de lightbox conserve la taille du viewport.
Les images des menus utilisent les dimensions de leur carte : 266 px pour les appartements,
396 px au maximum pour les actualités, 440 px pour les services et 310 px pour les lieux.
Les tailles des cartes qui peuvent rétrécir tiennent compte du padding sur les petits écrans.
Une réponse ancienne, un média sans variantes ou un navigateur sans format compatible garde l’original. Les URL externes de repli conservent leurs paramètres existants. Les dimensions d’un ancien média inconnu ne sont pas inventées : ses conteneurs existants réservent leur taille. Les logos, drapeaux et images du back-office conservent leur rendu existant.
Fichiers statiques
Section intitulée « Fichiers statiques »Les illustrations FAQ/contact ne passent pas par la médiathèque. Le script
scripts/generate-public-image-variants.mjs réutilise Sharp, déjà verrouillé dans les dépendances
de la documentation. Il applique l’orientation, conserve le ratio et produit du WebP qualité 82,
sans agrandissement, aux largeurs 320, 640, 960, 1280 et au maximum 1600 px selon la source.
Les originaux restent disponibles comme repli.
| Fichier | Original (octets) | Variante de comparaison | Octets |
|---|---|---|---|
| FAQ | 1 182 228 | 1254 px WebP | 25 532 |
| Contact | 1 032 876 | 1122 px WebP | 187 884 |
| Photo contact/modal | 1 414 827 | 1600 px WebP | 1 001 042 |
Ces comparaisons décrivent les fichiers ; le navigateur télécharge souvent une largeur inférieure. La FAQ déclare une taille de 190 px ; le contact utilise 461 px sur desktop et une colonne sur mobile.
npm cinpm ci --prefix documentationnode scripts/generate-public-image-variants.mjsnpx prettier --write src/lib/presentation/shared/media/StaticImageMetadata.tsMesure reproductible
Section intitulée « Mesure reproductible »Référence : Web e3e075a ; backend utilisé pour les deux révisions : a126fc8.
Mesure locale du 13 octobre 2026 sous Chromium Playwright, serveur Node de production,
sans bridage CPU/réseau. Deux contextes neufs : mobile 375 × 812, DPR 2 ; desktop
1365 × 900, DPR 1. Le test parcourt toute la page à pas de 500 px et additionne les corps
des réponses image par URL. Il mesure donc les octets d’images uniques, pas le poids réseau
total avec headers, HTML, scripts et polices.
Le jeu de données est contrôlé : une base seedée isolée, et un proxy local qui remplace les photos
publiques par contactUs.jpg et ses variantes WebP, sous des URL distinctes par ressource.
Les PNG statiques restent ceux du site. Les deux révisions reçoivent exactement le même contrat
et les mêmes originaux. Ce jeu vérifie la consommation des variantes et permet une comparaison
stable ; il ne reproduit pas les 7,12 Mo de l’échantillon de l’audit ni la médiathèque déployée.
Le test e2e/public-responsive-images.test.ts est activé uniquement avec
E2E_RESPONSIVE_IMAGES=1. Le script ci-dessous fournit la configuration et cette activation.
Il attend un backend seedé isolé sur 18091 (ou E2E_BACKEND_URL) et réserve les ports
18092 pour son proxy et 14173 pour le Web. Ne pas utiliser la base de développement personnelle.
# Construire cette branche ainsi qu’un checkout de e3e075a avec Node 22.npm run build# Démarrer séparément le backend :# harmony serve --dir=/tmp/harmony-220-e2e-db --http=127.0.0.1:18091# Depuis cette branche :node scripts/measure-public-images.mjs /chemin/vers/checkout-referencenode scripts/measure-public-images.mjsLes JSON et captures sont écrits dans /tmp/harmony-220-{before,after}-{mobile,desktop}
avec les extensions .json et .png, et joints aux résultats Playwright. Les tests vérifient
la variante du hero, sa priorité, les dimensions, le lazy loading, le cache public anonyme,
le placement des petites photos et un budget local inférieur à 5 Mo après optimisation.
| Profil | Octets avant | Octets après | Réduction | LCP avant → après (ms) | CLS avant → après |
|---|---|---|---|---|---|
| Mobile | 15 042 468 | 3 871 985 | 74,3 % | 600 → 616 | 0,002176 → 0,002176 |
| Desktop | 15 042 468 | 3 624 451 | 75,9 % | 328 → 332 | 0,003952 → 0,003504 |
Le hero sélectionne 960 px sur mobile DPR 2 et 1600 px sur desktop DPR 1. Les captures avant/après ont été inspectées ; le placement desktop conserve la même hauteur de page (7586 px), et la grille des deux petites photos reste sur une ligne. Aucun changement visible du cadrage ni défaut notable de compression n’a été repéré sur ces captures. Les données détaillées sont disponibles dans le rapport JSON.
Le gain établi concerne les octets d’images. Le LCP observé reste proche de la référence :
ces résultats ne démontrent pas une amélioration de ce délai.
LCP et CLS proviennent de PerformanceObserver dans ce laboratoire. Une seule navigation par
profil ne permet pas de conclure sur leurs percentiles ; aucun INP ni score terrain n’est revendiqué.
Après déploiement
Section intitulée « Après déploiement »Rejouer les deux profils en session anonyme sur le domaine de test autorisé, avec son jeu de
médias réel et cache froid. Vérifier les currentSrc, dimensions, headers du relais, rendu de
l’accueil et ouverture des galeries/modales. Pour une comparaison de performances, répéter les
mesures avec le même bridage et garder les médianes. Mesurer les Core Web Vitals terrain avec
des données RUM ou CrUX suffisantes lorsque le site sera public ; Cloudflare limite encore
volontairement l’accès. Ce ticket ne change ni l’accès Cloudflare ni le déploiement.