Ressources pour développeur web

Théme de la semaine : WebSécurité

Animation CSS d’un champ input radio : Tuto + code

Temps de lecture estimé : 14 minutes
Accueil CSS3 Animation CSS d’un champ input radio : Tuto + code

Un champ input radio remplit parfaitement son rôle dans un formulaire, mais son apparence par défaut manque souvent de personnalité. Heureusement, quelques lignes de CSS suffisent pour transformer un simple bouton radio en un élément moderne, élégant et beaucoup plus agréable à utiliser.

Dans ce tutoriel, vous allez apprendre à créer une animation CSS input radio fluide et professionnelle, étape par étape. Nous verrons comment personnaliser l’apparence d’un champ input type radio, ajouter des effets visuels dynamiques et concevoir des animations qui améliorent réellement l’expérience utilisateur, même si vous débutez en HTML, CSS ou JavaScript.

  • Donner un aspect moderne et professionnel à vos boutons radio grâce à des animations fluides qui améliorent immédiatement l’expérience utilisateur.
  • Comprendre comment personnaliser un composant de formulaire natif sans sacrifier l’accessibilité ni le fonctionnement standard du navigateur.
  • Maîtriser des effets visuels élégants, du simple rebond CSS à l’indicateur animé, pour rendre vos input radio plus vivants et interactifs.

Un champ input type radio semble souvent banal : un petit rond, un clic, une option choisie… et voilà. Avec un peu de CSS, nous allons le rendre beaucoup plus agréable à utiliser.

Nous allons partir d’un vrai champ input type radio, conserver une structure HTML accessible, puis personnaliser son apparence avec CSS. Ensuite, nous verrons deux approches : une animation simple basée sur les pseudo-éléments CSS, puis une animation plus avancée avec un indicateur qui se déplace d’un choix à l’autre grâce à un peu de JavaScript.

  • Avec une animation simple :
  • Avec un déplacement du bouton :

Code complet en fin de ce tutoriel.

Comprendre le rôle d’un champ input type radio

Avant d’animer quoi que ce soit, prenons une minute pour comprendre ce que nous manipulons.

Un champ input type radio permet à l’utilisateur de choisir une seule option parmi plusieurs. C’est très courant dans les formulaires : choix d’un abonnement, d’un mode de livraison, d’un niveau de difficulté, d’une formation, d’un thème, etc.

La particularité importante vient de l’attribut name. Tous les boutons radio qui partagent le même name appartiennent au même groupe. Résultat : lorsqu’un utilisateur en coche un, les autres se décochent automatiquement.

<label>
    <input type="radio" name="formation" checked>
    HTML & CSS
</label>

<label>
    <input type="radio" name="formation">
    JavaScript
</label>

<label>
    <input type="radio" name="formation">
    PHP & MySQL
</label>

Ici, les trois champs radio ont le même name="formation". Le navigateur comprend donc qu’il faut choisir une seule formation. Le mot-clé checked indique simplement l’option sélectionnée par défaut.

input radio

C’est une base très saine. Et c’est justement ce que l’on veut garder : un vrai champ de formulaire, pas une fausse interface bricolée avec des div.

Préparer une structure HTML propre

Pour créer une animation CSS d’un champ input radio, le HTML doit rester simple. Chaque option peut être placée dans un élément label. C’est pratique, car l’utilisateur peut cliquer sur le texte autant que sur le rond.

<div class="radio-group">

    <label class="radio">
        <input type="radio" name="formation" checked>
        <span>HTML & CSS</span>
    </label>

    <label class="radio">
        <input type="radio" name="formation">
        <span>JavaScript</span>
    </label>

    <label class="radio">
        <input type="radio" name="formation">
        <span>PHP & MySQL</span>
    </label>

</div>
  • La classe .radio-group sert à regrouper les options.
  • La classe .radio permet de mettre en forme chaque ligne.
  • Le texte est placé dans un span afin de pouvoir le styliser facilement lorsque l’input radio est coché.

Ce petit détail va nous permettre, par exemple, de mettre le texte en gras quand l’option est active.

Supprimer l’apparence par défaut du navigateur

Par défaut, les champs radio ont une apparence imposée par le navigateur et le système d’exploitation. C’est pratique, mais cela limite beaucoup la personnalisation.

Pour créer une animation input radio sur mesure, on utilise la propriété CSS appearance.

.radio input{
    appearance:none;
    -webkit-appearance:none;
}

