Skip to main content

Chapitre 11 : Gestion des états de formulaire avec :checked et :disabled

Contrôle visuel des inputs via les pseudo-classes d'état de formulaire

Dans le développement d'interfaces modernes, le formulaire n'est pas un élément statique. C'est un dialogue entre l'utilisateur et l'application. Pour que ce dialogue soit fluide, l'utilisateur doit savoir instantanément si une option est sélectionnée, si un champ est bloqué ou si une action est impossible.

En CSS, nous gérons cela grâce aux pseudo-classes d'état. Contrairement aux classes classiques que l'on ajoute via JavaScript, les pseudo-classes sont déclenchées nativement par le navigateur en fonction de l'état interne de l'élément HTML.

La pseudo-classe :checked

1. Quoi

La pseudo-classe :checked est un sélecteur qui cible tout élément de formulaire qui est actuellement sélectionné ou coché. Elle s'applique principalement aux éléments suivants :

  • <input type="checkbox"> (cases à cocher)
  • <input type="radio"> (boutons radio)
  • <option> (dans une liste déroulante <select>, bien que moins couramment stylisée ainsi)

C'est un état binaire : soit l'élément est coché, soit il ne l'est pas.

2. Pourquoi

L'utilité de :checked dépasse le simple fait de changer la couleur d'une case. En milieu professionnel, on l'utilise pour :

  • Améliorer l'accessibilité : Donner un feedback visuel fort pour les utilisateurs malvoyants.
  • Créer des composants personnalisés : Les cases à cocher natives sont très difficiles à styliser. On utilise souvent :checked combiné avec le sélecteur adjacent (+) ou général (~) pour styliser un élément décoratif (comme un <span> ou un <div>) à la place de l'input natif.
  • Logique d'interface sans JS : Créer des menus accordéons ou des onglets simples en utilisant la technique du "Checkbox Hack".

3. Comment

A. Syntaxe de base

Voici comment changer la couleur de bordure d'un input quand il est coché :

input[type="checkbox"]:checked {
outline: 2px solid blue;
background-color: lightblue;
}

B. Cas concret : Case à cocher personnalisée

Comme on ne peut pas modifier l'apparence interne d'une checkbox nativement de manière cohérente sur tous les navigateurs, la méthode standard consiste à masquer l'input et à styliser un label associé.

<div class="custom-checkbox">
<input type="checkbox" id="terms" class="hidden-checkbox" />
<label for="terms" class="checkbox-label">
<span class="checkmark"></span>
J'accepte les conditions générales
</label>
</div>
/* On masque l'input natif mais on le garde accessible pour le clavier */
.hidden-checkbox {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}

/* Le style de base de notre fausse case */
.checkmark {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
vertical-align: middle;
margin-right: 10px;
transition: all 0.2s ease;
}

/*
C'est ici que la magie opère :
"Quand l'input est :checked, cible le .checkmark qui se trouve
dans le label qui suit immédiatement"
*/
.hidden-checkbox:checked + .checkbox-label .checkmark {
background-color: #007bff;
border-color: #007bff;
}

/* Ajout d'un indicateur visuel (le check) via un pseudo-élément */
.hidden-checkbox:checked + .checkbox-label .checkmark::after {
content: '✓';
color: white;
display: block;
text-align: center;
font-size: 14px;
line-height: 20px;
}

C. Limitations

  • Sélecteurs descendants : :checked ne peut pas cibler un élément parent. Vous ne pouvez pas dire "si l'input est coché, change la couleur du fond de la div parente" (bien que le nouveau sélecteur :has() commence à rendre cela possible dans les navigateurs modernes).
  • Accessibilité : Si vous masquez l'input natif, assurez-vous que le focus reste visible (via :focus-visible) pour les utilisateurs naviguant au clavier.

4. Zone de Danger

Erreur commune : Essayer de styliser l'input natif avec background-color et s'étonner que cela ne fonctionne pas sur Safari ou Chrome.

Bonne pratique : Masquer l'input natif et utiliser :checked + label pour créer un design totalement contrôlé et cohérent.

Flux de gestion des états de formulaire


La pseudo-classe :disabled

1. Quoi

