Contenu principal
iOS checkbox

Personnaliser les checkbox en CSS façon iOS

Voici une astuce très intéressante pour personnaliser les checkbox, et ce, en pur CSS. Nous allons même pousser la personnalisation jusqu’à imiter les interrupteurs « 1/0 » des iBidules avec du CSS 3. Au programme : pseudo-éléments :before et :after, dégradés, multiples backgrounds et ombres.

[NOTE]
Bon, je dois avouer que je suis très déçu. Oui, j’étais tout fier d’avoir trouvé cette astuce pour personnaliser les checkbox, sauf que bien sûr, d’autres l’ont fait avant moi (oui bon ok, je me doutais bien que je serais pas le premier, mais bon…).
[/NOTE]

Obstacles imposés :

  1. Aucune image (vous vous attendiez à quoi?).
  2. Marquage HTML minimum : seulement l’input et son label, pas de div ou de span supplémentaire pour styler les checkbox (sinon c’est pas drôle).
  3. Compatible IE6. Heu… Non, ça on oublie.

Bon, c’est parti?

Marquage HTML

Comme énoncé, nous n’allons utiliser qu’un input et un label :

1

<input type="checkbox" name="check-3" id="check-3" /><label for="check-3">Activer la 3G</label>

Les deux choses importantes dans ce marquage sont l’attribut « for » du label, qui doit correspondre à l’input (afin qu’un clic sur le label active l’input), et l’input doit être située avant le label.
Une fois insérés dans la page, ça donne ça :

01020304050607080910111213

<div class="iOS">
	<ul>
		<li><input type="checkbox" name="check-3" id="check-3" checked="checked" /><label for="check-3">Activer la 3G</label></li>
	</ul>
	<p>La 3G permet un chargement plus rapide des données mais peut diminuer l'autonomie de la batterie.</p>
	<ul>
		<li><input type="checkbox" name="check-4" id="check-4" checked="checked" /><label for="check-4">Données cellulaires</label></li>
	</ul>
	<ul>
		<li><input type="checkbox" name="check-5" id="check-5" /><label for="check-5">Données à l'étranger</label></li>
	</ul>
	<p>Pendant vos déplacements à l'étranger, vous pouvez désactiver le service « Données à l'étranger » pour éviter des coûts importants liés à l'utilisation d'Internet, de MMS, de courriers électroniques ou d'autres services de données.</p>
</div>

Mise en page

Je vais passer rapidement sur le CSS de la page elle-même car ce n’est pas le but principal du tutoriel.

0102030405060708091011121314151617181920212223242526272829303132333435363738394041

