Customiser un input checkbox avec un label

Hello hello ! Le but de cet article est d'expliquer pas à pas comment customiser un input checkbox tout en gardant son label. Le plus important, c'est de réussir à jouer avec l'apparence de l'input tout en le laissant 100% accessible.

Voici l'input checkbox que nous allons réaliser dans cet article :

See the Pen
Untitled
by Laura Durieux (@Lauwed)
on CodePen.

Voici les différentes étapes :

  1. Côté HTML et accessibilité
  2. Casser l'apparence par défaut de l'input checkbox
  3. Personnaliser l'état checked de l'input checkbox

N'attendons plus et plongeons tête la première.

Côté HTML et accessibilité

La balise form

Commençons par créer notre HTML de base. Tout d'abord, qui dit balise input, dit balise form. Peu importe l'utilité de votre input, si vous en avez un, c'est qu'il y a une notion de formulaire. Même lorsque vous faites du shopping en ligne et que vous triez les articles en fonction de leur couleurs, c'est un formulaire.

Je vais donc commencer par créer mon formulaire.

<form action="/" method="GET" class="form">
</form>

Je spécifie les deux attributs action et method qui sont importants de connaître. Vous pouvez vous réferrer à la documentation MDN pour en savoir plus. J'ajoute également une classe form pour se mettre en situation le plus possible.

Création de l'input

Ok, première étape pour une bonne accessibilité. Maintenant, nous allons ajouter l'input et son label.

<form action="/" method="GET" class="form">
  <input type="checkbox" name="todo" value="Manger un ramen" id="ramen">
  <label for="ramen"><span>Manger un ramen</span></label>
</form>

Première chose ; Arrêtez de mettre la balise input dans la balise label. C'est un bon exemple de vouloir faire un effort niveau accessibilité, mais de ne pas correctement utiliser ses outils. Vous avez également remarqué que j'ai mis le label après l'input. Heureusement, grâce à lattribut value, le screen reader ira lire directement cette information. Du coup, que le label soit avant ou après la checkbox, la personne aura l'information lorsque il arrivera sur l'input checkbox.
Mais du coup, si je ne mets pas l'input dans le label, comment je fais pour les "lier" ? C'est ici que l'attribut for rentre en jeu. Vous pouvez remarquer que l'attribut id de l'input et l'attribut for du label on la même valeur. L'attribut for du label permet d'indiquer par l'ID à quel input il appartient. Maintenant, vous pouvez cibler un input même en cliquant sur son label.

Petit plus

Pour finir l'HTML, nous allons anticiper nos besoins pour le CSS en regroupant l'input et le label dans une div et lui donner une classe en respectant la convention de nommage BEM. Ici, le block, c'est le form et l'element, c'est l'item.

<form action="/" method="GET" class="form">
  <div class="form__item">
    <input type="checkbox" value="Manger un ramen" name="ramen" id="ramen">
    <label for="ramen"><span>Manger un ramen</span></label>
  </div>
</form>

Screenshot d'une checkbox de base où il est indiqué "Manger un ramen".

Casser l'apparence par défaut de l'input checkbox

Le style par défaut et utilisation de appearance: none;

Par défault, l'apparence et l'état checkeddes inputs sont assez simples.
Screenshot de deux inputs checkbox montrant le style par défault lorsque la checkbox est checked et unchecked.

La propriété de base qui donne justement ce style par défaut, c'est appearance: auto; inclus dans la user agent stylesheet. Notre première étape sera donc de supprimer ce style par défault en enlevant l'apparence automatique en utilisant appearance: none;.

input {
    appearance: none;
}

Ajouter du style à l'input

Par contre, le résultat c'est... Rien ! Cette propriété enlève tous les styles par défault, ce qui veut dire que la checkbox n'est plus visible, car elle n'a plus de largueur, hauteur, bord, .. définis ! Il va donc falloir tout redéfinir manuellement en CSS.
Voici le style par défault de la checkbox :

input {
    /* Disable default style */
    appearance: none;
    /* Define the width and the height */
    width: 25px;
    height: 25px;
    /* Space between the input and the label */
    margin-right: 15px;
    /* Custom appearance */
    border-radius: 50%;
    border: 2px solid black;
}

J'ai également ajouté un peu de style à la div qui groupe le label et l'input pour qu'ils soient centrés verticalement.

.form__item {
    display: flex;
    align-items: center;
}

Screenshot de la progression de l'article, la checkbox est maintenant ronde est le label dit "Manger un ramen".

Personnaliser l'état checked de l'input checkbox

En créant notre checkbox, nous avons fait attention à l'accessibilité, ensuite à correctement enlever le style par default et mettre le notre. Le problème suivant, c'est l'état checked de l'input. Vu que nous utilisons appearance: none;, lorsque vous cliquez sur l'input, il n'y a rien qui se passe visuellement. Nous allons donc devoir reprogrammer tout cela.

L'état checked d'un input en CSS

En CSS, il est possible de cibler l'état d'un élément grâce à un pseudo-sélecteur. En fonction de la balise HTML, différents pseudo-sélecteur sont disponibles. Vous connaissez sûrement déjà hover, focus ou encore visited pour les liens. Pour les inputs checkbox et radio, il existe un pseudo-sélecteur checked. Utilisons-le de suite ;