Cette propriété demande au navigateur d’enlever le style natif du champ. En clair, on dit au navigateur :

« Merci pour le petit rond par défaut, mais je vais le dessiner moi-même. »

Le préfixe -webkit-appearance améliore la compatibilité avec certains navigateurs, notamment Safari.

Attention toutefois : même si l’apparence visuelle change, l’élément reste un vrai input type="radio". C’est important pour l’accessibilité, les formulaires et le comportement naturel du navigateur.

👉 Pour aller lus loin : Normalize.css VS Reset.css

Dessiner le bouton input radio

Maintenant que l’apparence par défaut est supprimée, nous devons redessiner notre bouton radio.

.radio input{
    appearance:none;
    -webkit-appearance:none;
    width:24px;
    height:24px;
    margin:0;
    border:2px solid #cbd5e1;
    border-radius:50%;
    cursor:pointer;
}

La largeur et la hauteur créent un carré de 24 pixels. Grâce à border-radius:50%, ce carré devient un cercle. La bordure grise représente l’état normal du champ input type radio.

Le cursor:pointer indique visuellement que l’élément est cliquable. C’est un petit détail, mais il rend l’interface plus intuitive.

On peut ensuite ajouter une transition pour rendre le changement de couleur plus doux.

.radio input{
    transition:
        border-color .25s ease,
        transform .2s ease;
}

Avec cette transition, la bordure ne change pas brutalement. Elle glisse doucement vers sa nouvelle couleur. Ce n’est pas spectaculaire, mais c’est précisément ce qui rend une interface agréable.

Ajouter un effet au survol et au clic

Une animation réussie ne concerne pas seulement l’état coché. Elle peut aussi réagir au survol et au clic.

.radio input:hover{
    border-color:#94a3b8;
}

.radio input:active{
    transform:scale(.92);
}

Lorsque l’utilisateur passe la souris sur l’input radio, la bordure devient un peu plus foncée. Cela donne un retour visuel immédiat.

Avec :active, le bouton se réduit légèrement au moment du clic. C’est un effet très court, mais il donne l’impression que l’élément répond physiquement. Comme un petit bouton que l’on appuie vraiment.

Créer la pastille intérieure avec ::before

Un bouton radio coché contient généralement une petite pastille au centre. Au lieu d’ajouter un élément HTML supplémentaire, nous pouvons la créer avec le pseudo-élément ::before.

.radio input{
    display:grid;
    place-items:center;
}

.radio input::before{
    content:"";
    width:12px;
    height:12px;
    border-radius:50%;
    background:#111827;
    transform:scale(0);
    transition:
        transform .45s cubic-bezier(.34,1.56,.64,1);
}

Le pseudo-élément ::before crée une forme invisible dans l’input. Elle existe, mais elle est réduite à zéro grâce à transform:scale(0).

La propriété display:grid associée à place-items:center permet de centrer parfaitement cette pastille dans le cercle extérieur. C’est plus propre que de jouer avec des marges au hasard.

La transition utilise une courbe cubic-bezier. Pas de panique : c’est simplement une manière de contrôler le rythme de l’animation. Ici, l’effet donne une légère sensation de rebond.

Afficher la pastille quand l’option est cochée

Pour afficher la pastille intérieure, on utilise la pseudo-classe :checked.

.radio input:checked{
    border-color:#111827;
}

.radio input:checked::before{
    transform:scale(1);
}

Quand le champ input type radio est coché, la bordure devient foncée. En même temps, la pastille passe de scale(0) à scale(1). Elle apparaît donc progressivement, avec un petit effet dynamique.

C’est le cœur de notre animation input radio : l’état du formulaire déclenche directement une animation CSS.

Mettre le texte en valeur quand l’option est active

L’utilisateur doit comprendre rapidement quelle option est sélectionnée. La pastille aide déjà beaucoup, mais on peut renforcer l’information en modifiant légèrement le texte.

.radio span{
    transition:.25s ease;
}

.radio input:checked + span{
    font-weight:600;
}

Le sélecteur input:checked + span signifie :

« sélectionne le span qui se trouve juste après un input coché ».

Dans notre HTML, le span est bien placé juste après l’input. Lorsque l’option est active, le texte devient donc plus épais.

C’est discret, lisible, et cela améliore l’expérience utilisateur sans en faire trop.

Utiliser des variables CSS pour un code plus propre

Pour rendre le code plus facile à modifier, vous pouvez utiliser des variables CSS.

