TransitionsGratuit

Staggered Reveal

Douze carrés qui entrent l'un après l'autre, 0,1 s d'écart, en montant de 20 px et en grandissant de 0,8 à 1 : la cascade part quand le bloc arrive à l'écran, puis la grille reste en place — 1,3 Ko de JavaScript, vos propres éléments acceptés, posés d'emblée si l'utilisateur réduit les animations.

CSSJSCascade

Mis à jour le

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

Sapeur Yanis Morel

Module « secours routier » validé

35 h de formation · évaluation finale 17/20

① Badges de fin de formation — plateforme des sapeurs-pompiers volontaires

QuandLe sapeur vient de valider le module « secours routier » : il ouvre ses badges, et les douze compétences acquises apparaissent une à une, du balisage à la transmission radio.
PourquoiLa cascade fait compter les acquis un par un : chaque badge a son instant, au lieu d'un bloc d'icônes qu'on survole. L'ordre suit celui de l'intervention, du balisage de la zone au compte rendu radio.
RéglagesConteneur en grille de 4 colonnes (display: grid) au lieu de la rangée flexible ; 12 badges de 121 × 48 px (pastille dorée de 30 px + libellé) à la place des carrés ; délai et durée d'origine (0,1 s, 0,6 s), 1,7 s pour toute la grille ; le panneau des badges réaffiché relance la cascade.
Contrôle technique · 45 min

Quel jour vous arrange ?

Semaine du 3 novembre · centre de Lamballe

② Créneaux libres pour le contrôle technique — site d'un centre auto

QuandLe client choisit le jeudi dans la semaine : les douze créneaux libres de 8 h à 17 h 15 arrivent dans l'ordre des heures, ligne par ligne.
PourquoiL'ordre d'apparition suit l'ordre horaire : l'œil lit de 8 h à 17 h sans chercher, et le premier créneau, le plus demandé, est aussi le premier visible.
RéglagesGrille de 4 colonnes, lecture ligne par ligne ; 12 pastilles vert pâle de 118 × 39 px, heure en 15 px gras ; aucun réglage de temps modifié ; retour à la semaine par un bouton, nouveau passage = nouvelle cascade.
Meeting de Vitré · 8-9 nov.

Résultats en attente de validation

Le jury a signé les dernières feuilles dimanche à 18 h.

③ Médailles du week-end — page résultats d'un club d'athlétisme handisport

QuandLe jury a validé les résultats du meeting : le club publie la page, et les huit médailles arrivent l'une après l'autre, l'or d'abord, avec le nom de l'athlète et son épreuve.
PourquoiChaque médaille arrive séparément : chaque athlète a son moment à l'écran, au lieu d'un tableau où les noms se confondent.
Réglages8 cartes de 117 × 99 px au lieu de 12 carrés (cascade de 1,3 s) ; grille de 4 colonnes, médaille de 40 px en dégradé or, argent ou bronze ; nom en 11,5 px gras, épreuve et catégorie dessous ; ordre or → argent → bronze.

Comment ça marche

Le cadre .md-stagger-stage mesure 250 px de haut, fond #1a1a24. Dedans, .md-stagger est une rangée flexible qui passe à la ligne (flex-wrap: wrap, espacement de 8 px, marge intérieure de 20 px). Chaque élément reçoit la classe .md-stagger-item : 40 × 40 px, coins de 8 px, dégradé indigo vers violet (#6366f1 → #a855f7). Il part invisible, 20 px plus bas et réduit à 0,8 (opacity: 0; transform: translateY(20px) scale(0.8)), et l'animation mdStaggerIn l'amène en 0,6 s à sa place, pleine taille ; forwards l'y laisse. Tout le décalage tient dans une ligne du script : animation-delay vaut i × 0,1 s. Le douzième carré démarre à 1,1 s et s'arrête à 1,7 s. Mesuré dans Chromium 300 ms après le départ : opacités 0,77 / 0,52 / 0,16 pour les trois premiers, 0 pour les neuf autres.

