Pricing Toggle Odometer
Bascule mensuel / annuel pour une carte de prix : chaque chiffre roule dans sa colonne comme un compteur, le badge d'économie et le montant annuel apparaissent.
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



Comment ça marche
Chaque carte [data-pto] placée dans .fx-0808-root porte ses deux prix entiers : data-monthly="19" et data-yearly="15". Au chargement, le script efface le prix écrit en clair dans [data-pto-digits] (celui qu'on lit sans JavaScript) et le remplace par une colonne par chiffre, chacune contenant les chiffres 0 à 9 empilés. Afficher « 15 », c'est décaler la première colonne de 1 em vers le haut et la seconde de 5 em : la transition transform .65s cubic-bezier(.2,.85,.25,1) fait rouler les chiffres.
La bascule est une vraie case à cocher, invisible, posée dans un <label> avec le rail .pto-track. À l'événement change, le script choisit le prix annuel ou mensuel, met « Mensuel » ou « Annuel » en gras selon l'état, montre le badge .pto-save et la ligne .pto-sub (« Facturé 180 € / an », soit le prix annuel × 12, calculé au chargement). Le badge vaut data-save s'il est fourni, sinon l'économie réelle arrondie : 19 → 15 donne −21 %.
Chaque carte a sa propre bascule et ses propres colonnes : trois cartes côte à côte ou deux copies du bloc fonctionnent sans se gêner, et coller le script deux fois ne reconstruit pas les colonnes. Quand un prix compte moins de chiffres que l'autre (9 contre 12), le zéro de tête est masqué. Dans un <form>, un reset décoche la case et remet aussitôt le prix mensuel. Prix entiers uniquement : parseInt ignore les centimes.
Accessibilité
- Prix lu en clair : les colonnes 0-9 sont masquées aux lecteurs d'écran (
aria-hidden) ; une phrase invisible « 19 € par mois », mise à jour à chaque bascule, est annoncée grâce àaria-live="polite". - Clavier : la case reste atteignable par Tab et se coche avec Espace ; un anneau clair se dessine autour du rail au focus clavier (
:focus-visible). Son nom accessible est « Facturation annuelle ». - Mouvement réduit : sous
prefers-reduced-motion: reduce, les chiffres sont posés directement à leur valeur, sans défilement, et le rail comme le badge changent sans transition. Le prix change quand même. - Sans JavaScript : le prix mensuel écrit dans le HTML reste affiché ; la bascule se coche mais ne change rien.
Compatibilité navigateur
JavaScript sans dépendance (padStart, matchMedia, classList) et CSS courant. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 (Playwright) : deux copies dans la même page, clic sur la seconde sans effet sur la première, Tab puis Espace, toucher à 390 px, mouvement réduit, 0 erreur. L'anneau de focus utilise :focus-visible (Chrome 86, Firefox 85, Safari 15.4) ; avant, la bascule marche au clavier sans anneau visible.
Sans JavaScript, le prix mensuel du HTML reste lisible. Sans :focus-visible (Safari avant 15.4), pas d'anneau au focus clavier mais la bascule reste utilisable.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec votre achat) :
<div class="fx-0808-root">
<div class="pto-scene">
<div class="pto-card" data-pto data-monthly="19" data-yearly="15">
<label class="pto-switch">
<span class="pto-m pto-on">Mensuel</span>
<input type="checkbox" data-pto-toggle aria-label="Facturation annuelle">
<span class="pto-track" aria-hidden="true"></span>
<span class="pto-y">Annuel</span>
</label>
<div class="pto-price">
<span class="pto-amount" aria-hidden="true"><span data-pto-digits>19</span><span class="pto-cur">€</span></span>
<span class="pto-suffix" aria-hidden="true">/ mois</span>
<span class="pto-save" data-pto-save>−<span data-pto-pct>21</span> %</span>
<span class="pto-vh" aria-live="polite" aria-atomic="true"><span data-pto-sr>19</span> € par mois</span>
</div>
<div class="pto-sub" data-pto-sub>Facturé <span data-pto-total>180</span> € / an</div>
<button type="button" class="pto-cta">Commencer</button>
</div>
</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 |
|---|---|---|
Prix (HTML — data-monthly et data-yearly sur la carte) |
19 et 15 | Nombres entiers. Écrivez aussi le prix mensuel dans [data-pto-digits] : c'est ce qui s'affiche sans JavaScript. |
Économie du badge (HTML — data-save sur la carte) |
absent : calculée (−21 % pour 19 → 15) | Donnez un nombre pour imposer le pourcentage affiché, par exemple data-save="20". |
Largeur de la carte (CSS — --pto-w) |
280px | auto laisse la carte prendre la largeur de sa colonne (grille de formules). |
Taille du prix (CSS — font-size de .pto-amount) |
52px | Les colonnes de chiffres sont en em : elles suivent la taille, de 30 px dans un bandeau à 120 px dans un visuel d'accueil. |
Vitesse du roulement (CSS — transition de .pto-strip) |
.65s cubic-bezier(.2,.85,.25,1) | Raccourcissez à .4s pour un compteur plus sec. |
Couleur de la bascule (CSS — .pto-switch input:checked+.pto-track) |
#6366f1 | Le rail coché prend cette couleur ; l'anneau de focus utilise #a5b4fc. |
| Textes et devise (HTML) | Mensuel, Annuel, €, / mois, Facturé … € / an | Tout le texte est dans le HTML : placez .pto-cur avant ou après [data-pto-digits], traduisez la phrase lue par les lecteurs d'écran (.pto-vh) et le nom de la case (aria-label). |
FAQ
.pto-amount ; seule la partie entière roulera.[data-pto] écoute sa propre case. Pour une bascule commune, cachez celles des cartes et, au changement de votre bascule, cochez chaque case puis envoyez-lui dispatchEvent(new Event('change')).prefers-reduced-motion) : le script pose alors les chiffres directement. Le prix, le badge et la ligne annuelle changent exactement de la même façon.