Text Morph
Deux mots superposés qui se relaient toutes les 2,5 s au même endroit : celui qui part se floute à 12 px, rétrécit et descend, pendant que le suivant se précise — un fondu enchaîné flou en dégradé violet-cyan, sans lettre parasite. CSS et 1,4 Ko de JavaScript, liste de mots dans le HTML, simple fondu si l'utilisateur réduit les animations.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Liquid comprend 31 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le conteneur .text-morph-container (60 px de haut, contenu centré en flex) porte deux <span class="text-morph-word"> en position absolue, donc posés exactement l'un sur l'autre. Le texte est peint d'un dégradé violet vers cyan (#a855f7 → #06b6d4) découpé à la forme des lettres (background-clip: text et -webkit-text-fill-color: transparent), en 2,5rem, graisse 900. Deux classes décrivent les deux états : .text-morph-in (opaque, net, taille normale) et .text-morph-out (opacity: 0, filter: blur(12px), scale(.9) et 8 px plus bas). Une seule transition de 0,6 s sur l'opacité, le flou et la transformation fait tout le travail.
Le script (1,4 Ko) traite chaque .text-morph-container de la page. Toutes les 2,5 s (attribut data-interval, en millisecondes), il écrit le mot suivant dans le mot caché, puis échange les deux classes : le mot visible part en flou pendant que l'autre arrive, au même endroit. La liste vient de l'attribut data-words du conteneur, mots séparés par des virgules, relue à chaque relais : on peut la changer à tout moment depuis la page. Le premier mot est celui écrit dans le premier span. Mesuré dans Chromium 151, Firefox 153 et WebKit 26 sur 11 s : Design, Créatif, Moderne, Élégant, Fluide, dans l'ordre, sans mot sauté.
Les deux mots ne bougent jamais de place : le conteneur garde sa hauteur, la ligne au-dessus et au-dessous ne saute pas, même quand le nouveau mot est plus long. Il n'a pas de largeur propre (ses deux mots sont en position absolue) : seul sur sa ligne, il se centre ; dans une phrase, donnez-lui une largeur ou toute la ligne, comme dans les exemples. Le minuteur ne fait rien quand le bloc est hors de l'écran (IntersectionObserver) ou l'onglet masqué. Les classes d'état sont préfixées (text-morph-in, text-morph-out) : une classe .hidden ou .visible de votre framework ne vient pas masquer un mot.
Accessibilité
- Mouvement réduit : sous
prefers-reduced-motion: reduce, seule l'opacité est animée : le mot sortant s'efface sans flou, sans rétrécir ni descendre, le suivant apparaît en fondu au même endroit. Mesuré dans les trois moteurs :filter: noneettransform: nonesur le mot caché, relais toujours dans l'ordre. - Lecteurs d'écran : le mot caché porte
aria-hidden="true", déplacé à chaque relais : un lecteur d'écran ne lit qu'un mot, celui affiché. Le changement n'est pas annoncé (pas de zonearia-live), ce qui évite une annonce toutes les 2,5 s. Si la liste compte (les métiers recrutés, les produits du jour), donnez-la aussi en texte ailleurs dans la page. - Mouvement continu : le relais tourne sans fin. Sur une page qu'on lit longtemps, prévoyez un moyen de l'arrêter ou limitez-le à quelques tours (critère 2.2.2 des WCAG pour un contenu qui bouge plus de 5 s).
Compatibilité navigateur
CSS (transition de opacity, filter et transform, background-clip: text) + 1,4 Ko de JavaScript, aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26 : même ordre des mots, même fondu flou, même fondu simple sous mouvement réduit, pause hors écran vérifiée dans Chromium et Firefox. Le texte en dégradé s'écrit en propriétés séparées (-webkit-background-clip puis background-clip) : la forme courte background: … text est ignorée par les navigateurs plus anciens, qui laisseraient un texte transparent.
Sans JavaScript, le premier mot reste affiché, net et lisible ; le second reste caché. Sans filter (cas rare), les mots se relaient en fondu et léger glissement, sans flou. Sans IntersectionObserver, le relais tourne aussi hors écran.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec votre achat) :
<div class="fx-0405-root">
<div class="text-morph-container" data-words="Design, Créatif, Moderne, Élégant, Fluide">
<span class="text-morph-word text-morph-in">Design</span>
<span class="text-morph-word text-morph-out" aria-hidden="true">Créatif</span>
</div>
</div>
Paiement unique, sans abonnement — le code est à toi pour toujours
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Liste des mots (HTML — data-words sur .text-morph-container) |
Design, Créatif, Moderne, Élégant, Fluide |
Mots ou groupes de mots séparés par des virgules. Écrivez le premier dans le premier span. La liste est relue à chaque relais : changez l'attribut depuis votre script pour suivre une langue ou un stock. |
Cadence (HTML — data-interval, en millisecondes) |
2500 | 4000 pour un écran qu'on lit de loin ; en dessous de 1500, le mot n'a pas le temps d'être lu avant de repartir (la transition dure 0,6 s). |
Flou et déplacement (CSS — .text-morph-word.text-morph-out) |
blur(12px), scale(.9) translateY(8px) |
blur(20px) donne une sortie plus vaporeuse ; transform: none garde un fondu flou sur place. |
Durée du fondu (CSS — transition: opacity .6s, filter .6s, transform .6s) |
0,6 s | 1 s pour un relais très doux ; gardez-la nettement plus courte que la cadence. |
Dégradé du texte (CSS — background de .text-morph-word) |
linear-gradient(135deg, #a855f7, #06b6d4) |
Toute couleur ou dégradé ; pour un texte uni, retirez le dégradé et -webkit-text-fill-color, puis posez color. |
Taille (CSS — font-size de .text-morph-word, hauteur du conteneur) |
2,5rem, conteneur de 60 px | Adaptez la hauteur du conteneur à la taille du mot ; pour des noms longs, baissez la taille et ajoutez white-space: nowrap. |
FAQ
display: inline-flex et donnez-lui la largeur du mot le plus long (width: 7ch par exemple) : la phrase ne bougera pas d'un mot à l'autre.data-words de chaque version de la page. Comme la liste est relue à chaque relais, vous pouvez aussi la remplacer en direct (container.dataset.words = 'Hello, Hola, Ciao') : le prochain mot affiché vient de la nouvelle liste.