Transitions✨ Premium

Morph Transition

Un élément qui passe vraiment de display: flex à display: none — et retour — sans perdre son animation : il rétrécit à 0,3, s'arrondit en ovale et s'efface en 0,6 s avant de disparaître du flux, puis réapparaît par le même chemin grâce à @starting-style. transition-behavior: allow-discrete là où le navigateur sait animer display, repli mesuré en JavaScript ailleurs (Firefox), fondu de 0,15 s sous mouvement réduit.

CSSallow-discreteMorph

Mis à jour le

Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Transitions comprend 52 effets, dont 9 gratuits. Découvrir la catégorie →

3 exemples d'usage

Champ « numéro de TVA » — formulaire d'ouverture de compte d'un grossiste — Morph Transition exemple 1

① Champ « numéro de TVA » — formulaire d'ouverture de compte d'un grossiste

QuandUne épicerie ouvre son compte chez un grossiste en fruits et légumes : en cochant « Je suis un professionnel », le champ « Numéro de TVA intracommunautaire » apparaît ; en décochant, il disparaît et l'e-mail remonte.
PourquoiLe champ passe vraiment en display: none : absent, il ne prend pas de place et n'est pas lu par les lecteurs d'écran, tout en gardant une entrée et une sortie animées.
RéglagesÉlément remis dans le flux (position: relative, cadre sans hauteur fixe) ; champ crème bordé d'ambre ; la case est un .morph-tr-btn qui dessine sa coche d'après aria-expanded ; départ caché avec aria-expanded="false".
Bandeau « travaux sur la ligne 4 » — page d'un réseau de bus — Morph Transition exemple 2

② Bandeau « travaux sur la ligne 4 » — page d'un réseau de bus

QuandSur la page de l'arrêt Gare, le bandeau orange des travaux s'efface à la fin du chantier ; le bouton « Info trafic » le fait revenir. Les prochains passages remontent quand il disparaît.
PourquoiRetiré du flux en fin d'animation, le bandeau ne laisse pas de trou dans la page : la liste des passages reprend sa place sans espace vide.
RéglagesBandeau pleine largeur, liseré orange de 5 px à gauche, texte brun sur fond crème ; bouton dans l'en-tête vert du réseau ; même durée de 0,6 s.
Détail des frais de port — panier d'une boutique de thé en vrac — Morph Transition exemple 3

③ Détail des frais de port — panier d'une boutique de thé en vrac

