TransitionsGratuit

Fade Through

Un voile noir monte jusqu'à couvrir l'écran, la page change dessous, puis le voile se retire sur la nouvelle : 1,2 s dont 0,24 s de noir complet, et jamais une image où l'ancienne et la nouvelle page se mélangent — CSS, un calque, 1,5 Ko de JavaScript, la page B reste affichée jusqu'au clic suivant.

CSSOpacitySmooth

Mis à jour le

Page A
Page B
Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Transitions en contient 52 au total, dont 9 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →

Exemples d'usage

Urgences · Accueil IOABox de tri 1 · 14:36

Trié · 14:36

M. Lucien Arnaud

67 ans · venu par ses moyens, accompagné

Douleur thoracique depuis 40 min, irradiant dans le bras gauche.

TA
152/94
FC
104
SpO₂
95 %
T°
37,1

Tri 2 · très urgent→ déchocage, box 3

Arrivée borne · 14:32

Mme Inès Fabre

34 ans · saisi à la borne d'arrivée

Chute à vélo, cheville gauche gonflée — douleur 6/10 (déclarée).

TA
—
FC
—
SpO₂
—
T°
—

À trierconstantes à prendre

① Dossier du patient suivant — écran de tri infirmier aux urgences

QuandL'infirmière d'accueil valide le tri d'un patient et passe au suivant : l'écran quitte la fiche du patient trié (motif, constantes, niveau) pour celle du patient suivant, pré-remplie par la borne d'arrivée.
PourquoiLe noir intermédiaire garantit qu'aucune image ne mêle deux identités, même un instant : on ne lit jamais le nom de l'un à côté des constantes de l'autre.
RéglagesÉcran agrandi à 366 × 248 px, fond blanc pour les deux fiches, file d'attente à gauche ; voile noir d'origine, 1,2 s ; bouton vert « Valider le tri · patient suivant » sous la fiche.

RCaméra de recul

Obstacle à 1,2 m

DCaméra avant

Aire de service0 km/h12 °C

② Caméra de recul vers caméra avant — écran de bord d'un camping-car

QuandLe conducteur finit sa marche arrière sur l'aire de service et repasse en marche avant : l'écran quitte l'image de recul et ses lignes de guidage pour la caméra avant.
PourquoiDeux images vidéo fondues l'une dans l'autre seraient illisibles et trompeuses ; le noir bref dit « changement de caméra », comme en régie.
RéglagesÉcran de 424 × 240 px dans un cadre noir de tableau de bord ; lignes de guidage rouge, jaune, vert dessinées en CSS sur la page A, route et montagnes sur la page B ; le bouton physique « Changer de caméra » déclenche le voile.

Conduite

La Fiancée du Danube

  • 211« …et le fleuve se tait. »
  • 212Entracte — reprise dans 2 min
  • 213Acte II, sc. 1 — « La nuit est tombée sur le port »
  • 214« Personne ne viendra ce soir »

Écran de scène — aperçu

ENTRACTE

Reprise du spectacle dans 2 minutes

Acte II · scène 1

La nuit est tombée sur le port, et nulle lanterne ne répond.

Salle : extinctionFosse : prête

Suivant : 213L'acte II s'ouvre au top du chef

③ Fin de l'entracte vers premier surtitre de l'acte II — écran de surtitrage d'un opéra

QuandLa salle s'éteint pour la reprise : le régisseur des surtitres envoie la réplique suivante, l'écran passe d'« Entracte – reprise dans 2 min » au premier surtitre de l'acte II.
PourquoiLe fondu au noir suit la lumière de la salle : l'écran s'éteint avec elle et se rallume sur le texte, sans attirer l'œil pendant le noir scénique.
RéglagesLogiciel de régie : conduite des répliques à gauche, aperçu de l'écran de scène de 320 × 180 px à droite ; page B sur fond noir, texte blanc en serif 19 px ; gros bouton vert « GO ».

Comment ça marche

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.