.iOS {
	width: 302px;
	padding: 10px 9px 0;
	margin: auto;
	font-size: 16px;
	line-height: 20px;
	font-family: Helvetica, arial, sans-serif;
	background-color: #c5ccd4;
	-webkit-background-size: 7px 100%;
	background: -webkit-gradient(linear, 0% 0%, 100% 0, from(#c5ccd4), to(#cbd2d8), color-stop(.714, #c5ccd4), color-stop(.714, #cbd2d8));
	background: -webkit-repeating-linear-gradient(left 0deg, #c5ccd4, #c5ccd4 5px, #cbd2d8 5px, #cbd2d8 7px);
	background:    -moz-repeating-linear-gradient(left 0deg, #c5ccd4, #c5ccd4 5px, #cbd2d8 5px, #cbd2d8 7px);
	background:      -o-repeating-linear-gradient(     0deg, #c5ccd4, #c5ccd4 5px, #cbd2d8 5px, #cbd2d8 7px);
	background:         repeating-linear-gradient(left 0deg, #c5ccd4, #c5ccd4 5px, #cbd2d8 5px, #cbd2d8 7px);
}
.iOS p {
	padding: 0 0 20px 0;
	text-align: center;
	color: #4c566c;
	text-shadow: 0 1px 0 #fff;
}
.iOS ul {
	padding: 10px 10px 0;
	margin: 0 0 10px 0;
	color: #000;
	font-weight: bold;
	background: #fff;
	-webkit-border-radius:	10px;
	-moz-border-radius:	10px;
	-o-border-radius:	10px;
	border-radius:		10px;
	border: solid 1px #ababab;
}
.iOS ul+ul {
	margin-top: 20px;
}
.iOS li {
	display: block;
	clear: both;
	margin-bottom: 10px;
}

Juste une petite explication sur le fond crée avec un dégradé. Plutôt que d’utiliser l’habituel linear-gradient(), nous utilisons repeating-linear-gradient(). Nous avons donc :

  • left 0deg : le dégradé commence à gauche (left) et s’étend vers la droite (0 degrés).
  • #c5ccd4 : couleur de départ, gris.
  • #c5ccd4 5px : fin de la première rayure grise de 5px.
  • #cbd2d8 5px : début de la deuxième rayure, gris clair.
  • #cbd2d8 7px : fin de la fine rayure gris clair, 2px d’épaisseur, elle se finit donc à 7px.

Compatibilité navigateur et fallback

Bien sûr avec le CSS avancé que nous allons employer, nous pouvons faire une croix sur Internet Expl’Horreur (un petit clin d’œil à Teddy, qui se reconnaitra ;) ). Vus les changements que nous allons appliquer, nous devons faire attention à ce que nous faisons pour ne pas appliquer seulement la moitié du style sur certains navigateurs et ainsi gêner l’utilisation des checkbox. En gros, il nous faut faire du « tout ou rien ».

Récemment j’ai lu un article de Lea Verou exposant une technique très intéressante qui consiste à « filtrer » les déclarations CSS avec des sélecteurs potentiellement non-interprétés par les anciens navigateurs (d’ailleurs elle utilise comme exemple une personnalisation de checkbox aussi).
Pour faire bref, voici les les sélecteurs que nous allons utiliser ici, et qui pourraient ne pas être compris par le navigateur : les pseudo-éléments :before et :after, la pseudo-classe :checked, le sélecteur de voisin suivant immédiat « + » et le sélecteur de type [type="checkbox"]. Ainsi, pour être sûr que le navigateur comprend tous ces sélecteurs, il doit comprendre ceci par exemple : #foo input[type="checkbox"]:checked+label:before.

Mais où cela nous mène-t-il?
Et bien si un navigateur ne comprend pas un de ces sélecteurs, il n’appliquera pas la déclaration. Donc on va lui donner à manger tous ces sélecteurs à chaque déclaration.
Exemple :
Si on a le sélecteur suivant : input[type="checkbox"]+label, il nous manque :checked et :before. Donc on va faire : #foo:checked:before, input[type="checkbox"]+label en s’assurant que #foo est un id qui n’existe pas dans notre page. Oui, même si nos 4 sélecteurs sont séparés en 2 groupes par une virgule, le navigateur est sensé ne pas appliquer la déclaration.
Cependant, cette technique est à appliquer seulement lorsqu’elle est vraiment nécessaire car elle demande aux navigateurs plus de travail, étant donné qu’on lui demande plus de chose avec nos sélecteurs supplémentaires.

Au final, les navigateurs compatibles seront Firefox, Chrome, Safari et Opera (et IE9 à priori, avec une version dégradée, non testé car je ne l’ai pas à disposition). IE8 et inférieurs ne seront pas de la partie.

La checkbox originale

Notre premier travail va être de cacher la checkbox originale et de prévoir un fallback. La checkbox devra néanmoins rester visible pour les vieux navigateurs. Nous allons donc employer la technique que nous venons de voir, mais c’est là que le bas blesse, à cause de IE7. Avec IE7 si on fait a, b {...} et qu’il connait « a » mais pas « b », il va quand même appliquer la déclaration, parce qu’apparemment au lieu de traiter « a, b » comme un seul groupe, il le sépare en deux (comme quoi, même dans l’échec, IE peut encore faire pire). Donc nous sommes obligés de tricher encore plus.
D’abord on met un petit margin à l’input pour les navigateurs qui ne devront pas masquer la checkbox, puis on va utiliser les pseudo-classes :not() et :checked pour les autres navigateurs.

[update]

Navigation au clavier

A l’origine j’avais utilisé la propriété display: none; pour masquer les checkbox mais je me suis aperçu que cela désactivait la navigation au clavier par la même occasion.
A la place j’utilise les propriétés opacity, clip et appearance principalement. A noter que pour utiliser clip, l’élément doit être positionné en absolute, et j’utilise la double notation avec/sans virgules pour toucher tous les navigateurs. Les checkbox étant donc positionnées en absolute mais sans préciser de left ou de top, inutile de positionner les balises <li> en relative. Les checkbox ne bougeront pas, et se positionneront naturellement au niveau de leur label, mais invisibles.
A noter également que mettre une largeur ou une hauteur égale à zéro, désactive aussi la navigation au clavier. Les bordures ne peuvent être modifiées.
En somme, les propriétés position et clip sont les seules absolument nécessaires, les autres sont facultatives

424344454647484950515253545556575859

.iOS input {
	margin-right: 4px;
}
.iOS input[type="checkbox"]:checked,
.iOS input[type="checkbox"]:not(:checked) {
	width: 1px;
	height: 1px;
	margin: 0;
	padding: 0;
	opacity: 0;
	position: absolute;
	clip: rect(0 0 0 0);
	clip: rect(0,0,0,0);
	-webkit-appearance:	none;
	-moz-appearance:	none;
	-o-appearance:		none;
	appearance:		none;
}

Le label

On sélectionne les labels voisins aux checkbox. Nos boutons feront 70px de largeur donc on met un padding-right de 80px aux labels pour ne pas que le texte, s’il est trop long, passe sous le bouton (les boutons seront positionnés en absolu).

6061626364656667

#foo:checked:before,
.iOS input[type="checkbox"]+label {
	display: block;
	padding-right: 80px;
	position: relative;
	cursor: pointer;
	text-align: left;
}

Le fond du bouton

C’est là que ça se corse, nous avons pas mal d’obstacles d’un coup.

  1. On a du texte à afficher « I    O » dont les caractères doivent être bien positionnés. De plus, plusieurs espaces à la suite se transformeront en 1 seul espace et l’espace insécable ne va pas fonctionner.
  2. Les 2 caractères « I » et « O » sont de couleur différente. Nous pourrions mettre tout en gris puis utiliser :first-letter pour mettre le « I » en blanc. Or nous devront positionner notre bouton en absolu, et en absolu, cela ne marche pas (et j’avoue ne pas savoir pourquoi).
  3. Si on observe bien le bouton, la partie mobile est moins large que la moitié du bouton, ce qui va gêner pour le fond qui devra être moitié bleu et moitié gris clair.
  4. Si on ne met pas notre bouton en « block », on va avoir des écarts de dimensions incontrôlables entre les navigateurs. Il faudra donc le placer ensuite avec un float: right ou une position absolue.
  5. Tant qu’à y être, ça serait bien de rajouter des transitions CSS si possible.

Ouais, c’est pas gagné hein? ;)

Première étape : les sélecteurs, les dimensions, le positionnement, les coins arrondis et les ombres.

On va utiliser :before et notre bouton fera 70px par 20px, placé à droite à mi-hauteur.

6869707172737475767778798081828384

#foo:checked,
.iOS input[type="checkbox"]+label:before {
	position: absolute;
	right: 0;
	bottom: 50%;
	margin-bottom: -10px;
	width: 70px;
	height: 20px;
	-webkit-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-moz-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-o-box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-webkit-border-radius:	3px;
	-moz-border-radius:	3px;
	-o-border-radius:	3px;
	border-radius:		3px;
}

Deuxième étape, le texte.

On va insérer « I » et « O » avec content: "...". Comme je l’ai dit, plusieurs espaces côte à côte se transformeront en 1 seul espace, il faut donc utiliser un espace insécable, sauf que si on utilise le code &nbsp, ça va nous afficher I&nbsp&nbsp&nbspO. A la place nous utilisons le code 000a0, qui est le code exadécimal de l’espace insécable, précédé d’une barre oblique inverse. Ensuite il ne nous reste plus qu’à centrer le texte et préciser sa taille, l’interligne, etc. Premier soucis résolu.
Le deuxième point embêtant, comme dit plus haut, c’est la couleur. Le pseudo-élément :first-letter ne va pas s’appliquer pour je ne sais quelle raison. Donc, on va précisé que la couleur de texte doit être blanche quand le bouton est activé et grise quand il est désactivé. Cool, c’était pas bien compliqué. Au passage, on modifie la largeur du bouton et on rajoute un padding pour que le texte soit bien centré (à cause des lettres qui n’ont pas la même largeur, le texte serait décalé).

68697071727374757677787980

#foo:checked,
.iOS input[type="checkbox"]+label:before {
	content: 'I000a0000a0000a0000a0000a0000a0O';
	width: 66px;
	padding-left: 4px;
	line-height: 22px;
	font-size: 13px;
	font-weight: bold;
	text-align: center;
	letter-spacing: 0;
	color: rgb(132,132,132);
	text-shadow: none;
}

Troisième étape, le fond.

Comme nous le disions, on doit avoir un côté bleu et un côté gris, et les 2 côtés se « chevauchent » au milieu car le curseur mobile est moins large que la moitié de la largeur du bouton. Donc on pourrait dire que le fond est bleu quand le bouton est activé, et gris quand le bouton est désactivé. Oui, sauf que ça serait trop simple, et surtout, si on veut animer le bouton, ça va se voir qu’on change la couleur du fond.
Donc nous allons utiliser linear-gradient() pour faire un fond moitié bleu et moitié gris clair, et déplacer le fond avec background-position selon l’état du bouton (oui je sais, pourquoi faire simple alors qu’on peut faire compliqué ^^). Et ce n’est pas tout. Notre image de fond générée avec le dégradé, va occuper 100% de la largeur du bouton. Or, si on la déplace avec background-position, on va avoir « un trou », car elle va garder la même taille. Donc, il faut prévoir une image plus large dès le début! Pour cela, nous utilisons background-size pour donner au fond une largeur de 120% et garder la hauteur à 100%. Ensuite on pourra jouer avec la position du fond. On va utiliser des multiples backgrounds aussi, car en plus du bleu et du gris clair, il y a aussi un reflet en bas du bouton. Du coup, le background-size s’appliquera aux 2 (à moins de préciser des multiples background-size, ce qui est inutile dans notre cas).

686970717273747576777879808182

#foo:checked,
.iOS input[type="checkbox"]+label:before {
	background-color: rgb(53,126,247);
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255,255,255,.2)), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,.1))), -webkit-gradient(linear, 0% 0%, 100% 0%, from(rgb(53,126,247)), to(rgb(247,247,247)), color-stop(0.5, rgb(53,126,247)), color-stop(0.5, rgb(247,247,247)));
	background: -webkit-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%), -webkit-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:    -moz-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),    -moz-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:      -o-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),      -o-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:         linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),         linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	-webkit-background-size:120% 100%;
	-moz-background-size:	120% 100%;
	-o-background-size:	120% 100%;
	background-size:	120% 100%;
	background-position: 0 0, -14px 0;
	background-repeat: no-repeat;
}