input:checked {
    background-color: #ffa726;
}

Du coup, maintenant que la checkbox est checked, ça va donner ceci ;
Screenshot de la progression de l'article, lorsque la checkbox est checked, sa couleur de fond est orange.

Style de la checkbox checked en utilisant les pseudo-éléments

Bon, c'est bien beau, mais c'est pas ça que nous voulons faire comme effet lorsque la checkbox est checked. Nous aimerions qu'il y ait une boule orange qui apparaisse au centre de la checkbox. Cette boule, nous allons utiliser un pseudo-élément CSS pour la créer. Commençons pas la créer comme si elle est là par défault.

.form__item input {
    appearance: none;
    border-radius: 50%;
    border: 2px solid black;
    width: 25px;
    height: 25px;
    margin-right: 15px;
    /* Add relative position to center the bullet to the input and not the body */
    position: relative;
}
.form__item input::before {
    /* Mandatory to create a pseudo element, even if the content is empty */
    content: "";
    /* To be able to position the bullet at the center of the input */
    position: absolute;
    /* Centering the bullet */
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* Width and height relative to the size of the input */
    width: 75%;
    height: 75%;
    /* Custom style */
    border-radius: 50%;
    background: #ffa726;
}

Screenshot de la progression de l'article, lorsque la checkebox est checkex, il y a une boule orange au milieu.

Maintenant, nous allons utiliser la propriété transform pour faire apparaître la boule et la propriété transition pour donner un effet d'animation lorsque l'input change d'état.

.form__item input::before {
    /* ... */
    /* Scale to 0, like that the bullet is visually hidden */
    transform: translate(-50%, -50%) scale(0);
    /* Transition effect on the transform property */
    transition: transform .3s ease;
}

La boule devient invisible, car elle a été redimmensionnée à 0. Le pseudo-élément existe toujours, mais plus visuellement. Le but maintenant va de réussir à cibler le pseudo-élément lorsque la checkbox est checked. Pour cela, rien de plus simple, il faut d'abord cibler l'état de l'élément parent, puis indiquer le pseudo-élément ou psudo-sélecteur souhaité.

input:checked::before {
    /* Your code here */
}

Allons-y. Voici le code besoin pour faire réapparaître la boule lorsque la checkbox est checked.

input:checked::before {
    transform: translate(-50%, -50%) scale(1);
}

Personnaliser le label d'une checkbox checked

Pour pouvoir faire cela, il faut connaître le sélecteur css +. Ce que nous allons faire, c'est cibler le label, qui est le voisin direct de la chebox, quadn celle-ci est checked.

input:checked + label {
    color: #ffa726;
}

Screenshot de deux checkbox montrant la progression et la différence entre la checkbox checked et unchecked.

Style du label de la checkbox checked en utilisant les pseudo-éléments

Comme pour la chebox, nous allons utiliser un pseudo-élément pour créer la barre qui se retrouve sur le label lorsque la checkbox est checked. Nous allons commencer par raproduire ce style par défault, sans tenir compte de l'état de la checkbox.
Voici le style dont nous avons besoin :

.form__item label {
    /* Will help to position the pseudo-element */
    position: relative;
}
.form__item label span {
    /* Prevent user to select the text */
    user-select: none;
    /* Custom style */
    opacity: 0.5;
}
.form__item label::before {
    /* Mandatory to create a pseudo element, even if the content is empty */
    content: "";
    /* To be able to position the bar at the center vertically of the label */
    position: absolute;
    /* Positionning the bar */
    left: 0;
    top: 50%;
    /* Width relative to the size of the label */
    width: 100%;
    height: 3px;
    /* Custom style */
    background-color: #ffa726;
    transform: translateY(-50%);
}

Screenshot de la progression, lorsque la checkbox est checked, le label se retrouve barré par un rectangle orange.

Comme pour la boule, nous voulons que ça soit l'état uniquement quand la checkbox est checked. Voici les modifications que nous devons apporter au label pour son style de base :

.form__item label span {
    /* ... */
    /* Custom style */
    opacity: 1;
    /* Transition effect on the opacity */
    transition: opacity .3s ease;
}
.form__item label::before {
    /* ... */
    /* Adding the transformation scaleX to have the horizontal appearing animation */
    transform: translateY(-50%) scaleX(0);
    /* The origin of the transformation. Like that the bar appears from the left to the right */
    transform-origin: left center;
    /* Transition effect on the transformation */
    transition: transform .3s ease;
}

Je vous conseille d'aller lire un peu sur la propriété transform-origin qui peut bien sauver des vies de temps en temps. Ensuite, nous devons ajouter un peu de style lorsque la checkbox est checked.

.form__item input:checked + label span {
    opacity: 0.5;
}
.form__item input:checked + label::before {
    transform: translateY(-50%) scaleX(1);
}

Et voilà !

Merci beaucoup d'avoir lu cet article ! Je l'ai rédigé avec beaucoup de bonne humeur et j'espère avoir pu aider quelques âmes perdues.

N'hésitez pas à laisser un commentaire et de me supporter en m'achetant un café ✨

Ciao !