Le script parcourt chaque .md-stagger de la page. Si le conteneur a déjà des enfants, ce sont eux qui entrent en cascade ; s'il est vide, il crée 12 carrés de démonstration. La fonction play() retire puis remet la classe de chaque élément en forçant un recalcul entre les deux (void item.offsetWidth) : c'est ce qui relance une animation CSS déjà jouée. Un IntersectionObserver au seuil de 0,3 appelle play() quand 30 % du conteneur devient visible. Sortir de l'écran ne fait rien : la grille reste affichée. Y revenir rejoue la cascade. Mesuré dans Chromium, Firefox et WebKit : à 3,4 s puis à 6,4 s, les douze carrés sont toujours à opacité 1 ; après un aller-retour de défilement, 150 ms après le retour, opacités 0,35 / 0,04 / 0 — la cascade est repartie du début. Avec var once = true, l'observateur se déconnecte après la première entrée.

Pourquoi attendre l'écran plutôt que jouer au chargement ? Une cascade sert à faire lire une grille dans l'ordre : jouée hors de la vue, elle est finie avant que quelqu'un la regarde. Le conteneur reste vide tant qu'il n'est pas atteint, puis l'état final tient — un contenu qui disparaîtrait pour réapparaître toutes les trois secondes empêcherait de le lire. Le cadre a sa propre classe, préfixée comme le reste de l'effet : deux effets de la bibliothèque collés sur la même page ne se réécrivent pas. Une variante de la catégorie applique le même décalage, au clic, à la sortie et au retour d'une liste.

Accessibilité

  • Mouvement réduit : sous prefers-reduced-motion: reduce, le CSS retire l'animation et pose chaque élément à sa place (animation: none; opacity: 1; transform: none). Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : les douze éléments sont à opacité 1 dès la première mesure, sans montée ni agrandissement.
  • Lecteurs d'écran : vos propres éléments sont dans la page dès le chargement ; l'opacité nulle du départ ne les cache pas aux lecteurs d'écran, qui les lisent dans l'ordre du code. Les 12 carrés de démonstration sont des div vides : ils ne disent rien. Si la grille porte une information (des badges, des créneaux), écrivez-la dans les éléments eux-mêmes, pas dans un décor.
  • Durée : la cascade dure 1,7 s pour 12 éléments, une fois, puis plus rien ne bouge. Pour une grille plus longue, gardez cet ordre de grandeur (voir la FAQ) : au-delà de 3 s, les derniers éléments arrivent quand on a déjà commencé à lire.
  • Clavier : rien n'est interactif dans l'effet. Si vos éléments sont des liens ou des boutons, ils restent focalisables pendant la cascade ; le dernier est visible au plus tard 1,7 s après l'entrée à l'écran.

Compatibilité navigateur

CSS (@keyframes, animation-fill-mode: forwards, transform) + 1,3 Ko de JavaScript (IntersectionObserver, NodeList.forEach), aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : mêmes délais, même grille tenue après la cascade, même relance au retour dans l'écran, même pose directe sous mouvement réduit. IntersectionObserver existe depuis Chrome 51, Firefox 55 et Safari 12.1.

Chrome 51+✓ Complet
Firefox 55+✓ Complet
Safari 12.1+✓ Complet
Edge 79+✓ Complet
Mobile iOS 12.2+✓ Complet
Android Chrome 51+✓ Complet

Sans IntersectionObserver, le script joue la cascade une fois, au chargement. Sans JavaScript, le conteneur de démonstration reste vide ; vos propres éléments, s'ils portent déjà la classe md-stagger-item dans le HTML, apparaissent tous ensemble au chargement (même montée, sans décalage). Sans animations CSS, ils restent à opacité 0 : dans ce cas, n'ajoutez pas la classe dans le HTML et laissez le script la poser.

Le code

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

HTML
<div class="md-stagger-stage">
  <div class="md-stagger"></div>
</div>
CSS
.md-stagger-stage,
.md-stagger-stage *,
.md-stagger-stage *::before,
.md-stagger-stage *::after {
  box-sizing: border-box
}

.md-stagger-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 250px;
  overflow: hidden;
  position: relative;
  background: rgb(26, 26, 36);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.md-stagger {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 20px;
  flex-wrap: wrap;
}