:root{
    --radio-size:24px;
    --radio-border:#cbd5e1;
    --radio-active:#111827;
    --radio-hover:#94a3b8;
    --duration:.45s;
}

Ensuite, on remplace les valeurs répétées par ces variables.

.radio input{
    width:var(--radio-size);
    height:var(--radio-size);
    border:2px solid var(--radio-border);
}

.radio input:hover{
    border-color:var(--radio-hover);
}

.radio input:checked{
    border-color:var(--radio-active);
}

.radio input::before{
    background:var(--radio-active);
    transition:transform var(--duration) cubic-bezier(.34,1.56,.64,1);
}

L’avantage est simple : si vous voulez changer la couleur active, vous le faites à un seul endroit. C’est beaucoup plus confortable, surtout quand le projet grandit.

Formation web et informatique - Alban Guillier - Formateur

Des formations informatique pour tous !

Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…

Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.

Découvrez mes formations Qui suis-je ?

Code complet N°1 de l’animation CSS simple du bouton input radio

Voici une première version complète, idéale pour débuter.

<div class="radio-group">

    <label class="radio">
        <input type="radio" name="formation" checked>
        <span>HTML & CSS</span>
    </label>

    <label class="radio">
        <input type="radio" name="formation">
        <span>JavaScript</span>
    </label>

    <label class="radio">
        <input type="radio" name="formation">
        <span>PHP & MySQL</span>
    </label>

</div>
:root{
    --radio-size: 24px;
    --radio-border: #cbd5e1;
    --radio-active: #111827;
    --radio-hover: #94a3b8;
    --duration: 0.45s;
}

.radio-group{
    display:flex;
    flex-direction:column;
    gap:18px;
}

.radio{
    display:flex;
    align-items:center;
    gap:12px;
    cursor:pointer;
    user-select:none;
    font-size:16px;
    color:#1e293b;
}

.radio input{
    appearance:none;
    -webkit-appearance:none;
    width:var(--radio-size);
    height:var(--radio-size);
    margin:0;
    border:2px solid var(--radio-border);
    border-radius:50%;
    display:grid;
    place-items:center;
    cursor:pointer;
    position:relative;
    transition:
        border-color .25s ease,
        transform .2s ease;
}

.radio input:hover{
    border-color:var(--radio-hover);
}

.radio input:active{
    transform:scale(.92);
}

.radio input::before{
    content:"";
    width:12px;
    height:12px;
    border-radius:50%;
    background:var(--radio-active);

    transform:scale(0);
    transition:
        transform var(--duration)
        cubic-bezier(.34,1.56,.64,1);

    view-transition-name:radio-dot;
}

.radio input::after{
    content:"";
    position:absolute;
    inset:-8px;
    border-radius:50%;
    background:rgba(0,0,0,.08);
    opacity:0;
    transform:scale(.5);
    transition:.3s;
}

.radio input:checked{
    border-color:var(--radio-active);
}

.radio input:checked::before{
    transform:scale(1);
}

.radio input:checked::after{
    opacity:1;
    transform:scale(1);
}

.radio span{
    transition:.25s ease;
}

.radio input:checked + span{
    font-weight:600;
}

::view-transition-group(radio-dot){
    animation-duration:450ms;
    animation-timing-function:cubic-bezier(.22,1,.36,1);
}

Cette version fonctionne sans JavaScript. Elle est parfaite si vous souhaitez une animation CSS simple, légère et facile à intégrer dans un formulaire existant.

Contrôler le changement d’état

L’ajout de ce JavaScript sélectionne tous les boutons radio présents sur la page et leur associe un événement de clic.

Lorsqu’un utilisateur clique sur un bouton radio, le comportement par défaut du navigateur est temporairement bloqué afin de contrôler manuellement le changement d’état.

Une fonction est alors définie pour cocher le bouton sélectionné.

  • Si le navigateur prend en charge l’API View Transitions, cette fonction est exécutée à l’intérieur d’une transition visuelle, ce qui permet d’animer le passage d’un choix à un autre de manière fluide.
  • Dans le cas contraire, le bouton radio est simplement coché normalement.

L’objectif principal de ce code est donc d’ajouter une animation de transition lors du changement de sélection d’un bouton radio tout en conservant un fonctionnement compatible avec les navigateurs ne prenant pas en charge cette technologie.

document.querySelectorAll('input[type="radio"]').forEach(radio => {

    radio.addEventListener('click', e => {

        if (radio.checked) {
            return;
        }

        e.preventDefault();

        const updateRadio = () => {
            radio.checked = true;
        };

        if (document.startViewTransition) {
            document.startViewTransition(updateRadio);
        } else {
            updateRadio();
        }
    });

});

