# Rapport : corrections scroll colonnes gauche/droite et scale-down du titre ## Contexte Sur les pages full des projets et evenements, deux animations GSAP coexistent : 1. **Scale-down du titre** : le header (titre) retrecit de `scale 1.0 -> 0.4` pendant le scroll, avec une translation `y` qui compense pour garder le bord visuel fixe. 2. **Gel de la colonne gauche** : la colonne gauche defile naturellement puis se fige (contre-translation 1:1 du scroll) pendant que la colonne droite (images en `position: sticky`) continue. Les images de la colonne droite sont en `position: sticky; top: 0` et s'empilent les unes sur les autres. ## Problemes identifies ### 1. node/573 — image remonte en fin de scroll (FIXE) **Cause** : le `:after` de `.field_images` avait `height: 100vh` statique. Quand la colonne gauche est tres haute (texte long), le contexte sticky s'epuisait avant la fin du scroll, et l'image remontait. **Fix** : - CSS : `height: var(--sticky-extent, 100vh)` sur le `:after` (fallback 100vh si pas de JS). - JS : `adjustStickyContext()` calcule `scrollRange - imgHeight + clientHeight` et pose la valeur en CSS custom property `--sticky-extent` sur `.field_images`. Recalcule au load de chaque image. **Status** : fonctionne. ### 2. node/576 — ecart entre le titre et le contenu (FIXE puis REGRESSIF) **Cause** : `initHeaderH` etait mesure une fois, de maniere synchrone, avant les `requestAnimationFrame`. Si la police Epilogue ou Splitting changeait la hauteur du header apres cette mesure, `end` et `y` etaient faux, creant un ecart visuel entre le bas du header et le contenu. **Fix applique** : - `y` et `end` transformes en fonctions qui mesurent `pageHeader.offsetHeight` au moment ou GSAP en a besoin. - `initHeaderH` supprime, remplace par `pageHeader.offsetHeight` partout (y compris dans `leftFreezeStart`). **Status** : l'ecart est corrige, mais le fix a introduit un nouveau probleme (voir point 4). ### 3. node/549 et 570 — colonne gauche redescend en fin de scroll (REGRESSIF puis REFIXE) **Cause** : `leftFreezeStart` utilisait `Math.max(overflow, titleEnd)`. Sur 549/570, `titleEnd` (proportionnel a la hauteur du titre a 12em) peut depasser `overflow` (hauteur naturelle de la colonne - viewport), ce qui decale le freeze trop loin et la colonne redescend. **Fix applique** : retour a la logique d'origine pour `leftFreezeStart` : ```js const overflow = leftColumn.offsetHeight - scroller.clientHeight; if (overflow > 0) return overflow; return pageHeader ? pageHeader.offsetHeight * (1 - headerScaleTarget) : 0; ``` **Status** : fonctionne pour 549/570, mais casse 576 (voir point 4). ### 4. node/576 — colonne se fige trop tot, titre diminue en descendant (REGRESSIF) **Cause** : `ensureScrollForTitle` agrandit le spacer de la colonne gauche pour garantir assez de scroll pour le scale-down du titre. Le spacer rend `overflow > 0`, donc `leftFreezeStart` retourne `overflow`. Mais `overflow` peut etre inferieur a `titleEnd` (le spacer donne juste assez de scroll pour `end`, pas assez pour que `overflow = end`). La colonne se fige avant la fin du scale-down, le `y` pousse le titre vers le bas. **Tentative de fix avec `Math.max(overflow, titleEnd)`** : corrige 576 mais casse 549/570 (voir point 3). **Fix actuel** : `ensureScrollForTitle` est appele **avant** la creation des ScrollTriggers. La formule du spacer est `spacerAdd = end + viewport - colHeight`, ce qui garantit que `overflow = end = titleEnd` apres l'agrandissement. La logique d'origine de `leftFreezeStart` (`if overflow > 0 return overflow`) retourne alors une valeur >= `titleEnd`. **Status** : a tester. Le succes depend du timing : si le spacer est bien en place avant le calcul de `leftFreezeStart`, tout est coherent. ### 5. node/586 — pas d'image, scroll insuffisant pour le scale-down (NON FIXE) **Cause** : sans image, la colonne droite est vide. Le contenu de la colonne gauche est court. Le scroll naturel est insuffisant pour que le titre termine son scale-down. **Fix tente** : `ensureScrollForTitle` agrandit le spacer avec la formule `spacerAdd = end + viewport - colHeight`. Le scroll resultant devrait etre exactement `end`. **Probleme** : le spacer etait calcule avec l'ancienne formule `end - scrollRange` qui ne tenait pas compte du viewport. La nouvelle formule corrige le calcul, mais le `lenis.resize()` au `window.load` est necessaire pour que Lenis recalcule sa plage de scroll apres le chargement de la police. **Status** : partiellement corrige. Le scroll est cree mais peut etre insuffisant si Lenis ne refresh pas correctement. Le `lenis.resize()` au `load` a ete ajoute dans `initSmoothScroll`. ### 6. node/545 et 583 — pas d'image, texte court, pas de scroll (OK) Ces pages n'ont pas d'image et le contenu est court. Il n'y a pas de scroll, le titre ne diminue pas. C'est le comportement attendu et accepte. ## Fichiers modifies ### `assets/js/main.js` 1. **`initSmoothScroll()`** : - Ajout de `lenis.resize()` + `ScrollTrigger.refresh()` au `window.load` - `return lenis` pour permettre un acces futur 2. **`initPageFull()`** : - `cols`, `leftColumn`, `rightColumn`, `spacer`, `spacerBaseHeight` declares en haut de la fonction - `adjustStickyContext()` defini avant `ensureScrollForTitle`, calcule `--sticky-extent` base sur `scrollRange - imgHeight + clientHeight` - `ensureScrollForTitle()` : agrandit le spacer si `scrollRange < end`. Formule : `spacerAdd = end + viewport - colHeight`. Appele avant le `gsap.to()` et au `window.load`. - `y` et `end` du ScrollTrigger du titre : transformes en fonctions `() => pageHeader.offsetHeight * (1 - headerScaleTarget)` - `initHeaderH` supprime - `leftFreezeStart` : logique d'origine (`overflow` si > 0, sinon `titleEnd`), mais avec `pageHeader.offsetHeight` au lieu de `initHeaderH` ### `assets/scss/main.scss` 1. **`:after` de `.field_images`** : `height: 100vh` remplace par `height: var(--sticky-extent, 100vh)` ### `assets/js/titles.js` - Ajout de `measureOverflow()` : mesure le debordement du titre sous l'image et pose `data-overflow-lines` sur le h2. Utilise au scroll pour les pages liste (home, archives, projets lies). - Appele dans `initTitles()` apres le traitement Splitting de chaque titre. ## Points non resolus 1. **586** : le scroll cree par le spacer peut etre insuffisant si Lenis ne refresh pas correctement. Le `lenis.resize()` au `load` devrait aider mais n'a pas ete confirme. 2. **576** : depend du timing de `ensureScrollForTitle` par rapport aux ScrollTriggers. Si le spacer n'est pas en place avant le calcul de `leftFreezeStart`, la colonne se fige trop tot. 3. **Interaction `ensureScrollForTitle` / `adjustStickyContext`** : les deux se recalculent mutuellement mais peuvent entrer en conflit si les hauteurs changent apres le chargement. 4. **549/570** : la logique d'origine de `leftFreezeStart` fonctionne, mais toute modification qui change `overflow` (comme le spacer) peut casser le comportement. ## Approches envisagees mais abandonnees 1. **`Math.max(overflow, titleEnd)` dans `leftFreezeStart`** : corrige 576/586 mais casse 549/570. 2. **`format: 'iife'` dans Vite** : incompatible avec multiple entry points. 3. **`format: 'es'` dans Vite** : ne change pas le output (deja en format classique). 4. **`type: module` dans `leshed.libraries.yml`** : pour resoudre la redeclaration `const ct` entre `titles.js` et `main.js`.applique mais non teste en prod. ## Tests a faire - [ ] 549 : colonne gauche ne redescend pas en fin de scroll - [ ] 570 : colonne gauche ne redescend pas en fin de scroll - [ ] 576 : titre diminue sans descendre, contenu colle au header - [ ] 573 : image ne remonte pas en fin de scroll - [ ] 586 : titre peut terminer son scale-down - [ ] 545 : pas de scroll, titre ne diminue pas (OK) - [ ] 583 : pas de scroll, titre ne diminue pas (OK) - [ ] Pages liste (home, archives) : `data-overflow-lines` fonctionne