La scène .ft-stage (colonne flexible, fond rgb(26, 26, 36), 300 px de haut au minimum) contient un écran .transition-preview de 200 px de haut, coins de 12 px, overflow: hidden, et le bouton .trigger-btn dessous. Dans l'écran, trois couches superposées en position: absolute; inset: 0 : la page A (.old-content, dégradé violet), la page B (.new-content, dégradé cyan vers vert, cachée par opacity: 0; visibility: hidden) et, au-dessus (z-index: 10), le voile .fade-through-overlay : un aplat #0a0a0f à opacité 0, qui laisse passer les clics (pointer-events: none).
Au clic, le script pose la classe .animating sur le voile, qui joue @keyframes fadeThrough en 1,2 s ease-in-out : opacité 0 → 1 à 40 %, maintenue jusqu'à 60 %, retour à 0 à 100 %. L'écran est donc entièrement noir de 0,48 s à 0,72 s ; à 0,6 s, au milieu de ce noir, le script bascule la classe .is-b sur .transition-preview, et deux règles échangent les pages (A passe à opacity: 0; visibility: hidden, B à 1 et visible). À 1,2 s, .animating est retirée. Comme l'échange se fait sous un voile opaque, aucune image ne montre A et B en même temps — c'est la différence avec un fondu enchaîné, où les deux pages se superposent à mi-course.
La page atteinte est un état, pas un moment : .is-b reste posée, la page B tient jusqu'au clic suivant, qui rejoue le voile et ramène A (mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 : B affichée tant qu'on ne touche à rien, retour à A au deuxième clic, puis de nouveau B). Pendant l'animation, le bouton reçoit aria-disabled="true" et le script ignore les clics, plutôt que disabled : un bouton désactivé perd le focus clavier (mesuré dans Chromium et Firefox, le focus repart sur body), et la touche Entrée ne fonctionnerait plus pour revenir. Le script parcourt toutes les .ft-stage de la page, sans id ni fonction globale : plusieurs écrans cohabitent, chacun avec son bouton. Une variante du catalogue fait passer l'écran par une couleur pleine au lieu du noir.
CSS (@keyframes sur opacity, inset) + 1,5 Ko de JavaScript (classList, matchMedia), aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 : voile opaque au moment de l'échange, page B tenue, retour à A au clic suivant, focus clavier conservé, échange direct sous mouvement réduit. La propriété inset, qui place les trois couches, fixe les versions minimales : Chrome 87, Firefox 66, Safari 14.1.
Sans JavaScript, la page A reste affichée et le bouton ne fait rien : prévoyez un lien classique vers le contenu B si la page doit marcher sans script. Dans un navigateur sans inset (Safari avant 14.1), remplacez inset: 0 par top: 0; right: 0; bottom: 0; left: 0 sur .transition-content et .transition-overlay.