La pseudo-classe :disabled cible tout élément de formulaire qui possède l'attribut HTML disabled. Elle s'applique à presque tous les éléments de saisie :

  • <input> (text, email, password, checkbox, radio, etc.)
  • <button>
  • <select>
  • <textarea>

Un élément désactivé ne peut pas recevoir le focus, ne peut pas être modifié par l'utilisateur et n'est pas envoyé lors de la soumission du formulaire.

2. Pourquoi

L'aspect visuel d'un champ désactivé est crucial pour l'expérience utilisateur (UX) :

  • Prévention d'erreurs : Indiquer clairement qu'une action est impossible tant qu'une condition n'est pas remplie (ex: le bouton "Envoyer" est désactivé tant que l'email n'est pas valide).
  • Hiérarchie d'information : Montrer des données en lecture seule qui ne doivent pas être modifiées.
  • Guidage : Forcer l'utilisateur à suivre un ordre précis de remplissage.

3. Comment

A. Syntaxe de base

input:disabled {
background-color: #f5f5f5;
color: #999;
cursor: not-allowed; /* Change le curseur en signe "interdit" */
}

B. Cas concret : Bouton de validation conditionnel

Imaginons un bouton qui doit être visuellement "éteint" tant que l'utilisateur n'a pas coché la case "CGU".

<form>
<div class="field">
<input type="checkbox" id="agree" />
<label for="agree">J'accepte les conditions</label>
</div>

<button type="submit" id="submit-btn" disabled>
Créer mon compte
</button>
</form>
#submit-btn {
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: opacity 0.3s ease;
}

/* Style spécifique quand le bouton est désactivé */
#submit-btn:disabled {
background-color: #cccccc;
color: #666666;
cursor: not-allowed;
opacity: 0.7;
/* On retire les effets de survol pour éviter la confusion */
pointer-events: none;
}

C. Limitations

  • Spécificité : :disabled a une priorité forte, mais peut être écrasé par des sélecteurs d'ID ou des styles inline.
  • Interaction : Un élément :disabled ne déclenche aucun événement JavaScript (click, mouseover, etc.). Si vous avez besoin de déclencher un message d'erreur au clic sur un champ "bloqué", vous ne devez pas utiliser disabled mais plutôt un attribut personnalisé ou une classe, tout en gérant l'accessibilité.

4. Zone de Danger

Erreur commune : Utiliser uniquement une couleur grise pour indiquer qu'un champ est désactivé sans ajouter l'attribut HTML disabled. L'utilisateur pourra toujours cliquer dedans, créant une frustration immense. ✅ Bonne pratique : Toujours coupler le style :disabled avec l'attribut HTML disabled pour garantir que le comportement technique correspond au signal visuel.


Questions clés

Q1 : Quelle est la différence fondamentale entre :checked et :disabled ?

Découvrir la réponse

:checked est un état de sélection (est-ce que l'option est choisie ?), tandis que :disabled est un état de disponibilité (est-ce que l'élément est utilisable ?).

Q2 : Pourquoi utiliser :checked + label plutôt que de styliser directement l'input ?

Découvrir la réponse

Parce que les navigateurs limitent drastiquement la personnalisation visuelle des inputs de type checkbox et radio. En ciblant le label (ou un élément enfant du label), on peut créer n'importe quel design tout en conservant la fonctionnalité native.

Q3 : Est-ce que :disabled fonctionne sur un élément <div> ?

Découvrir la réponse

Non. La pseudo-classe :disabled ne fonctionne que sur les éléments de formulaire interactifs. Pour un div, il faudra utiliser une classe CSS (ex: .is-disabled) et gérer l'interactivité via JavaScript ou pointer-events: none.

Q4 : Comment s'assurer qu'un champ désactivé reste accessible ?

Découvrir la réponse

Bien que :disabled retire le champ de la tabulation clavier, il est important que le contraste de couleur reste suffisant pour que l'utilisateur puisse au moins lire l'information contenue dans le champ.


Mise en pratique

Exercice 1 : Reproduction guidée

