Formulaires✨ Premium

Submit Button States

Bouton d'envoi qui montre la vraie réponse de votre serveur : roue pendant l'attente, coche verte si l'envoi réussit, croix rouge s'il échoue.

JSAnimationStates

Mis à jour le

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

3 exemples d'usage

Proposer un trajet de covoiturage — formulaire envoyé avec la touche Entrée — Submit Button States exemple 1

① Proposer un trajet de covoiturage — formulaire envoyé avec la touche Entrée

QuandLe conducteur a rempli départ, arrivée, heure et places, puis appuie sur Entrée dans le champ Arrivée. Le navigateur vérifie d'abord les champs obligatoires ; la roue tourne ensuite le temps que le serveur enregistre l'annonce, la coche verte s'affiche et « Trajet en ligne » apparaît sous le bouton.
PourquoiC'est l'usage le plus direct de l'effet : un vrai formulaire, un bouton submit, la validation native avant l'envoi. Pas besoin de viser le bouton, Entrée suffit, et le bouton montre quand même que la publication a abouti.
RéglagesBouton type="submit" dans un vrai <form> à champs required, dégradé vert, data-sbs-reset="0" pour garder la coche. onSend renvoie une promesse résolue après 0,65 s, qui remplace la vraie requête dans la démo ; la phrase de confirmation est affichée par la scène dans le then. Dans la capture, le clic hors du bouton est rejoué comme la touche Entrée (form.requestSubmit()).
« Payer » refusé puis réessayé dans un récapitulatif de paiement — Submit Button States exemple 2

② « Payer » refusé puis réessayé dans un récapitulatif de paiement

QuandLe client clique « Payer 48,00 € » : la banque refuse, le bouton passe au rouge avec une croix et un message explique quoi faire. Il réessaie : cette fois la coche verte confirme et le numéro de commande apparaît.
PourquoiLe même bouton raconte l'échec puis la réussite : le client sait tout de suite si son paiement est passé, et le rouge ne bloque pas le second essai.
RéglagesBouton pleine largeur dans un vrai <form> (type="submit"), dégradé bleu, data-sbs-reset="1600". La scène simule la banque : la promesse est rejetée au 1er envoi, résolue au 2e. Le message sous le bouton est géré par la scène dans les then de la promesse.
« Synchroniser » d'un tableau de bord qui se met à jour tout seul — Submit Button States exemple 3

③ « Synchroniser » d'un tableau de bord qui se met à jour tout seul

QuandSur un tableau de bord de ventes, le petit bouton en haut à droite se lance de lui-même à intervalle régulier : roue pendant la récupération, coche verte quand les chiffres sont à jour, puis retour à « Synchroniser ».
PourquoiLe bouton sert d'indicateur d'état : on voit qu'une mise à jour est en cours et qu'elle a réussi, sans voyant supplémentaire. Le visiteur peut aussi cliquer pour forcer une synchro.
RéglagesBouton en pastille (112 px de large, 12 px, icônes 16 px), data-sbs-reset="700". La scène appelle root.fx0290.run(synchro) toutes les 2,2 s ; synchro attend 0,8 s puis met à jour le chiffre d'affaires.

Comment ça marche

Le bouton ne simule rien : il attend que vous lui branchiez votre envoi. Chaque bloc .fx-0290-root reçoit au chargement un objet root.fx0290. Vous lui donnez une fonction qui renvoie une promesse, par exemple root.fx0290.onSend(() => fetch('/api/contact', { method: 'POST', body: new FormData(form) }).then(r => { if (!r.ok) throw r; })). Au clic, le bouton passe en chargement (classe sbs-loading : texte masqué, roue qui tourne) ; promesse résolue → sbs-success (vert, coche) ; promesse rejetée → sbs-error (rouge, croix).

Placé dans un <form> avec type="submit", le bouton laisse d'abord le navigateur vérifier les champs (required, type="email"…) : votre fonction n'est appelée qu'à l'événement submit, qui est alors annulé pour que la page ne se recharge pas. Hors formulaire, mettez type="button" : c'est le clic qui déclenche. Sans fonction branchée, le bouton garde son comportement normal (un submit envoie le formulaire).