Quatrième étape, transition CSS.

On va animer la couleur de texte et la position du fond.

68697071727374

#foo:checked,
.iOS input[type="checkbox"]+label:before {
	-webkit-transition:	color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	-moz-transition:	color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	-o-transition:		color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	transition:		color 0.2s ease-in-out, background-position 0.2s ease-in-out;
}

Cinquième étape, l’état « activé »

C’est vrai que je ne vous ai pas encore expliqué comment on va faire :) En fait c’est tout simple, on va se servir de l’état checked de la checkbox. Ainsi, input+label permet de styler le label quand la checkbox est décochée, et input:checked+label permet de styler le label quand la checkbox est cochée.
Donc pour styler notre fond de bouton quand il est « activé » on fait :

110111112113

.iOS input[type="checkbox"]:checked+label:before {
	color: #fff;
	background-position: 0 0, 0 0;
}

[update]

Sixième étape, l’état « focus »

Comme la navigation au clavier est maintenant activée, il faut penser à l’état « focus ». Pour le mettre en avant, je vous propose de modifier box-shadow afin de créer une bordure de 4px autour du bouton. A noter que cette fois il n’est pas nécessaire de rajouter #foo:checked, car nous allons cibler le pseudo-élément :before, or, sans la propriété content cette déclaration n’aura aucun effet sur les vieux navigateurs (la propriété content a déjà été déclarée ET filtrée précédemment).

