Bouton à bascule (Switch)

Un interrupteur permettant d'activer ou désactiver une option de manière binaire (On/Off), sans JavaScript.

1. Démonstration

Voulez-vous recevoir vos notifications ?

2. Accessibilité (La magie du role="switch")

Créer un interrupteur accessible sans JavaScript est possible en détournant intelligemment une case à cocher native.

Concept / Attribut Explication
<input type="checkbox"> Fournit nativement la gestion du focus clavier (Tab) et le changement d'état via la touche Espace.
role="switch" Modifie la sémantique de la case à cocher. Au lieu de dire "coché/non coché", le lecteur d'écran annoncera "activé/désactivé" (ou On/Off), ce qui correspond à l'attente cognitive d'un interrupteur.
Zone de clic étendue Envelopper l'<input> et le texte dans une balise <label> permet à l'utilisateur de cliquer sur le texte pour actionner le switch.

Note RGAA : Si l'action du switch modifie immédiatement l'interface (comme le changement de thème clair/sombre), il est parfois préférable d'utiliser un <button aria-pressed="true/false"> couplé à du JavaScript. Mais pour des réglages de formulaire, le <input type="checkbox" role="switch"> est parfait !

3. Code source

HTML

La structure est identique à celle d'une case à cocher classique, avec le role="switch" en plus.

<label class="switch-label">
  <input type="checkbox" role="switch" name="newsletter">
  Abonnement à la newsletter
</label>

CSS

Ce code utilise appearance: none pour effacer le rendu natif du navigateur. Il dessine ensuite le fond de l'interrupteur, et utilise le pseudo-élément ::before pour créer la pastille (le curseur) qui se déplace avec une transition fluide.

/* Style du conteneur (Label) */
.switch-label {
  display: inline-flex;
  align-items: center;
  gap: 1rem;
  cursor: pointer;
  font-weight: 700;
  width: fit-content;
}

/* La piste de l'interrupteur (Background) */
input[type="checkbox"][role="switch"] {
  -webkit-appearance: none;
  appearance: none;
  background-color: transparent;
  margin: 0;
  width: 3rem;       /* Largeur de la pilule */
  height: 1.5rem;    /* Hauteur de la pilule */
  border: 2px solid var(--border-color);
  border-radius: 1.5rem; /* Forme de pilule */
  position: relative;
  cursor: pointer;
  transition: background-color var(--transition-speed) ease;
}

/* Focus visible très contrasté */
input[type="checkbox"][role="switch"]:focus-visible {
  outline: 3px solid var(--text-color);
  outline-offset: 3px;
}

/* La pastille de l'interrupteur (Le fond rond) */
input[type="checkbox"][role="switch"]::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: calc(1.5rem - 8px);
  height: calc(1.5rem - 8px);
  border-radius: 50%;
  background-color: var(--text-color);
  transition: transform var(--transition-speed) ease-in-out, background-color var(--transition-speed) ease;
}

/* L'icône interne (Croix ou Coche via clip-path) */
input[type="checkbox"][role="switch"]::after {
  content: "";
  position: absolute;
  /* On centre parfaitement l'icône de 8px au milieu de la pastille de 16px */
  top: 6px;
  left: 6px;
  width: calc(1.5rem - 16px);
  height: calc(1.5rem - 16px);
  background-color: var(--bg-color); /* L'icône prend la couleur de fond (noir) */
  
  /* Forme de la Croix (X) */
  clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
  transition: transform var(--transition-speed) ease-in-out, background-color var(--transition-speed) ease;
}

/* États : Coché / Activé */
input[type="checkbox"][role="switch"]:checked {
  background-color: var(--text-color);
}

input[type="checkbox"][role="switch"]:checked::before {
  /* On déplace la pastille vers la droite */
  transform: translateX(1.5rem);
  background-color: var(--bg-color);
}

input[type="checkbox"][role="switch"]:checked::after {
  /* On déplace l'icône en synchronisation avec la pastille */
  transform: translateX(1.5rem);
  background-color: var(--text-color);
  
  /* La Croix se transforme instantanément en Coche (✓) */
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}