Split Text
Le JS découpe le libellé en un span par lettre ; au survol, chaque lettre saute de 8 px — les impaires vers le haut, les paires vers le bas — avec 30 ms de décalage entre voisines et une courbe à rebond de 500 ms.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Boutons comprend 27 effets, dont 2 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Le script parcourt chaque [data-split-text], lit son textContent, vide l'élément et recrée un <span class="split-char"> par caractère. Les espaces deviennent des espaces insécables (\u00A0) pour ne pas s'effondrer dans un inline-block, et chaque span reçoit style="animation-delay: n × 0.03s" selon son rang : 0 s pour la première lettre, 0,27 s pour la dixième. « Split Text » donne 10 spans en 136 px.
Côté CSS, .split-char est en display: inline-block (condition pour qu'un transform agisse) et .btn-split-text force overflow: visible — contrairement aux autres boutons du catalogue — pour que les lettres puissent sortir de la boîte. Au :hover, chaque span reçoit une animation de .5s cubic-bezier(.68, -.55, .265, 1.55), une seule itération, forwards ; :nth-child(2n+1) prend splitTextUp (50 % : translateY(-8px) rotate(-5deg)) et :nth-child(2n) prend splitTextDown (50 % : translateY(8px) rotate(5deg)). Début et fin sont à zéro : chaque lettre part et revient à sa place.
La courbe à dépassement s'applique à chaque moitié du trajet : la lettre dépasse ses 8 px puis revient, ce qui fait le rebond. Le décalage de 30 ms fait courir une vague le long du mot : la dernière lettre finit 0,27 s après la première, l'ensemble dure 0,77 s puis se fige (l'animation ne rejoue qu'en quittant et reprenant le survol). Le bouton lui-même se soulève de 2 px avec une ombre 0 10px 30px rgba(20,184,166,.4) en .3s.
Le script attend un bouton en texte brut. Ne l'appliquez jamais à un bouton déjà découpé en spans sur plusieurs lignes (par exemple le HTML récupéré après exécution du script) : il redécouperait aussi les retours à la ligne et les espaces d'indentation. Mesuré dans Chromium sur un tel bouton : 63 spans au lieu de 10, une largeur de 323 px au lieu de 136 (chaque espace d'indentation devient un espace insécable visible), des délais de 0,15 à 1,77 s, et toutes les lettres tombent sur un rang pair, donc toutes descendent — plus aucune alternance. Une seule source : soit <button class="demo-btn btn-split-text" data-split-text>Split Text</button> avec le script, soit un bouton pré-découpé sans le script.
Accessibilité
- Contraste insuffisant dans la version fournie : texte blanc sur le dégradé
#14b8a6 → #0d9488= 2,49:1 et 3,74:1, sous le seuil AA de 4,5:1 et, pour la première teinte, sous le seuil de 3:1 des grands textes. Passez àlinear-gradient(135deg, #0f766e, #115e59)(5,5:1 et 7,6:1). - Lecteurs d'écran : un libellé découpé en dix
<span>inline-block est parfois épelé ou coupé en mots aléatoires (« Spl it Te xt ») par VoiceOver et NVDA. Donnez au bouton un nom accessible entier : dans le script, avant de vider l'élément,t.setAttribute('aria-label', e.trim())— les spans deviennent alors purement décoratifs. - prefers-reduced-motion absent du code : ajoutez
@media (prefers-reduced-motion: reduce) { .btn-split-text:hover .split-char { animation: none } .btn-split-text { transition: none } }. Le bouton reste identique, sans vague ni soulèvement. - Clavier : pas de
:hoverà la navigation Tab. Dupliquez les trois règles de survol sous.btn-split-text:focus-visible(l'animation des spans et le soulèvement) pour que le focus déclenche la même vague. L'outlinenatif n'est pas retiré. - Tactile : effet exclusivement souris ; sur iOS l'état survolé (bouton soulevé) reste collé après un tap — enveloppez les règles dans
@media (hover: hover). Cible ≈ 50 px de haut, au-dessus du minimum de 44 px.overflow: visiblelaisse les lettres déborder de 8 px + rotation : prévoyez 12 px libres au-dessus et au-dessous.
Compatibilité navigateur
Requiert ES2015 (fonctions fléchées), NodeList.forEach, les animations CSS de transform et :nth-child(an+b) — tous les navigateurs depuis 2016. Zéro dépendance.
Sans JavaScript, un bouton en texte brut reste un bouton dégradé ordinaire qui se soulève au survol : aucune lettre ne bouge. Un bouton pré-découpé en spans (avec ses animation-delay en ligne) fonctionne en revanche sans aucun script. Sans animations CSS, les lettres restent immobiles.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Décalage entre lettres (JS — .03 * n) |
0.03s | Vitesse de la vague. 0.015s = les lettres sautent presque ensemble ; 0.08s = ola lente, qui dure 0,7 s sur dix lettres avant même le rebond. |
| animation-duration (CSS — :hover .split-char) | .5s | Durée du saut de chaque lettre. .35s = sec ; .8s = mou. La vague totale = durée + décalage × (nombre de lettres − 1). |
| translateY (CSS — @keyframes splitTextUp / splitTextDown, 50 %) | −8px / +8px | Amplitude du saut. ±4px = frémissement ; ±14px = les lettres quittent la boîte, prévoyez la place (overflow: visible). |
| rotate (CSS — @keyframes, 50 %) | −5deg / +5deg | Inclinaison au sommet du saut. 0deg = pur rebond vertical ; ±12deg = lettres qui basculent, moins lisibles pendant 0,25 s. |
| cubic-bezier(.68, -.55, .265, 1.55) (CSS — animation) | courbe à dépassement | Appliquée à chaque moitié du saut : la lettre dépasse ses 8 px puis revient. ease-in-out à la place donne un saut propre sans rebond. |
| Alternance (CSS — :nth-child(2n+1) / :nth-child(2n)) | haut / bas | Direction par rang. Mettez splitTextUp aux deux sélecteurs pour une vague entièrement vers le haut ; :nth-child(3n) pour une lettre sur trois. |
| background (CSS — .btn-split-text) | linear-gradient(135deg, #14b8a6, #0d9488) | Couleur du bouton. Pour un contraste AA avec le texte blanc : #0f766e → #115e59. L'ombre du survol (rgba(20,184,166,.4)) est à recolorer avec. |
FAQ
textContent, qui contient les sauts de ligne et les espaces d'indentation, et crée un span par caractère — y compris pour chaque espace, converti en espace insécable visible. Résultat mesuré : 63 spans, un bouton 2,4 fois plus large, et chaque lettre à un rang pair (donc splitTextDown pour toutes). Repartez d'un bouton en texte brut : <button class="demo-btn btn-split-text" data-split-text>Votre texte</button>, et laissez le script découper.animation et les deux animation-name de .btn-split-text:hover .split-char vers .btn-split-text .split-char. Pour une boucle, remplacez 1 par infinite et ajoutez une pause entre deux vagues en allongeant les keyframes (le saut entre 0 % et 30 %, immobilité jusqu'à 100 %). Pensez alors à prefers-reduced-motion : une vague permanente est bien plus intrusive qu'une vague au survol.[data-split-text] et ne recrée que du texte : une icône SVG ou un <span> enfant disparaîtrait. Posez data-split-text non pas sur le bouton mais sur un <span> qui n'enveloppe que le libellé, et placez l'icône à côté : <button class="btn-split-text"><span data-split-text>Jouer</span> ▶</button>. Adaptez alors les sélecteurs CSS (.btn-split-text:hover .split-char fonctionne tel quel, les spans restant des descendants).