Après un succès ou un échec, le bouton revient au repos au bout de data-sbs-reset millisecondes (2000 par défaut, 0 pour garder l'état). Un nouveau clic après une erreur relance l'envoi ; un clic pendant le chargement est ignoré, pas de double envoi. Vous pouvez aussi piloter l'état vous-même : root.fx0290.run(promesse) lance un chargement depuis votre code, root.fx0290.setState('loading' | 'success' | 'error' | 'default') force un état.

Accessibilité

  • Focus conservé : pendant le chargement, le bouton reçoit aria-disabled="true" au lieu de disabled. Il garde donc le focus clavier (un bouton désactivé renvoie le focus en haut de la page) ; les clics répétés sont ignorés par le script.
  • État annoncé : une zone invisible role="status" lit le texte de data-sbs-loading, data-sbs-success ou data-sbs-error (« Envoi en cours… », « Envoyé », « Échec de l'envoi, réessayez »). Le nom du bouton reste « Envoyer » même quand le texte est masqué à l'écran.
  • Clavier : vrai <button>, Entrée et Espace marchent ; anneau clair au focus clavier (:focus-visible).
  • Couleur et forme : le succès et l'erreur ne reposent pas que sur le vert et le rouge : coche ou croix, plus le message annoncé. Affichez aussi un message visible sous le bouton pour dire quoi faire en cas d'échec.
  • Mouvement réduit : plus de soulèvement au survol ni de fondu ; la roue, seul indicateur visuel d'attente, tourne 2,4 fois plus lentement.

Compatibilité navigateur

JavaScript sans dépendance (Promise, closest, classList) et SVG en ligne. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 (Playwright) : deux copies dont une dans un formulaire avec champ obligatoire, succès, échec puis nouvel essai au toucher, Entrée au clavier avec le focus resté sur le bouton, mouvement réduit, aucun nom ajouté sur window, 0 erreur.

Chrome 86+✓ Complet (mesuré)
Firefox 85+✓ Complet (mesuré)
Safari 15.4+✓ Complet (mesuré dans WebKit)
Edge 86+✓ Complet (moteur Chromium)
Mobile iOS 15.4+✓ Moteur WebKit, non vérifié sur appareil
Android Chrome 86+✓ Complet (toucher mesuré)

Sans JavaScript, c'est un bouton normal : dans un formulaire, il envoie le formulaire. L'anneau de focus demande :focus-visible (Chrome 86, Firefox 85, Safari 15.4).

Le code

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

index.html — structure
<div class="fx-0290-root" data-sbs-loading="Envoi en cours…" data-sbs-success="Envoyé" data-sbs-error="Échec de l'envoi, réessayez" data-sbs-reset="2000">
  <button type="submit" class="sbs-btn">
    <span class="sbs-text">Envoyer</span>
    <span class="sbs-loader" aria-hidden="true"><svg>…</svg></span>
    <span class="sbs-icon sbs-ok" aria-hidden="true"><svg>…</svg></span>
    <span class="sbs-icon sbs-ko" aria-hidden="true"><svg>…</svg></span>
  </button>
  <span class="sbs-status" role="status" aria-live="polite"></span>
</div>

Paiement unique, sans abonnement — le code est à toi pour toujours

Personnaliser

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

Option / propriétéDéfautEffet
Envoi branché (JS — root.fx0290.onSend(fn)) aucun : le bouton garde son comportement normal fn renvoie une promesse : résolue → succès, rejetée (ou throw) → erreur. Avec fetch, rejetez quand response.ok est faux.
Lancer depuis votre code (JS — root.fx0290.run(fn ou promesse)) — Passe en chargement puis en succès ou en erreur selon la promesse, et la renvoie pour enchaîner (.then).
Forcer un état (JS — root.fx0290.setState(état)) 'default' 'loading', 'success', 'error' ou 'default', pour un envoi géré ailleurs (WebSocket, bibliothèque de formulaires).
Retour au repos (HTML — data-sbs-reset sur la racine) 2000 ms 0 garde la coche ou la croix jusqu'au prochain clic.
Messages annoncés (HTML — data-sbs-loading, data-sbs-success, data-sbs-error) Envoi en cours… · Envoyé · Échec de l'envoi, réessayez Textes lus par les lecteurs d'écran ; adaptez-les à l'action (« Paiement refusé »).
Couleurs (CSS — .sbs-btn, .sbs-success, .sbs-error) dégradé #6366f1 → #8b5cf6 · #10b981 · #ef4444 Changez le background de chaque état ; gardez un contraste suffisant pour l'icône blanche.
Type du bouton (HTML) type="submit" Gardez submit dans un formulaire (validation native d'abord) ; type="button" pour une action hors formulaire.

FAQ

Seulement si votre fonction ne rejette pas : fetch ne rejette sa promesse qu'en cas de panne réseau. Ajoutez .then(r => { if (!r.ok) throw r; }) pour que les réponses 4xx et 5xx passent le bouton en rouge.
Oui, si le bouton est dans le formulaire avec type="submit" : le navigateur bloque l'envoi tant qu'un champ required est vide, et votre fonction n'est pas appelée. Le bouton reste au repos.
Utilisez root.fx0290.run(fetch(…)) : il renvoie la même promesse, avec la valeur résolue. Avec onSend, traitez la réponse dans votre fonction avant de la renvoyer.