QuandLe client touche « Détail » sur la ligne des frais de port : emballage, transporteur et seuil du port offert (encore 6,50 € d'achats) s'insèrent sous la ligne, et le total puis le bouton « Commander » descendent d'autant.
PourquoiLe bloc entre et sort du flux par display: none, sans réserver de place vide : le panier reste court tant qu'on ne demande rien.
RéglagesBloc crème remis dans le flux du panier (position: relative, cadre sans hauteur fixe) : deux lignes de frais, une jauge et le reste à acheter pour le port offert ; bouton « Détail » en pastille verte sur la ligne « Livraison » ; le total et « Commander » remontent quand le bloc passe à display: none.

Comment ça marche

Le bloc .morph-tr-element (dégradé rose #ec4899 → #f43f5e, coins de 14 px) est piloté par un bouton qui le désigne par aria-controls. Pour le cacher, une seule classe, .morph-tr-display-none, qui demande à la fois display: none, opacity: 0, scale(0.3) et border-radius: 50%. D'ordinaire, display bascule d'un coup et coupe toute animation. Ici la transition le cite avec allow-discrete (transition: all 0.6s …, display 0.6s allow-discrete) : le navigateur garde l'élément affiché pendant les 0,6 s, joue le rétrécissement et l'arrondi, puis seulement le retire. Mesuré dans Chromium 151 et WebKit 26.5 : à 100 ms, opacité 0,38 à 0,43 et échelle 0,55 à 0,60 ; à 950 ms, display: none.

Le retour pose un autre problème : un élément qui sort de display: none n'a pas d'état « avant », donc rien à animer. La règle @starting-style lui en fournit un (opacité 0, échelle 0,3, coins à 50 %) : à l'affichage, il part de là et revient à sa forme sur la courbe à rebond cubic-bezier(0.34, 1.56, 0.64, 1) — mesuré à 100 ms : opacité 0,57 à 0,62, échelle 0,70 à 0,73, dans les trois moteurs. Conséquence à connaître : @starting-style joue aussi au premier affichage de la page, l'élément entre donc en douceur au chargement.

Firefox 153 répond oui à CSS.supports('transition-behavior', 'allow-discrete') mais n'anime pas encore display : sans repli, l'élément y disparaîtrait d'un coup (mesuré : display: none et opacité 0 dès 150 ms). Le script (2,4 Ko) le vérifie sur un témoin invisible au chargement — il passe un div en display: none avec la transition et lit si display a tenu. S'il n'a pas tenu, il anime par la classe .morph-tr-hidden puis pose .morph-tr-gone (display: none) au bout des 0,6 s ; à l'entrée, il retire .morph-tr-gone, force un calcul, puis retire .morph-tr-hidden. Même résultat mesuré dans Firefox : opacité 0,39 à 100 ms, display: none à 700 ms. Un départ caché se déclare par aria-expanded="false" sur le bouton : le script pose les classes de l'état caché qui conviennent au navigateur.

Accessibilité

  • Vraiment absent une fois caché : en display: none, l'élément n'est ni lu par les lecteurs d'écran, ni atteint par la touche Tab — ce qu'une simple opacité à 0 ne garantit pas. C'est l'intérêt de cet effet pour un champ de formulaire ou un panneau de filtres.
  • État du bouton : le bouton porte aria-controls et aria-expanded, mis à jour à chaque clic (true quand l'élément est affiché, mesuré). Pour une case à cocher qui affiche un champ (scène 1), gardez un vrai bouton ou une vraie case dont le changement appelle la même bascule.
  • Mouvement réduit : sous prefers-reduced-motion, ni rétrécissement ni arrondi : un fondu de 0,15 s, puis display: none (mesuré dans les trois moteurs : échelle 1 et opacité 0,08 à 0,11 à 100 ms, retiré à 350 ms).
  • Contraste : texte blanc en gras de 13,6 px sur #ec4899 → #f43f5e, soit 3,5 à 3,7:1, sous le seuil AA de 4,5:1. Pour un texte qui compte, prenez #be185d → #be123c (6,0 et 6,3:1) ou mettez le contenu sur fond clair, comme dans les scènes.

Compatibilité navigateur

CSS (@starting-style, transition-behavior: allow-discrete, @supports) + 2,4 Ko de JavaScript, aucune dépendance. Mesuré dans Chromium 151, WebKit 26.5 (chemin CSS) et Firefox 153 (chemin JavaScript) : même sortie animée suivie de display: none, même entrée, même état final, double clic rapide sans état bloqué, deux éléments indépendants dans la même page. Repères publics : @starting-style depuis Chrome 117, Safari 17.5 et Firefox 129 ; transition de display depuis Chrome 117 et Safari 18 ; Safari 17.4 à 17.6 ne connaît que la valeur allow-discrete, et le script y prend le relais.

Chrome 117+✓ Complet (chemin CSS)
Firefox 129+✓ Complet (sortie animée par le script)
Safari 17.5+✓ Complet (chemin CSS dès 18, script avant)
Edge 117+✓ Complet
Mobile iOS 17.5+✓ Complet (chemin CSS dès 18, script avant)
Android Chrome 117+✓ Complet

Dans les navigateurs plus anciens, le script prend le chemin par classes : sortie animée puis display: none, entrée animée. Seule l'arrivée au chargement de la page (portée par @starting-style) n'y est pas animée. Sans JavaScript, le bouton ne fait rien et l'élément reste dans son état de départ.

Le code

Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :

index.html — structure
<div class="morph-tr-stage">
  <div class="morph-tr-wrapper">
    <div class="morph-tr-element" id="morphTrElement">…</div>
  </div>
  <button class="morph-tr-btn" type="button" aria-controls="morphTrElement" aria-expanded="true">…</button>
</div>
Débloquer le code complet — 9,90 € TTC/mois

Le code complet HTML + CSS + JS de Morph Transition, prêt à coller — et les 840 effets. Ou 299 € TTC une fois, accès à vie.

Personnaliser

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

Option / propriétéDéfautEffet
Forme de sortie (CSS — .morph-tr-display-none, .morph-tr-hidden et @starting-style) scale(0.3), border-radius: 50%, opacité 0 translateY(-8px) à la place de l'échelle donne une sortie vers le haut plus sobre. Changez les trois règles ensemble pour que l'entrée et la sortie se répondent.
Durée (CSS — 0.6s ; JS — 600 dans le repli) 0,6 s Gardez la même valeur dans le CSS (display 0.6s allow-discrete compris) et dans le délai du script, sinon Firefox retire l'élément trop tôt ou trop tard.
Libérer la place (CSS — .morph-tr-wrapper et position: absolute de l'élément) cadre fixe de 200 × 100 px : la place reste réservée Pour que le contenu suivant remonte, remettez l'élément dans le flux (position: relative) et retirez la hauteur du cadre. Ne posez pas de display avec un sélecteur plus fort que .morph-tr-element.morph-tr-display-none (style en ligne, id…) : la règle de l'état caché doit pouvoir le passer à none. Le display: flex du CSS vendu, moins précis, ne gêne pas.
Départ caché (HTML — aria-expanded="false" sur le bouton, classe morph-tr-display-none sur l'élément) affiché au chargement La classe évite un affichage furtif avant le script ; le script pose ensuite la bonne combinaison de classes pour le navigateur.
Entrée au chargement (CSS — bloc @starting-style) l'élément affiché entre en douceur à l'ouverture de la page Pour qu'il soit là d'emblée, ajoutez une classe sur la page après le premier affichage et limitez la règle @starting-style à cette classe.
Rebond (CSS — cubic-bezier(.34, 1.56, .64, 1)) léger dépassement à l'entrée ease pour un formulaire sérieux ; le rebond convient mieux à une bulle ou une carte.

FAQ

C'est le principe de display: none : pendant les 0,6 s, l'élément reste affiché (il rétrécit visuellement, mais scale ne change pas la place qu'il occupe), puis il est retiré et ce qui suit remonte d'un bloc. À l'entrée, c'est l'inverse : la place s'ouvre d'abord, l'élément grandit ensuite. Pour un glissement progressif du contenu suivant, il faut animer la hauteur, comme le fait Accordion Fold (fx-0720).
Parce que Firefox 153 répond oui à CSS.supports('transition-behavior', 'allow-discrete') tout en retirant l'élément sans attendre la fin de la transition (mesuré). Le test sur témoin interroge le comportement réel : il passe un div invisible en display: none avec la transition et regarde si display tient encore. Le jour où Firefox animera display, il prendra le chemin CSS sans rien changer au code.
Popover Bloom ouvre une bulle par-dessus la page, ancrée à un bouton, sans toucher à la mise en page. Morph Transition agit dans le flux : l'élément occupe sa place quand il est là et la rend quand il disparaît. Les deux s'appuient sur @starting-style pour l'entrée.