Glitch Effect
Deux copies du libellé, découpées en bandes par clip-path, sautent de ±3 px avec des ombres magenta et cyan — un glitch permanent, en CSS seul.
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
.btn-glitch::before et ::after recréent le libellé avec content: attr(data-text) — l'attribut data-text du bouton doit porter le même texte que son contenu. Les deux pseudo-éléments sont en position: absolute; top: 0; width: 100%; height: 100%, centrés en display: flex, avec le même fond #6366f1 que le bouton : là où une bande est visible, elle recouvre entièrement le texte d'origine.
Chaque copie ne montre qu'une bande horizontale. ::before est décalé de left: 2px avec un fantôme magenta (text-shadow: -2px 0 #f0f) et animé par glitchTop (1,4 s) ; ::after est décalé de left: -2px avec un fantôme cyan (text-shadow: 2px 0 #0ff) et animé par glitchBottom (1,7 s). Le clip-path: inset(haut 0 bas) de chaque keyframe découpe une bande située entre 22 % et 66 % de la hauteur — là où se trouve le texte.
steps(1, end) fait tout le caractère de l'effet : aucune interpolation entre les keyframes. Tous les 20 % du cycle (280 ms pour la bande haute, 340 ms pour la basse), la bande saute à une nouvelle position et à un nouveau translateX entre −3 et +3 px. Les deux durées n'ont pas de diviseur commun : le motif ne se répète qu'au bout de 23,8 s et paraît aléatoire.
overflow: hidden sur .demo-btn coupe les débordements de ±3 px aux bords, et position: relative sur .btn-glitch ancre les pseudo-éléments. L'animation tourne dès le chargement, sans survol : le bouton n'est jamais immobile.
Accessibilité
- prefers-reduced-motion absent du code : un mouvement permanent dans le champ de vision. Ajoutez
@media (prefers-reduced-motion: reduce) { .btn-glitch::before, .btn-glitch::after { display: none } }— il reste un bouton indigo ordinaire, au libellé parfaitement lisible. - Lecteurs d'écran :
content: attr(data-text)est du contenu généré, que Chrome, Firefox et Safari intègrent au nom accessible d'un bouton — le libellé peut être annoncé trois fois (« Jouer Jouer Jouer »). Posezaria-labelsur le bouton avec le texte visible : il devient le nom accessible et les pseudo-éléments sont ignorés. - Lisibilité : à tout instant, une bande décale une partie des glyphes de 3 px au plus et ajoute des fantômes colorés. Acceptable pour un libellé court (un ou deux mots, 16 px, graisse 600) ; à éviter sur un texte long ou sous 14 px.
- Contraste : texte blanc sur
#6366f1= 4,47:1, juste sous le seuil AA de 4,5:1 ; les fantômes magenta et cyan dégradent encore la lisibilité locale.#4f46e5donne 6,3:1 — à changer aux trois occurrences (bouton et deux pseudo-éléments). - Scintillement : les bandes changent 3,6 et 2,9 fois par seconde, mais la zone qui bouge fait quelques pixels de haut sur un petit élément — loin des éclairs plein écran visés par WCAG 2.3.1. La règle reduced-motion ci-dessus reste la bonne protection pour les personnes sensibles au mouvement. Anneau de focus natif conservé (pas d'
outline: none).
Compatibilité navigateur
Requiert clip-path: inset() sans préfixe, content: attr(), la fonction de temps steps() et text-shadow. Aucun JavaScript, zéro dépendance.
Sans clip-path, les deux copies s'affichent entières l'une sur l'autre : le texte reste lisible mais bouge en bloc de ±3 px avec ses ombres colorées. Doublez les déclarations en -webkit-clip-path pour Safari 9.1 à 13.
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 |
|---|---|---|
| data-text (HTML — attribut du bouton) | identique au libellé | Texte des deux copies. Doit rester égal au libellé visible ; dans un framework, liez les deux à la même variable. |
| animation-duration (CSS — ::before 1.4s / ::after 1.7s) | 1.4s / 1.7s | Vitesse des sauts (cinq par cycle). Gardez deux durées sans diviseur commun pour éviter un motif visible ; 0.8s / 1.1s = frénétique. |
| translateX (CSS — @keyframes glitchTop / glitchBottom) | −3 à +3 px | Amplitude du tremblement horizontal. ±6 px déchire franchement le texte ; ±1 px donne un frisson. |
| clip-path: inset(…) (CSS — chaque keyframe) | bandes entre 22 % et 66 % de la hauteur | Position et épaisseur des tranches. Elles visent la zone du texte (≈ 30–70 % de la hauteur avec le padding par défaut) — à recaler si vous changez le padding. |
| text-shadow (CSS — ::before #f0f / ::after #0ff) | −2px 0 #f0f / 2px 0 #0ff | Aberration chromatique. Un seul canal (#f0f seul) est plus sobre ; 0 0 6px à la place du décalage donne un halo au lieu d'un dédoublement. |
| left: 2px / -2px (CSS — ::before / ::after) | ±2px | Décalage de base des copies par rapport au texte d'origine. À 0, les copies ne se distinguent que par leurs ombres. |
| steps(1, end) (CSS — animation-timing-function) | steps(1, end) | Saccade sans interpolation. steps(2, end) ajoute une position intermédiaire ; linear transforme le glitch en glissement continu — ce n'est plus un glitch. |
FAQ
.btn-glitch::before, .btn-glitch::after { display: none }. Réaffichez-les avec leurs animations sous .btn-glitch:hover::before et .btn-glitch:hover::after (display: flex plus la déclaration animation de chacune). L'animation repart de son 0 % à chaque survol.data-text, pas le contenu du bouton. Si vous changez le libellé (traduction, état « Chargement… »), mettez l'attribut à jour au même moment : btn.dataset.text = btn.textContent. Un attribut absent donne des bandes vides : le texte semble alors troué.position: relative fait des ::before/::after en position: absolute des calques exactement superposés au bouton (top: 0; width/height: 100%). overflow: hidden coupe ce qui dépasse quand les copies glissent de ±3 px et à cause des left: ±2px — sans lui, des bords de couleur apparaissent à l'extérieur du bouton.