Code complet N°2 de l’animation CSS avec déplacement du bouton input radio

Passons maintenant à une version plus originale. Au lieu d’avoir une pastille indépendante dans chaque input radio, nous allons créer une seule pastille qui se déplace d’une option à l’autre.

Visuellement, cela donne une impression plus fluide : l’indicateur ne disparaît pas, il voyage.

Pour cela, on ajoute un élément .indicator dans le groupe.

<div class="radio-group">

    <div class="indicator"></div>

    <label class="radio">
        <input type="radio" name="langage" checked>
        <span>HTML & CSS</span>
    </label>

    <label class="radio">
        <input type="radio" name="langage">
        <span>JavaScript</span>
    </label>

    <label class="radio">
        <input type="radio" name="langage">
        <span>PHP</span>
    </label>

    <label class="radio">
        <input type="radio" name="langage">
        <span>SEO</span>
    </label>

</div>

Ici, .indicator représente la pastille noire. Elle sera positionnée par-dessus les champs radio.

.radio-group{
    position:relative;
    display:flex;
    flex-direction:column;
    gap:20px;
}

.radio{
    display:flex;
    align-items:center;
    gap:12px;
    cursor:pointer;
}

.radio input{
    appearance:none;
    width:24px;
    height:24px;
    border:2px solid #cbd5e1;
    border-radius:50%;
    margin:0;
    cursor:pointer;
}

.radio input:checked{
    border-color:#111827;
}

.indicator{
    position:absolute;

    width:12px;
    height:12px;

    background:#111827;
    border-radius:50%;

    left:6px;

    pointer-events:none;

    transition:
        top .45s cubic-bezier(.22,1,.36,1),
        transform .35s cubic-bezier(.34,1.56,.64,1);

    z-index:2;
}

Enfin, on ajoute ce script qui déplace et anime automatiquement un indicateur visuel afin qu’il suive le bouton radio actuellement sélectionné lors du chargement de la page et à chaque changement de choix :

const indicator = document.querySelector('.indicator');
const radios = document.querySelectorAll('input[type="radio"]');
const group = document.querySelector('.radio-group');

function moveIndicator(target){

    const groupRect = group.getBoundingClientRect();
    const rect = target.getBoundingClientRect();

    indicator.style.top =
        (rect.top - groupRect.top + 6) + 'px';

    indicator.style.transform = 'scale(1.25)';

    setTimeout(() => {
        indicator.style.transform = 'scale(1)';
    }, 180);
}

const checked = document.querySelector('input:checked');
moveIndicator(checked);

radios.forEach(radio => {

    radio.addEventListener('change', () => {

        moveIndicator(radio);

    });

});

Passons à quelques explications …

Positionner l’indicateur dans le groupe

Pour que l’indicateur puisse se déplacer correctement, le parent doit être en position:relative.

.radio-group{
    position:relative;
    display:flex;
    flex-direction:column;
    gap:20px;
}

.indicator{
    position:absolute;
    width:12px;
    height:12px;
    background:#111827;
    border-radius:50%;
    left:6px;
    pointer-events:none;
    z-index:2;
}

L’indicateur est en position:absolute, ce qui permet de le placer précisément dans le groupe. La propriété left:6px sert à le centrer horizontalement dans un bouton de 24 pixels.

Le pointer-events:none est important : il empêche l’indicateur de gêner les clics sur les vrais champs radio. Autrement dit, la pastille est visible, mais elle ne bloque pas l’interaction.

👉 Tout savoir sur le positionnement en CSS.

Animer le déplacement vertical

Pour déplacer l’indicateur, on va modifier sa propriété top avec le JavaScript. Côté CSS, il faut donc préparer la transition.

.indicator{
    transition:
        top .45s cubic-bezier(.22,1,.36,1),
        transform .35s cubic-bezier(.34,1.56,.64,1);
}

La propriété top anime le déplacement vertical. La propriété transform servira à ajouter un petit effet de rebond.

Cette combinaison donne une sensation plus vivante. L’indicateur ne saute pas sèchement d’un choix à l’autre, il glisse.

Calculer la position avec JavaScript

Le JavaScript va mesurer la position du champ radio sélectionné, puis déplacer l’indicateur à cet endroit.

const groupRect = group.getBoundingClientRect();
const rect = target.getBoundingClientRect();