Objectif : Créer un champ de saisie texte qui change de couleur de fond lorsqu'il est désactivé.

  1. Créez un <input type="text"> avec une valeur par défaut.
  2. Ajoutez l'attribut disabled à cet input.
  3. En CSS, utilisez la pseudo-classe :disabled pour mettre le fond en gris clair (#eee) et le texte en gris foncé (#777).
Découvrir la solution commentée

<input type="text" value="Ce champ est bloqué" disabled />
/* CSS */
input:disabled {
background-color: #eee; /* Fond gris pour signifier l'inactivité */
color: #777; /* Texte atténué */
border: 1px solid #ccc;
padding: 8px;
}

Exercice 2 : Adaptation

Objectif : Créer un groupe de boutons radio personnalisés.

  1. Créez trois boutons radio pour choisir une taille (S, M, L).
  2. Masquez les inputs radio natifs.
  3. Stylisez des labels pour qu'ils ressemblent à des boutons.
  4. Utilisez :checked pour que le bouton sélectionné change de couleur de fond (ex: bleu) et de couleur de texte (blanc).
Découvrir la solution commentée
<div class="size-picker">
<input type="radio" name="size" id="s" value="s" checked />
<label for="s">S</label>

<input type="radio" name="size" id="m" value="m" />
<label for="m">M</label>

<input type="radio" name="size" id="l" value="l" />
<label for="l">L</label>
</div>
/* Masquage des radios natifs */
.size-picker input[type="radio"] {
display: none;
}

/* Style des labels comme des boutons */
.size-picker label {
display: inline-block;
padding: 10px 20px;
border: 2px solid #007bff;
color: #007bff;
cursor: pointer;
border-radius: 5px;
margin-right: 5px;
transition: all 0.2s;
}

/* État sélectionné : on cible le label qui suit l'input coché */
.size-picker input[type="radio"]:checked + label {
background-color: #007bff;
color: white;
}

Exercice 3 : Conception

Objectif : Créer un "Interrupteur" (Toggle Switch) moderne.

Le HTML de départ est fourni, concentrez-vous uniquement sur le CSS.

<div class="switch-container">
<input type="checkbox" id="wifi" class="toggle-input" />
<label for="wifi" class="toggle-label"></label>
<span>Wi-Fi</span>
</div>

Étapes de base :

  1. Masquez l'input natif (il reste fonctionnel, seul le label sera visible).
  2. Stylisez .toggle-label pour qu'il ressemble à une pilule (border-radius élevé, dimensions fixes).
  3. Utilisez :checked + .toggle-label pour passer le fond de gris à vert quand la case est cochée.
  4. Ajoutez une transition sur la couleur de fond.

Bonus (optionnel — fait appel aux pseudo-éléments) :

  1. Ajoutez un cercle blanc qui se déplace de gauche à droite lors du clic. Pour cela, créez ce cercle via .toggle-label::after (pseudo-élément CSS) et déplacez-le avec transform: translateX(...) sur l'état :checked.
  2. Ajoutez un second switch avec l'attribut disabled et stylisez son état grisé via :disabled + .toggle-label.
Découvrir la solution commentée
<div class="switch-container">
<input type="checkbox" id="wifi" class="toggle-input" />
<label for="wifi" class="toggle-label"></label>
</div>

<div class="switch-container">
<input type="checkbox" id="wifi-off" class="toggle-input" disabled />
<label for="wifi-off" class="toggle-label"></label>
<span>(Désactivé)</span>
</div>
.switch-container {
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}

/* Masquage de l'input */
.toggle-input {
display: none;
}

/* Le corps du switch */
.toggle-label {
width: 50px;
height: 26px;
background-color: #ccc;
border-radius: 13px;
position: relative;
cursor: pointer;
transition: background-color 0.3s;
}

/* Le cercle blanc */
.toggle-label::after {
content: '';
position: absolute;
width: 22px;
height: 22px;
background-color: white;
border-radius: 50%;
top: 2px;
left: 2px;
transition: transform 0.3s;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

/* État Coché : Fond vert et déplacement du cercle */
.toggle-input:checked + .toggle-label {
background-color: #4cd964;
}

.toggle-input:checked + .toggle-label::after {
transform: translateX(24px);
}

/* État Désactivé : On cible le label quand l'input est :disabled */
.toggle-input:disabled + .toggle-label {
background-color: #e0e0e0;
cursor: not-allowed;
opacity: 0.6;
}

/* Empêcher le mouvement si désactivé (même si l'input est coché) */
.toggle-input:disabled + .toggle-label::after {
background-color: #f0f0f0;
}