.md-stagger-item {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: linear-gradient(135deg, #6366f1, #a855f7);
  opacity: 0;
  transform: translateY(20px) scale(0.8);
  animation: mdStaggerIn 0.6s forwards;
}

@keyframes mdStaggerIn {
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Mouvement réduit : les éléments sont posés d'emblée, sans montée ni agrandissement */
@media (prefers-reduced-motion: reduce) {
  .md-stagger-item {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
JavaScript (fx-0725)
(function() {
  // Staggered Reveal : chaque .md-stagger de la page entre en cascade quand il
  // arrive à l'écran, puis reste affiché. Il se rejoue s'il sort de l'écran et y revient.
  var once = false; // true : la cascade ne se joue qu'à la première entrée

  document.querySelectorAll('.md-stagger').forEach(function(container) {
    // Vos éléments s'il y en a dans le conteneur, sinon 12 carrés de démonstration
    var own = container.children.length > 0;

    function play() {
      if (!own) {
        container.innerHTML = '';
        for (var i = 0; i < 12; i++) container.appendChild(document.createElement('div'));
      }
      Array.prototype.forEach.call(container.children, function(item, i) {
        item.classList.remove('md-stagger-item');
        void item.offsetWidth; // relance l'animation
        item.classList.add('md-stagger-item');
        item.style.animationDelay = (i * 0.1) + 's';
      });
    }

    if (!('IntersectionObserver' in window)) {
      play();
      return;
    }
    var obs = new IntersectionObserver(function(entries) {
      if (!entries[0].isIntersecting) return;
      play();
      if (once) obs.disconnect();
    }, {
      threshold: 0.3
    });
    obs.observe(container);
  });
})();

Personnaliser

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

Option / propriétéDéfautEffet
Écart entre deux éléments (JS — i * 0.1) 0,1 s La durée totale vaut (nombre d'éléments − 1) × écart + 0,6 s. 0,05 s pour une grille de 20 éléments (1,55 s) ; 0,15 s pour quatre grandes cartes.
Durée et courbe (CSS — animation: mdStaggerIn 0.6s forwards) 0,6 s, courbe par défaut (ease) 0,4 s rend la cascade plus sèche. Une courbe cubic-bezier(.2, .8, .2, 1) fait arriver chaque élément vite et se poser en douceur.
Point de départ (CSS — transform: translateY(20px) scale(0.8)) 20 px plus bas, 80 % de la taille translateX(-16px) fait entrer depuis la gauche, utile pour une liste ; scale(0.5) seul donne un effet d'éclosion. Gardez l'état d'arrivée du @keyframes à zéro.
Seuil d'entrée (JS — threshold: 0.3) 30 % du conteneur visible 0 lance dès le premier pixel visible, quitte à jouer la cascade sous le bord de l'écran ; 0,6 attend que la grille soit presque entière.
Une seule fois (JS — var once = false) rejoue à chaque retour dans l'écran true déconnecte l'observateur après la première cascade : la grille reste ensuite immobile, même après un aller-retour de défilement.
Vos éléments (HTML — enfants de .md-stagger) aucun : 12 carrés de démonstration Dès que le conteneur a des enfants, ce sont eux qui entrent en cascade, dans l'ordre du code. Donnez-leur width: auto; height: auto et leur propre fond pour remplacer le carré de 40 px.

FAQ

Pourquoi la grille ne se rejoue-t-elle pas en boucle ?

Parce qu'une cascade accompagne une arrivée : une fois la grille entrée, elle doit rester lisible. Le script la rejoue seulement quand le conteneur revient à l'écran (après un défilement, ou après avoir été masqué). Pour la relancer à la demande, après un changement de filtre par exemple, masquez un parent du conteneur (attribut hidden) puis réaffichez-le un peu plus tard : l'observateur voit la grille revenir et rejoue la cascade. C'est ce que font les trois exemples de cette page. L'attribut hidden posé sur .md-stagger lui-même ne suffit pas : son display: flex l'emporte ; ajoutez alors .md-stagger[hidden] { display: none }.

Comment animer mes propres cartes au lieu des carrés ?

Placez-les dans .md-stagger : le script les trouve et leur applique la classe et le délai, dans l'ordre du HTML. Écrivez la classe md-stagger-item directement sur chaque carte : elles sont alors cachées dès le chargement, sans apparition furtive avant la cascade, et s'affichent même si le script ne se charge pas. Ajoutez width: auto; height: auto et votre fond pour remplacer le carré dégradé de 40 px ; la mise en page (grille, liste) est libre.

Et pour une liste de 40 éléments ?

À 0,1 s d'écart, le 40e arriverait après 4,5 s : trop long. Plafonnez le délai — Math.min(i, 12) * 0.1 fait partir tous les éléments au-delà du 12e en même temps, à 1,2 s, pour être en place à 1,8 s — ou réduisez l'écart à 0,03 s. Pour une longue page, observez plutôt chaque bloc séparément : un .md-stagger par section, chacun joue sa cascade quand il arrive à l'écran.