indicator.style.top =
    (rect.top - groupRect.top + 6) + 'px';

indicator.style.transform = 'scale(1.25)';

setTimeout(() => {
    indicator.style.transform = 'scale(1)';
}, 180);

La méthode getBoundingClientRect() permet de connaître la position d’un élément dans la page. On mesure d’abord le groupe, puis le bouton radio ciblé.

Ensuite, on calcule la différence entre les deux positions. Cela permet de savoir où placer l’indicateur à l’intérieur du groupe.

Le + 6 sert à centrer la pastille dans le cercle. Comme l’input fait 24 pixels et la pastille 12 pixels, il reste 12 pixels de différence. La moitié donne 6 pixels.

Enfin, l’indicateur grossit légèrement avec scale(1.25), puis revient à sa taille normale. C’est le petit effet « pop » qui rend l’animation plus agréable.

Déplacer l’indicateur au changement d’option

Il faut maintenant écouter le changement de chaque champ input radio.

const checked = document.querySelector('input:checked');
moveIndicator(checked);

radios.forEach(radio => {

    radio.addEventListener('change', () => {
        moveIndicator(radio);
    });

});

Au chargement de la page, on cherche l’input déjà coché avec input:checked, puis on place l’indicateur dessus.

Ensuite, à chaque changement, on appelle la fonction moveIndicator() avec le bouton sélectionné.

Cette version est un peu plus avancée, mais elle reste très accessible. Le JavaScript ne remplace pas le fonctionnement du formulaire : il ne fait qu’accompagner visuellement le changement.

Quelle version choisir ?

Si vous débutez, commencez avec la première version et son animation CSS simple. Elle est légère et suffit largement pour la majorité des formulaires.

La version avec indicateur mobile est intéressante si vous voulez un rendu plus original, par exemple pour une interface de choix de catégorie, un formulaire moderne ou une page d’inscription un peu plus travaillée.

Dans les deux cas, gardez une règle simple : l’animation doit aider l’utilisateur, pas le distraire. Une bonne animation est comme un bon serveur au restaurant : présente au bon moment, discrète le reste du temps.

Bonnes pratiques pour animer un input radio

Une animation input radio doit rester rapide. Si l’effet dure trop longtemps, l’interface donne une impression de lenteur. En général, une durée entre 200 et 500 millisecondes fonctionne très bien.

Veillez aussi au contraste. Le bouton sélectionné doit être clairement visible. Si votre couleur active est trop proche de la couleur de bordure, l’utilisateur risque de ne pas comprendre quelle option est choisie.

Enfin, ne supprimez pas le vrai champ input. Il peut être personnalisé, mais il doit rester présent dans le HTML. C’est essentiel pour le comportement du formulaire, l’accessibilité et la navigation au clavier.


Peut-on personnaliser un champ input type radio uniquement avec CSS ?

Oui. Grâce à la propriété appearance: none, vous pouvez supprimer le style par défaut du navigateur et créer entièrement l’apparence de votre bouton radio avec du CSS, sans utiliser JavaScript.

L’animation d’un input radio ralentit-elle un site web ?

Non, si elle reste simple. Les transitions CSS sur les couleurs, les transformations ou l’opacité sont très légères et n’ont généralement aucun impact perceptible sur les performances du site.

Faut-il utiliser JavaScript pour créer une animation input radio ?

Pas forcément. La plupart des animations de boutons radio peuvent être réalisées uniquement avec CSS. JavaScript devient utile lorsque vous souhaitez créer des effets plus avancés, comme un indicateur qui se déplace d’une option à l’autre.


Animer un champ input radio est un excellent exercice pour progresser en CSS. Vous travaillez à la fois la structure HTML, les pseudo-classes comme :checked, les pseudo-éléments comme ::before, les transitions, les transformations et même un peu de JavaScript si vous choisissez la version avec indicateur mobile.

Le plus intéressant, c’est que cette technique peut ensuite être adaptée à beaucoup d’autres éléments d’interface : cases à cocher, boutons de sélection, filtres, cartes cliquables ou préférences utilisateur. Prenez le temps de modifier les couleurs, les tailles, les durées et les courbes d’animation. C’est en expérimentant que vous développerez votre œil.

Et souvenez-vous : un bon formulaire n’est pas seulement fonctionnel. Il peut aussi être agréable à utiliser. Même un petit bouton radio mérite parfois son moment de gloire.

👉 Pour aller plus loin avec les formulaire :