114115116117118119

.iOS input[type="checkbox"]:focus+label:before {
	-webkit-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	-moz-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	-o-box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
}

Le fond du bouton au complet

068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119

#foo:checked,
.iOS input[type="checkbox"]+label:before {
	content: 'I000a0000a0000a0000a0000a0000a0O';
	position: absolute;
	right: 0;
	bottom: 50%;
	margin-bottom: -10px;
	width: 66px;
	height: 20px;
	padding-left: 4px;
	line-height: 22px;
	font-size: 13px;
	font-weight: bold;
	text-align: center;
	letter-spacing: 0;
	color: rgb(132,132,132);
	text-shadow: none;
	background-color: rgb(53,126,247);
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255,255,255,.2)), color-stop(0.5, transparent), color-stop(0.5, rgba(255,255,255,.1))), -webkit-gradient(linear, 0% 0%, 100% 0%, from(rgb(53,126,247)), to(rgb(247,247,247)), color-stop(0.5, rgb(53,126,247)), color-stop(0.5, rgb(247,247,247)));
	background: -webkit-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%), -webkit-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:    -moz-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),    -moz-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:      -o-linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),      -o-linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	background:         linear-gradient(top, transparent 50%, rgba(255,255,255,.1) 50%, rgba(255,255,255,.2) 100%),         linear-gradient(left, rgb(53,126,247) 50%, rgb(247,247,247) 50%);
	-webkit-background-size:120% 100%;
	-moz-background-size:	120% 100%;
	-o-background-size:	120% 100%;
	background-size:	120% 100%;
	background-position: 0 0, -14px 0;
	background-repeat: no-repeat;
	-webkit-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-moz-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-o-box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(0,0,0,.3);
	-webkit-border-radius:	3px;
	-moz-border-radius:	3px;
	-o-border-radius:	3px;
	border-radius:		3px;
	-webkit-transition:	color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	-moz-transition:	color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	-o-transition:		color 0.2s ease-in-out, background-position 0.2s ease-in-out;
	transition:		color 0.2s ease-in-out, background-position 0.2s ease-in-out;
}
.iOS input[type="checkbox"]:checked+label:before {
	color: #fff;
	background-position: 0 0, 0 0;
}
.iOS input[type="checkbox"]:focus+label:before {
	-webkit-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	-moz-box-shadow:	inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	-o-box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
	box-shadow:		inset 0 2px 4px rgba(0,0,0,.6), 0 0 0 4px rgba(0,0,0,.6);
}