Accessibilité

  • Mouvement réduit : sous prefers-reduced-motion: reduce, le script ne joue pas le voile et bascule .is-b tout de suite ; une transition CSS de 150 ms sur l'opacité des pages adoucit l'échange. Mesuré : aucune animation du voile, page B visible en moins de 250 ms.
  • Clavier : le déclencheur est un vrai <button type="button"> (Entrée et Espace marchent, aucun envoi de formulaire si l'écran est placé dans un <form>). Pendant 1,2 s il porte aria-disabled="true" mais garde le focus : on peut enchaîner A → B → A au clavier sans perdre sa place.
  • Lecteurs d'écran : la page cachée est en visibility: hidden, donc absente de l'arbre d'accessibilité ; seule la page affichée est lue. Le changement n'est pas annoncé de lui-même : si le contenu de B doit être lu dès son arrivée, ajoutez aria-live="polite" sur .transition-preview ou déplacez le focus sur le titre de B dans le délai d'échange.
  • Clignotement : un seul passage au noir par clic, 0,24 s de noir complet entouré de deux montées de 0,48 s : rien qui approche le seuil de trois éclats par seconde, même en cliquant sans arrêt (le bouton ignore les clics pendant l'animation).

Compatibilité navigateur

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.

Chrome 87+✓ Complet
Firefox 66+✓ Complet
Safari 14.1+✓ Complet
Edge 87+✓ Complet
Mobile iOS 14.5+✓ Complet (au toucher)
Android Chrome 87+✓ Complet (au toucher)

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.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="ft-stage">
  <div class="transition-preview">
    <div class="transition-content old-content">Page A</div>
    <div class="transition-content new-content">Page B</div>
    <div class="transition-overlay fade-through-overlay"></div>
  </div>
  <button type="button" class="trigger-btn">Trigger Transition</button>
</div>
CSS
.ft-stage,
.ft-stage *,
.ft-stage *::before,
.ft-stage *::after {
  box-sizing: border-box
}

/* Racine de l'effet : écran en haut, bouton dessous. */
.ft-stage {
  --space-4: 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 300px;
  padding: 16px;
  position: relative;
  background: rgb(26, 26, 36);
  color: #fff;
  font-family: sans-serif
}

.ft-stage .transition-preview {
  position: relative;
  width: 100%;
  height: 200px;
  border-radius: 12px;
  overflow: hidden;
  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%)
}

.ft-stage .transition-content {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  font-weight: 600;
  color: white
}

.ft-stage .transition-content.old-content {
  background: linear-gradient(135deg, #6366f1, #8b5cf6)
}

.ft-stage .transition-content.new-content {
  background: linear-gradient(135deg, #06b6d4, #10b981);
  opacity: 0;
  visibility: hidden
}

/* Page B affichée : la classe .is-b est posée sur l'écran au milieu de la transition, sous le calque,
   et retirée au clic suivant (retour à A). */
.ft-stage .transition-preview.is-b .old-content {
  opacity: 0;
  visibility: hidden
}

.ft-stage .transition-preview.is-b .new-content {
  opacity: 1;
  visibility: visible
}

.ft-stage .trigger-btn {
  padding: 12px 24px;
  font-size: 0.9rem;
  font-weight: 600;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-family: inherit;
  background: linear-gradient(135deg, #6366f1, #8b5cf6);
  color: white;
  transition: all 0.3s ease;
  margin-top: var(--space-4)
}

.ft-stage .trigger-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 30px rgba(99, 102, 241, 0.4)
}

.ft-stage .trigger-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none
}

.ft-stage .transition-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 10
}

.ft-stage .fade-through-overlay {
  background: #0a0a0f;
  opacity: 0
}

.ft-stage .fade-through-overlay.animating {
  animation: fadeThrough 1.2s ease-in-out
}

@keyframes fadeThrough {
  0% {
    opacity: 0;
  }

  40% {
    opacity: 1;
  }

  60% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

/* Mouvement réduit : pas de calque, les deux pages s'échangent en fondu de 150 ms. */
@media (prefers-reduced-motion: reduce) {
  .ft-stage .transition-content {
    transition: opacity .15s, visibility .15s
  }
}
JavaScript (fx-0685)
(function () {
  /* fx-0685 — Fade Through : un voile noir monte jusqu'à couvrir la page A, la page change dessous,
     puis le calque se retire sur la nouvelle page. Un clic mène à la page B et y reste ; le clic suivant ramène à A. */
  var SWAP_AT = 600;   // échange A ⇄ B, au milieu : le calque couvre tout l'écran
  var END_AT = 1200;   // fin de l'animation du calque (même durée que le CSS)
  var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');

  document.querySelectorAll('.ft-stage').forEach(function (stage) {
    var screen = stage.querySelector('.transition-preview');
    var layer = stage.querySelector('.fade-through-overlay');
    var btn = stage.querySelector('.trigger-btn');
    if (!screen || !layer || !btn) return;
    var busy = false;

    btn.addEventListener('click', function () {
      if (busy) return;
      // Mouvement réduit : pas de calque, échange direct (fondu de 150 ms dans le CSS)
      if (reduceMotion.matches) {
        screen.classList.toggle('is-b');
        return;
      }
      busy = true;
      btn.setAttribute('aria-disabled', 'true'); // et non disabled, qui ferait perdre le focus clavier
      layer.classList.add('animating');
      setTimeout(function () {
        screen.classList.toggle('is-b');
      }, SWAP_AT);
      setTimeout(function () {
        layer.classList.remove('animating');
        btn.removeAttribute('aria-disabled');
        busy = false;
      }, END_AT);
    });
  });
})();

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Couleur du voile (CSS — .fade-through-overlay { background }) #0a0a0f (presque noir) #fff donne un fondu au blanc, plus doux sur une interface claire ; une couleur de marque fait passer l'écran par elle. Gardez une couleur pleine : un voile semi-transparent laisserait voir l'échange.
Durée (CSS — animation: fadeThrough 1.2s ; JS — SWAP_AT, END_AT) 1,2 s ; échange à 600 ms, fin à 1 200 ms Changez les trois ensemble : SWAP_AT à la moitié de la durée, END_AT égal à la durée. 0,6 s convient à une navigation fréquente, 2 s à un changement de scène solennel.
Temps passé dans le noir (CSS — étapes 40% et 60% des keyframes) 40 % → 60 %, soit 0,24 s de noir complet 30% / 70% allonge le noir à 0,48 s, l'effet « rideau » d'une régie ; 45% / 55% le réduit à un battement. L'échange doit rester entre les deux étapes.
Courbe (CSS — ease-in-out) ease-in-out ease-in assombrit lentement puis vite, comme une lumière qu'on baisse ; linear donne un fondu mécanique, plus vidéo.
Contenus (HTML — .old-content, .new-content) « Page A » et « Page B » sur deux dégradés Mettez n'importe quel HTML dans chaque couche (fiche, image, vidéo). Pour enchaîner plus de deux pages, remplissez la couche cachée avant le clic ; le voile masque l'échange quelle que soit la page.
Échange en mouvement réduit (CSS — transition: opacity .15s, visibility .15s) fondu de 150 ms 0s rend l'échange instantané. Évitez de dépasser 200 ms : la règle sert à ceux qui ont demandé moins d'animation.

FAQ

Quelle différence avec Crossfade Transition (fx-0695) ?

Crossfade Transition fait un fondu enchaîné : l'ancienne page baisse pendant que la nouvelle monte, et à mi-course on voit les deux superposées à 50 %. Fade Through passe par le noir : l'ancienne page disparaît sous un voile opaque, l'échange se fait caché, la nouvelle apparaît ensuite. Choisissez le fondu enchaîné quand A et B se ressemblent (deux états d'un même écran) ; le passage par le noir quand mélanger les deux serait trompeur ou illisible — deux personnes, deux caméras, deux documents.

Comment déclencher la transition sans le bouton (changement d'onglet, minuterie, réponse d'un serveur) ?

Le bouton peut rester caché : appelez stage.querySelector('.trigger-btn').click() depuis votre code, le voile et l'échange suivent. Pour remplir la page B avec une réponse de serveur, écrivez son contenu dans .new-content avant l'appel ; si la réponse risque d'arriver après 0,6 s, attendez-la avant de cliquer, sinon l'écran se rallumerait sur une page vide.

Pourquoi le bouton n'est-il pas désactivé avec l'attribut disabled pendant l'animation ?

Parce qu'un bouton disabled perd le focus : mesuré dans Chromium 151 et Firefox 153, le focus repart sur body dès le premier clic au clavier, et un deuxième appui sur Entrée ne fait plus rien. Le script pose aria-disabled="true" (lu par les lecteurs d'écran, grisé par le CSS) et ignore lui-même les clics pendant 1,2 s : l'utilisateur garde sa place et peut revenir à A dès la fin du voile.