Accueil / Articles

Un bouton de retour d’information avec pouce levé animé sans aucune bibliothèque d’animation

Créez un widget de feedback avec des pouces levés et baissés, doté d’une animation pop basée sur des keyframes, d’états actifs et d’une requête fetch, en utilisant uniquement HTML, CSS et quelques lignes de JavaScript.

Éditeur : REACTAPP.TOP7 min de lecture1559 mots

Sources: Creating an Interactive Thumbs Up Feedback Button with HTML, CSS and JavaScript

Les boutons pouce levé et pouce baissé constituent l’un des moyens les moins coûteux pour demander aux utilisateurs si une page, une réponse ou une fonctionnalité les a aidés. La mise en œuvre habituelle se limite à changer la couleur d’arrière-plan au clic, ce qui peut facilement passer inaperçu lorsque rien d’autre sur la page ne change. Cette démarche guide vous vers une version légèrement plus riche : en cliquant sur le pouce levé, l’icône est brièvement remplacée par un smiley scintillant qui apparaît avec une animation d’agrandissement et de rotation, avant de reprendre son aspect de pouce levé mis en évidence. Vous verrez le code HTML, le CSS, le JavaScript qui gère les différents états, ainsi que les points à corriger avant de le déployer.

Fonctionnement du widget

L’interface dispose de deux boutons circulaires, l’un pour un retour positif et l’autre pour un retour négatif. Un clic sur le bouton positif déclenche une courte séquence :

  1. L’icône du pouce levé est remplacée par un smiley entouré de symboles scintillants.
  • Une animation à frames clés l’affiche avec une légère expansion et rotation.
  • Après une courte pause, le smiley redevient un simple pouce levé.
  • Le bouton passe en état actif, mis en évidence.
  • De telles micro-interactions donnent à une interface un sentiment de réactivité sans avoir recours à un framework d’animation.

    Marquage pour les deux boutons

    Chaque bouton dispose d’un id pour le scripting, d’une classe commune pour le stylage et d’une classe modificatrice pour sa propre couleur active. Les icônes sont des caractères emoji à l’intérieur des boutons :

    <div class="feedback">
      <button
          id="thumbsUp"
          class="feedback-btn thumbs-up"
          aria-label="Thumbs up">
          👍🏻
      </button>
      <button
          id="thumbsDown"
          class="feedback-btn thumbs-down"
          aria-label="Thumbs down">
          👎🏻
      </button>
    </div>
    

    Le aria-label est important ici. Sans lui, un lecteur d’écran annoncerait le nom propre de l’emoji (ou rien d’utile) ; donc ce label donne à chaque bouton un nom accessible clair. Si vous souhaitez que les technologies d’assistance sachent également quelle option est sélectionnée, ajoutez aria-pressed et mettez-le à jour en même temps que la classe active.

    Boutons ronds avec une réaction au survol

    Le style de base définit la taille, élimine l’apparence par défaut, centre l’icône à l’aide de Flexbox et transforme le bouton en cercle :

    .feedback-btn {
        width: 52px;
        height: 52px;
        border: 1px solid #ddd;
        background: #fff;
        border-radius: 50%;
        cursor: pointer;
        font-size: 25px;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: all 0.25s ease;
    }
    

    border-radius: 50% sur un élément carré crée un cercle, et transition rend les changements ultérieurs plus fluides ; en spécifiant des propriétés précises au lieu de all, on évite d’animer des éléments qui ne le nécessitent pas.

    Une règle de survol assombrit légèrement l’arrière-plan et agrandit un peu le bouton, afin que le curseur reçoive une réponse immédiate :

    .feedback-btn:hover {
        background: #f5f5f5;
        transform: scale(1.08);
    }
    

    Les keyframes derrière l’effet visuel

    Le moment intéressant est le passage du pouce levé au smiley scintillant. Cela est géré par une règle @keyframes nommée sparkle:

    @keyframes sparkle {
        0% {
            transform: scale(0.4) rotate(-20deg);
            opacity: 0;
        }
        35% {
            transform: scale(1.4) rotate(10deg);
            opacity: 1;
        }
        65% {
            transform: scale(1) rotate(-5deg);
        }
        100% {
            transform: scale(1) rotate(0deg);
        }
    }
    

    En termes de chronologie : à 0 %, le contenu est réduit à 40 % de sa taille, incliné de 20 degrés vers l’arrière et invisible. À 35 %, il atteint une taille de 140 % avec une légère inclinaison dans l’autre direction et une opacité totale. À 65 %, il redevient de taille normale avec une petite rotation inverse, et à 100 %, il retrouve sa taille et son angle naturels. Ce dépassement suivi d’un retour à la normale crée l’effet de « pop ». Notez que cette règle définit uniquement le mouvement ; une classe .sparkle distincte qui l’applique avec animation: sparkle 0.8s ease forwards est présentée dans l’exemple complet à la fin.

    Sequencement des états en JavaScript

    Le gestionnaire de clic pour l’icône « thumbs-up » coordonne le changement de contenu, la classe d’animation et l’état final :

    thumbsUp.addEventListener("click", function () {
        if (thumbsUp.classList.contains("active")) {
            return;
        }
        thumbsUp.innerHTML = "✨😊✨";
        thumbsUp.classList.add("sparkle");
        setTimeout(function () {
            thumbsUp.innerHTML = "👍";
            thumbsUp.classList.remove("sparkle");
            thumbsUp.classList.add("active");
        }, 900);
    });
    

    Elle commence par une vérification : si le bouton possède déjà la classe active, le gestionnaire renvoie, ce qui empêche la répétition de l’animation en cliquant à plusieurs reprises. Ensuite, elle remplace le contenu par le smiley :

    thumbsUp.innerHTML = "✨😊✨";
    

    et ajoute la classe qui déclenche les keyframes :

    thumbsUp.classList.add("sparkle");
    

    Un setTimeout de 900 millisecondes, légèrement supérieur aux 0,8 seconde de l’animation, permet ensuite de restaurer le symbole du pouce levé, d’enlever la classe sparkle et d’ajouter active :

    thumbsUp.innerHTML = "👍";
    

    Le contenu n’est jamais une entrée de l’utilisateur, donc innerHTML est sécurisé ici, bien que textContent exprime mieux l’intention.

    Mise en évidence de l’option sélectionnée

    La classe active assure la confirmation visuelle. Un retour positif se caractérise par un arrière-plan vert pâle et une bordure verte :

    .thumbs-up.active {
        background: #e8f5e9;
        border-color: #4caf50;
    }
    

    Le feedback négatif utilise le même motif en rouge :

    .thumbs-down.active {
        background: #ffebee;
        border-color: #f44336;
    }
    

    Des couleurs distinctes permettent de voir d’un coup d’œil quel choix a été enregistré, mais il ne faut pas s’appuyer uniquement sur la couleur ; aria-pressed aide les utilisateurs qui ne parviennent pas à les distinguer.

    Garantir l’exclusivité des choix

    L’icône de doigt baissé utilise une interaction plus simple sans animation. Elle active sa propre classe active, efface l’état positif et, si elle est désormais sélectionnée, ramène l’icône de doigt levé à sa forme initiale :

    thumbsDown.addEventListener("click", function () {
        thumbsDown.classList.toggle("active");
        thumbsUp.classList.remove("active");
        if (thumbsDown.classList.contains("active")) {
            thumbsUp.innerHTML = "👍🏻";
        }
    });
    

    Le nettoyage de l’autre bouton empêche que les deux options ne paraissent sélectionnées en même temps. Un cas particulier persiste : si un utilisateur clique sur « thumbs-up » puis sur « thumbs-down » dans l’intervalle de 900 ms, le délai en attente se déclenche toujours et réactive « thumbs-up ». Il faut stocker l’identifiant du timer et appeler clearTimeout dans le gestionnaire de « thumbs-down » pour combler cette lacune. Notez également qu’un « thumbs-up » actif ne peut pas être annulé en le cliquant, tandis que « thumbs-down » permet de basculer ; il convient de rendre les deux fonctionnalités cohérentes.

    Pourquoi une petite animation vaut la peine

    Un changement de couleur statique indique qu’un événement s’est produit ; une courte animation (clic, mouvement, confirmation) donne l’impression que l’interface a réagi. Cela est particulièrement important pour les retours en temps réel, où rien d’autre sur la page ne signalise le succès. Cela convient aux formulaires de retour d’information sur les cours, aux évaluations des FAQ et des bases de connaissances, aux outils d’assistance, aux avis produits, aux enquêtes, aux sites de documentation, aux réponses des chatbots d’IA et aux outils internes.

    Respectez la directive prefers-reduced-motion en désactivant les keyframes pour les utilisateurs qui souhaitent moins d’animations.

    Envoi de l’évaluation vers un backend

    Au stade actuel, le widget est purement visuel. Dans un produit réel, il faut stocker ce choix, et le flux d’opérations doit inclure une demande :

    User clicks 👍
           ↓
    Animation plays
           ↓
    Feedback recorded
           ↓
    Database updated
           ↓
    User sees confirmation
    

    N’importe quel backend fonctionne, que ce soit PHP, Node.js, Python ou un autre langage. Depuis le gestionnaire de clics, vous envoyez l’évaluation vers une adresse URL spécifique :

    fetch("/api/feedback", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            feedback: "positive"
        })
    });
    

    Le serveur peut stocker la page, l’utilisateur, la réponse, l’heure de création et le type de retour d’information. Vérifiez response.ok, car fetch ne rejette la requête que en cas d’erreur réseau, et décidez si l’animation doit être exécutée de manière optimiste (plus rapidement, mais nécessitant un retrait en cas d’échec du sauvegarde) ou uniquement après succès.

    La page complète

    Tout est ici dans un seul fichier, y compris la classe .sparkle, une échelle :active pour l’état enfoncé, ainsi que le gestionnaire de validation positive qui efface la sélection négative :

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Feedback Buttons</title>
    
    <style>
        .feedback {
            display: flex;
            gap: 12px;
            align-items: center;
        }
    
        .feedback-btn {
            width: 52px;
            height: 52px;
            border: 1px solid #ddd;
            background: #fff;
            border-radius: 50%;
            cursor: pointer;
            font-size: 25px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.25s ease;
        }
    
        .feedback-btn:hover {
            background: #f5f5f5;
            transform: scale(1.08);
        }
    
        .feedback-btn:active {
            transform: scale(0.92);
        }
    
        /* Sparkle animation */
        .sparkle {
            animation: sparkle 0.8s ease forwards;
        }
    
        @keyframes sparkle {
            0% {
                transform: scale(0.4) rotate(-20deg);
                opacity: 0;
            }
    
            35% {
                transform: scale(1.4) rotate(10deg);
                opacity: 1;
            }
    
            65% {
                transform: scale(1) rotate(-5deg);
            }
    
            100% {
                transform: scale(1) rotate(0deg);
            }
        }
    
        /* Filled thumbs up */
        .thumbs-up.active {
            background: #e8f5e9;
            border-color: #4caf50;
        }
    
        .thumbs-down.active {
            background: #ffebee;
            border-color: #f44336;
        }
    </style>
    </head>
    
    <body>
    
    <div class="feedback">
    
        <button
            id="thumbsUp"
            class="feedback-btn thumbs-up"
            aria-label="Thumbs up">
            👍🏻
        </button>
    
        <button
            id="thumbsDown"
            class="feedback-btn thumbs-down"
            aria-label="Thumbs down">
            👎🏻
        </button>
    
    </div>
    
    <script>
    
    const thumbsUp = document.getElementById("thumbsUp");
    const thumbsDown = document.getElementById("thumbsDown");
    
    thumbsUp.addEventListener("click", function () {
    
        // Prevent repeated animation
        if (thumbsUp.classList.contains("active")) {
            return;
        }
    
        // Step 1: Sparkling smiley
        thumbsUp.innerHTML = "✨😊✨";
        thumbsUp.classList.add("sparkle");
    
        // Step 2: Change to filled thumbs up
        setTimeout(function () {
    
            thumbsUp.innerHTML = "👍";
            thumbsUp.classList.remove("sparkle");
            thumbsUp.classList.add("active");
    
        }, 900);
    
        // Remove thumbs down selection
        thumbsDown.classList.remove("active");
    });
    
    thumbsDown.addEventListener("click", function () {
    
        thumbsDown.classList.toggle("active");
    
        // Remove thumbs up selection
        thumbsUp.classList.remove("active");
    
        if (thumbsDown.classList.contains("active")) {
            thumbsUp.innerHTML = "👍🏻";
        }
    });
    
    </script>
    
    </body>
    </html>
    

    En résumé

    Un système de retour d’information bien conçu ne nécessite que du HTML, du CSS et quelques lignes de JavaScript. Les keyframes assurent l’effet de pop, des séquences de changement de classe planifiées gèrent les états, et l’icône mise en évidence finale confirme le choix.

    • Gardez la logique d’animation dans le CSS et laissez JavaScript uniquement activer/désactiver les classes et le contenu.
  • Surveillez le délai de timeout afin que des clics trop rapides sur l’autre bouton ne puissent pas maintenir les deux options actives.
  • Ajoutez l’attribut aria-pressed ainsi qu’un mécanisme de fallback pour réduire les mouvements, afin que le widget reste accessible.
  • Considérez l’appel au backend comme faisant partie de l’interaction : gérez les échecs et choisissez entre un retour d’information optimiste et un retour confirmé.
  • La même approche s’applique aux icônes SVG, aux animations Lottie, aux indications sonores ainsi qu’à des workflows plus avancés basés sur des API.