Le curseur mobile

Là, si vous avez compris la partie précédente, il n’y aura pas de problème pour le curseur :)
En résumé : un fond en léger dégradé (gris vers un gris un peu plus clair du haut vers le bas), des ombres (qui vont changer selon l’état du bouton pour ne pas trop dépasser sur les côtés gauche et droit du bouton car je suis perfectionniste), coins arrondis, et on anime la propriété « right ».

120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154

#foo:checked,
.iOS input[type="checkbox"]+label:after {
	content: '';
	position: absolute;
	right: 40px;
	bottom: 50%;
	margin-bottom: -10px;
	width: 30px;
	height: 20px;
	background-color: rgb(205,205,205);
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(205,205,205)), to(rgb(254,254,254)));
	background: -webkit-linear-gradient(top, rgb(205,205,205), rgb(254,254,254));
	background:    -moz-linear-gradient(top, rgb(205,205,205), rgb(254,254,254));
	background:      -o-linear-gradient(top, rgb(205,205,205), rgb(254,254,254));
	background:         linear-gradient(top, rgb(205,205,205), rgb(254,254,254));
	-webkit-box-shadow:	inset 0 1px 1px rgba(255,255,255,.7), 1px 0 2px rgba(0,0,0,.6), -1px 0 1px rgba(0,0,0,.6);
	-moz-box-shadow:	inset 0 1px 1px rgba(255,255,255,.7), 1px 0 2px rgba(0,0,0,.6), -1px 0 1px rgba(0,0,0,.6);
	-o-box-shadow:		inset 0 1px 1px rgba(255,255,255,.7), 1px 0 2px rgba(0,0,0,.6), -1px 0 1px rgba(0,0,0,.6);
	box-shadow:		inset 0 1px 1px rgba(255,255,255,.7), 1px 0 2px rgba(0,0,0,.6), -1px 0 1px rgba(0,0,0,.6);
	-webkit-border-radius:	3px;
	-moz-border-radius:	3px;
	-o-border-radius:	3px;
	border-radius:		3px;
	-webkit-transition:	right 0.2s ease-in-out;
	-moz-transition:	right 0.2s ease-in-out;
	-o-transition:		right 0.2s ease-in-out;
	transition:		right 0.2s ease-in-out;
}
.iOS input[type="checkbox"]:checked+label:after {
	right: 0;
	-webkit-box-shadow:	inset 0 1px 1px rgba(255,255,255,.7), 1px 0 1px rgba(0,0,0,.6), -1px 0 2px rgba(0,0,0,.6);
	-moz-box-shadow:	inset 0 1px 1px rgba(255,255,255,.7), 1px 0 1px rgba(0,0,0,.6), -1px 0 2px rgba(0,0,0,.6);
	-o-box-shadow:		inset 0 1px 1px rgba(255,255,255,.7), 1px 0 1px rgba(0,0,0,.6), -1px 0 2px rgba(0,0,0,.6);
	box-shadow:		inset 0 1px 1px rgba(255,255,255,.7), 1px 0 1px rgba(0,0,0,.6), -1px 0 2px rgba(0,0,0,.6);
}

Conclusion

Un tout petit bémol : nous appliquons les transitions CSS sur les pseudo-éléments :before et :after. Malheureusement, seul Firefox 4 gère les transitions sur ces pseudo-éléments pour l’instant. Les autres navigateurs modernes gèrent bien les transitions, mais seulement sur les balises « classiques ». Mais j’ai bon espoir que ceux-ci corrigent ce bug assez rapidement, surtout quand on voit la vitesse à laquelle Chrome passe de version en version. Safari aura bientôt droit à sa nouvelle version également, il ne reste plus qu’à prier ;) Quant à Opera, les mises à jour se succèdent assez rapidement mais je ne suis pas son évolution, vue sa part d’utilisation très faible.

Néanmoins, même si ces transitions ne sont pas encore disponibles, ça n’empêche pas notre bouton de très bien se comporter :) En plus, les navigateurs obsolètes, même s’ils ne peuvent pas en profiter, ne sont pas impactés.

Allez, pour le plaisir, voilà ce que ça donne avec le zoom au maximum sur Firefox :
iOS checkbox - Zoom
Pas mal non?

See ya!

PS : j’ai prévu deux autres tutoriels dans le même genre en suivant ;)

[update] Article originalement publié le 22 mai 2011.