Dans la lignée de mon précédent article Personnaliser les checkbox en CSS façon iOS, nous allons utiliser le même principe pour personnaliser des checkbox et des boutons radio sans image, en n’utilisant que du CSS (CSS3 principalement), mais en gardant un aspect boite à cocher et bouton radio cette fois.
Nous partons avec les mêmes handicaps imposés, à savoir : aucune image, et un balisage html réduit à son minimum : l’input et son label.
Marquage HTML
Aujourd’hui je vous propose 3 exemples pour les boites à cocher, et 1 pour les boutons radio. Voici donc la structure de départ :
0102030405060708091011121314
<ul class="choices-border">
<li><input type="checkbox" name="check-1" id="check-1" /><label for="check-1">Choix 1</label></li>
<li><input type="checkbox" name="check-2" id="check-2" /><label for="check-2">Choix 2</label></li>
<li><input type="radio" name="radio-1" id="radio-1" /><label for="radio-1">Radio 1</label>
<input type="radio" name="radio-1" id="radio-2" checked="checked" /><label for="radio-2">Radio 2</label>
<input type="radio" name="radio-1" id="radio-3" /><label for="radio-3">Radio 3</label>
</li>
</ul>
<ul class="choices-text">
<li><input type="checkbox" name="check-3" id="check-3" /><label for="check-3">Choix 3</label></li>
</ul>
<ul class="choices-thin">
<li><input type="checkbox" name="check-4" id="check-4" /><label for="check-4">Choix 4</label></li>
</ul>
Base CSS commune
Dans la même veine que mon précédent article, nous allons masquer les inputs, et utiliser les pseudo éléments :before
et :after
du label pour créer respectivement le fond de nos « fausses inputs » et leur… Comment appeler ça au fait? Ha oui, je l’ai : leur machin-qui-indique-qu’elles-sont-activées… >_> Bon ok c’est trop long, on va dire « coche » alors.
On aura donc toute une base CSS commune, les différences entre mes exemples seront au niveau de :after
.
[Aparté]
Au passage, j’en profite pour introduire l’unité rem. Pour ceux qui ne connaissent pas, c’est un peu un croisement entre le px et le em, et profitant du meilleur des 2 mondes. En fait, le rem est très proche de em, avec comme différence le « r » (non, ne me jetez pas les cailloux déjà, attendez) qui signifie « root » (racine) : em donne une taille relative au parent le plus proche (par exemple si le parent a une taille de caractères de 10px et que son élément fils a une taille de 1.5em, il en résulterait 15px), alors que rem donne une taille relative à la racine du document (balise html donc), ce qui pose beaucoup moins de problèmes au niveau des imbrications de balises.
Il y a bien sûr les adeptes du pixel, et ceux du em, on ne rentrera pas dans ce débat aujourd’hui bien sûr, ce n’est pas le but.
L’avantage du em (et donc du rem) est de tenir compte de la taille de caractère réglée dans le navigateur du visiteur. Par défaut, cette taille de caractère est de 16px. Donc, avec les réglages par défaut, et pour simplifier, 1em = 1rem = 16px. Du coup, 1rem = 0.625px, ça commence à se compliquer :/ Mais, il y a bien sûr une astuce pour simplifier tout ça, pour en savoir plus je vous conseille le très bon article Font sizing with rem [en], où Jonathan Snook expose sa méthode pour, à la fois simplifier grandement les calculs tout en évitant d’avoir 4 chiffres derrière la virgule, et aussi pour tenir compte des navigateurs n’ayant pas encore implémenté cette unité.
Pour résumer sa méthode, on fixe le font-size à 62.5% sur la balise <html>
(16px x 62.5% = 10px). Maintenant nous avons donc notre « root » à 10px, donc 1rem = 10px, donc 1px = 0.1rem. C’est quand même beaucoup plus pratique à utiliser :)
Pour la compatibilité navigateurs, je suppose que IE9 l’a implémenté, il reste Opera qui ne l’a pas encore fait pour l’heure. Il faut donc prévoir un fallback en pixels :
1234
a.foo {
font-size: 16px;
font-size: 1.6rem;
}
Bien sûr, cette unité ne se limite pas à la taille de caractère, mais peut être utilisée pour dimensionner des éléments.
[/Aparté]
Masquer les inputs
Même chose que dans le tutoriel précédent : on masque les inputs seulement sur les navigateurs récents, tout en conservant la navigation au clavier.
0102030405060708091011121314151617
input[type="checkbox"]:checked,
input[type="checkbox"]:not(:checked),
input[type="radio"]:checked,
input[type="radio"]: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
Le seul point à noter est padding-left
, c’est dans cet espace que viendront se loger nos fake inputs.
1819202122232425262728
#foo:checked:before,
input[type="checkbox"]+label,
input[type="radio"]+label {
float: left;
padding-left: 26px;
padding-left: 2.6rem;
margin-right: 16px;
margin-right: 1.6rem;
position: relative;
cursor: pointer;
}
Le fond de nos fausses inputs
Nos inputs sont positionnées en absolu à gauche de leur label, des coins arrondis avec border-radius
, un fond dégradé blanc de 40% d’opacité en haut vers 90% en bas, et des ombres à l’intérieur (inset
) et à l’extérieur avec box-shadow
pour donner de volume. La seule différence entre les checkbox et les boutons radio est au niveau de border-radius
.
29303132333435363738394041424344454647484950515253545556575859606162636465666768
#foo:checked,
input[type="checkbox"]+label:before,
input[type="radio"]+label:before {
content: '';
position: absolute;
left: 0;
top: 50%;
margin-top: -9px;
margin-top: -.9rem;
width: 16px;
width: 1.6rem;
height: 16px;
height: 1.6rem;
background-color: rgba(255,255,255,.7);
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255,255,255,.4)), to(rgba(255,255,255,.9)));
background: -webkit-linear-gradient(top, rgba(255,255,255,.4), rgba(255,255,255,.9));
background: -moz-linear-gradient(top, rgba(255,255,255,.4), rgba(255,255,255,.9));
background: -o-linear-gradient(top, rgba(255,255,255,.4), rgba(255,255,255,.9));
background: linear-gradient(top, rgba(255,255,255,.4), rgba(255,255,255,.9));
-webkit-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1);
-moz-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1);
-o-box-shadow: inset 1px 1px 2px rgba(0,0,0,.3), inset 0 0 0 1px rgba(0,0,0,.1);
-o-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1);
box-shadow: inset 1px 1px 2px rgba(0,0,0,.2), inset 0 0 0 1px rgba(0,0,0,.1);
box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1);
-webkit-border-radius: .3rem;
-moz-border-radius: .3rem;
-o-border-radius: 3px;
-o-border-radius: .3rem;
border-radius: 3px;
border-radius: .3rem;
}
input[type="radio"]+label:before {
-webkit-border-radius: .8rem;
-moz-border-radius: .8rem;
-o-border-radius: 8px;
-o-border-radius: .8rem;
border-radius: 8px;
border-radius: .8rem;
}
Montrer le focus
On va juste rajouter une bordure grise autour de nos fausses inputs pour montrer le focus, en tenant compte de la valeur précédente de box-shadow
bien sûr.
697071727374757677
input[type='checkbox']:focus + label:before,
input[type='radio']:focus + label:before {
-webkit-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1), 0 0 0 .3rem rgba(0,0,0,.4);
-moz-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1), 0 0 0 .3rem rgba(0,0,0,.4);
-o-box-shadow: inset 1px 1px 2px rgba(0,0,0,.3), inset 0 0 0 1px rgba(0,0,0,.1), 0 0 0 3px rgba(0,0,0,.4);
-o-box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1), 0 0 0 .3rem rgba(0,0,0,.4);
box-shadow: inset 1px 1px 2px rgba(0,0,0,.2), inset 0 0 0 1px rgba(0,0,0,.1), 0 0 0 3px rgba(0,0,0,.4);
box-shadow: inset .1rem .1rem .2rem rgba(0,0,0,.3), inset 0 0 0 .1rem rgba(0,0,0,.1), 0 0 0 .3rem rgba(0,0,0,.4);
}
Transitions CSS
Nous mettons en place une transition
sur la coche (pseudo-élément :after), qui va agir sur la propriété transform
, elle servira lors du clic sur le label (le machin-qui-indique-qu’elles-sont-activées va apparaitre en grossissant et disparaitre en rapetissant) et lors du survol avec la souris.
787980818283
input+label:after {
-webkit-transition: -webkit-transform 0.2s ease-in-out;
-moz-transition: -moz-transform 0.2s ease-in-out;
-o-transition: -o-transform 0.2s ease-in-out;
transition: transform 0.2s ease-in-out;
}
Checkbox version « traits verts »
Notre coche va être positionnée en absolu, comme le fond. Pour la forme, il s’agit d’un bloc transparent comportant une bordure verte en bas et à gauche. A noter la différence d’épaisseur entre les 2 bordures, c’est pour compenser la déformation ultérieure.
084085086087088089090091092093094095096097098099100101102103104105106107
#foo:checked,
.choices-border input[type="checkbox"]+label:after {
content: '';
position: absolute;
left: 7px;
left: .7rem;
top: 50%;
margin-top: -4px;
margin-top: -.4rem;
width: 14px;
width: 1.4rem;
height: 6px;
height: .6rem;
border-style: solid;
border-color: #01C30C;
border-width: 0 0 2px 3px;
border-width: 0 0 .2rem .3rem;
-webkit-box-shadow: -.1rem .1rem .1rem 0 rgba(0,0,0,.4);
-moz-box-shadow: -.1rem .1rem .1rem 0 rgba(0,0,0,.4);
-o-box-shadow: -1px 1px 1px 0 rgba(0,0,0,.4);
-o-box-shadow: -.1rem .1rem .1rem 0 rgba(0,0,0,.4);
box-shadow: -1px 1px 1px 0 rgba(0,0,0,.4);
box-shadow: -.1rem .1rem .1rem 0 rgba(0,0,0,.4);
}
A ceci nous ajoutons transform
. Nous avons notre sorte de « L » (sauf qu’il est plus large que haut), il faut d’abord fixer le point d’origine des transform
, c’est très important pour les transitions. Nous fixons donc transform-origin
à « 0 100% », c’est à dire à l’intersection des 2 bords. Ensuite, on utilise skewX(-30deg)
pour déformer la coche (d’où la bordure plus épaisse à gauche), ça aura pour effet de tirer le haut de notre coche vers la droite (on obtient une sorte de « V » couché sur la droite), puis une rotation dans le sens inverse des aiguilles d’une montre avec rotate(-40deg)
. Pour finir, notre coche ne doit pas être visible à l’état « normal », on utilise donc scale(0)
.
108109110111112113114115116117118
#foo:checked,
.choices-border input[type="checkbox"]+label:after {
-webkit-transform: rotateZ(-40deg) skewX(-30deg) scale(0);
-moz-transform: rotate(-40deg) skewX(-30deg) scale(0);
-o-transform: rotate(-40deg) skewX(-30deg) scale(0);
transform: rotate(-40deg) skewX(-30deg) scale(0);
-webkit-transform-origin: 0 100%;
-moz-transform-origin: 0 100%;
-o-transform-origin: 0 100%;
transform-origin: 0 100%;
}
État coché
Nous passons juste scale()
à 1.
119120121122123124
.choices-border input[type="checkbox"]:checked+label:after {
-webkit-transform: rotateZ(-40deg) skewX(-30deg) scale(1);
-moz-transform: rotate(-40deg) skewX(-30deg) scale(1);
-o-transform: rotate(-40deg) skewX(-30deg) scale(1);
transform: rotate(-40deg) skewX(-30deg) scale(1);
}
État hover
On augmente la taille de la coche de 30% au survol de la souris.
125126127128129130
.choices-border input[type="checkbox"]:checked+label:hover:after {
-webkit-transform: rotateZ(-40deg) skewX(-30deg) scale(1.3);
-moz-transform: rotate(-40deg) skewX(-30deg) scale(1.3);
-o-transform: rotate(-40deg) skewX(-30deg) scale(1.3);
transform: rotate(-40deg) skewX(-30deg) scale(1.3);
}
Et voilà, nous avons terminé la première version « traits verts ».
Checkbox version « thin »
Une version quasi-similaire à la précédente, donc je vais vous donner le code d’un seul coup. Les différences : les 2 bordures seront en noir avec opacité à 40%, la coche sera plus petite et ne dépassera pas de son fond, pas de déformation avec scale()
, juste une rotation à 45 degrés.
131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
#foo:checked,
.choices-thin input[type="checkbox"]+label:after {
content: '';
position: absolute;
left: 7px;
left: .7rem;
top: 50%;
margin-top: -2px;
margin-top: -.2rem;
width: 8px;
width: .8rem;
height: 4px;
height: .4rem;
border-style: none none solid solid;
border-color: transparent transparent rgba(0,0,0,.4) rgba(0,0,0,.4);
border-width: 0 0 2px 2px;
border-width: 0 0 .2rem .2rem;
-webkit-transform: rotate(-45deg) scale(0);
-moz-transform: rotate(-45deg) scale(0);
-o-transform: rotate(-45deg) scale(0);
transform: rotate(-45deg) scale(0);
-webkit-transform-origin: 0 100%;
-moz-transform-origin: 0 100%;
-o-transform-origin: 0 100%;
transform-origin: 0 100%;
}
.choices-thin input[type="checkbox"]:checked+label:after {
-webkit-transform: rotate(-45deg) scale(1);
-moz-transform: rotate(-45deg) scale(1);
-o-transform: rotate(-45deg) scale(1);
transform: rotate(-45deg) scale(1);
}
.choices-thin input[type="checkbox"]:checked+label:hover:after {
-webkit-transform: rotate(-45deg) scale(1.2);
-moz-transform: rotate(-45deg) scale(1.2);
-o-transform: rotate(-45deg) scale(1.2);
transform: rotate(-45deg) scale(1.2);
}
Checkbox version « texte »
Cette fois nous n’allons pas utiliser la propriété border
pour faire notre coche, mais simplement le caractère « v » avec l’attribut content
. Histoire de donner un style manuscrit à notre « v », on lui applique la fonte « Comic Sans MS » (HOOOUUUUUU honte à moi!). A part ça, la méthode reste presque la même que pour les autres versions, on remplace rotate()
par un skewY()
, et on modifie transform-origin()
.
169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
#foo:checked,
.choices-text input[type="checkbox"]+label:after {
content: 'v';
position: absolute;
left: 2px;
left: .2rem;
top: 50%;
margin-top: -17px;
margin-top: -1.7rem;
width: 14px;
width: 1.4rem;
font-family: "Comic Sans MS", cursive;
color: #333;
font-size: 24px;
font-size: 2.4rem;
-webkit-transform: skewX(-25deg) skewY(-40deg) scale(0);
-moz-transform: skewX(-25deg) skewY(-40deg) scale(0);
-o-transform: skewX(-25deg) skewY(-40deg) scale(0);
transform: skewX(-25deg) skewY(-40deg) scale(0);
-webkit-transform-origin: 50% 100%;
-moz-transform-origin: 50% 100%;
-o-transform-origin: 50% 100%;
transform-origin: 50% 100%;
}
.choices-text input[type="checkbox"]:checked+label:after {
-webkit-transform: skewX(-25deg) skewY(-40deg) scale(1);
-moz-transform: skewX(-25deg) skewY(-40deg) scale(1);
-o-transform: skewX(-25deg) skewY(-40deg) scale(1);
transform: skewX(-25deg) skewY(-40deg) scale(1);
}
.choices-text input[type="checkbox"]:checked+label:hover:after {
-webkit-transform: skewX(-25deg) skewY(-40deg) scale(1.3);
-moz-transform: skewX(-25deg) skewY(-40deg) scale(1.3);
-o-transform: skewX(-25deg) skewY(-40deg) scale(1.3);
transform: skewX(-25deg) skewY(-40deg) scale(1.3);
}
Boutons radio
Cette fois c’est moins compliqué pour faire la coche puisqu’il s’agit d’un disque. Hauteur et largeur sont donc identiques, border-radius
est fixé à 50% (un fallback en rem est prévu pour Safari qui n’aime pas trop les pourcentages, je ne sais pour quelle raison). Ensuite, tout est une histoire de couleur de fond et de box-shadow
. La seule transformation que nous gardons est scale()
pour conserver la transition. Cette fois, inutile de préciser un transform-origin
puisque nous souhaitons nous positionner au centre du disque, soit « 50% 50% », c’est à dire la valeur par défaut.
205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
#foo:checked,
input[type="radio"]+label:after {
content: '';
position: absolute;
left: 2px;
left: .2rem;
top: 50%;
margin-top: -7px;
margin-top: -.7rem;
width: 12px;
width: 1.2rem;
height: 12px;
height: 1.2rem;
background-color: #FD6800;
-webkit-border-radius: .6rem;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
-webkit-box-shadow: inset 0 .1rem .2rem rgba(255,255,255,.6), inset 0 -.1rem .1rem 0 rgba(0,0,0,.5), 0 0 .3rem #000;
-moz-box-shadow: inset 0 .1rem .2rem rgba(255,255,255,.6), inset 0 -.1rem .1rem 0 rgba(0,0,0,.5), 0 0 .3rem #000;
-o-box-shadow: inset 0 1px 2px rgba(255,255,255,.6), inset 0 -1px 1px 0 rgba(0,0,0,.5), 0 0 3px #000;
-o-box-shadow: inset 0 .1rem .2rem rgba(255,255,255,.6), inset 0 -.1rem .1rem 0 rgba(0,0,0,.5), 0 0 .3rem #000;
box-shadow: inset 0 1px 2px rgba(255,255,255,.6), inset 0 -1px 1px 0 rgba(0,0,0,.5), 0 0 3px #000;
box-shadow: inset 0 .1rem .2rem rgba(255,255,255,.6), inset 0 -.1rem .1rem 0 rgba(0,0,0,.5), 0 0 .3rem #000;
-webkit-transform: scale(0);
-moz-transform: scale(0);
-o-transform: scale(0);
transform: scale(0);
}
input[type="radio"]:checked+label:after {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-o-transform: scale(1);
transform: scale(1);
}
input[type="radio"]:checked+label:hover:after {
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3);
}
Le travail est fini! :)
See ya!
Commentaires
Commentaire de Raphaël.
Bonjour :)
Joli tuto !
Même si je ne suis pas habitué à utiliser des propriété comme skewX ou scale (j’apprends :) ) j’ai une piste pour la compatibilité iphone.
Pour que le label soit cliquable, il faut rajouter l’attribut « onclick » dans la balise label, et c’est tout ;)
PS : pour la checkbox choix3 il y a une problème de police avec le V sur iphone :)
Commentaire de mackk2.
Wow ca marche du tonnerre. Merci pour le tuto.
Par contre j’essaie de rajouter une checkbox « façon iOS » comme dans ton ancien tuto pour avoir la checkbox iOS et juste a coté la Checkbox version « traits verts ». Mais pas moyen qu’une checkbox en coche 2 en même temps on dirait, peut être avec du javascript?
Merci
Commentaire de Greg.
@Raphaël
Oui je suppose qu’il n’y a que ça pour iOS, mais ça enlève un peu le « charme » du full-css.
@mackk2
Oui c’est faisable avec du javascript.
Commentaire de mackk2.
Ok merci.
Dommage j’aurais voulu afficher une croix ou une coche tout en css.
Commentaire de Greg.
@mackk2
Tu peux à condition d’ajouter une classe à chaque input (et appliquer le style désiré à la classe voulue), c’est pour cocher les 2 en même temps qu’il te faudra du javascript (ça pourrait être simulé en CSS mais il n’y en aurait qu’une de réellement cochée).
Au passage, teste bien que tout ça marche bien sur tous les navigateurs, je ne conseille pas trop d’utiliser ceci sur un site en production : Safari mobile pose problème (il faudra lui ajouter du javascript) ainsi que IE9 (et n’ayant pas ce navigateur je ne peux pas rectifier mon code).
A+
PS : perso j’utiliserais les classes conditionnelles et pourquoi pas modernizr pour ne pas appliquer ces styles sur IE9.
Commentaire de mackk2.
Ok merci je vais essayer ca:-)
Commentaire de Icz.
Merci beaucoup pour ces superbes checkbox ♥
Commentaire de kopax.
Bonjour,
Merci beaucoup pour ce tuto, ces checkbox/radio sont vraiment sympa.
J’avais besoins de customiser les buttons sur un site fais en em qui doit s’adapter au format des différentes tablettes, je suis tomber ici en cherchant comment agrandir un checkbox (ba oui j’avais jamais essayé avant ^^)
En tout cas c’est vraiment super, mais je suis étonné, que personne ai le même bug que moi avec les checkbox.
J’ai modifié ta feuille de style et virer toute les css en px, et modifier tes rem en em.
L’affichage est niquel, seulement en clickant plein de fois d’un checkbox à l’autre, j’arrive à faire disparaitre les checkbox (??)
J’utilise firefox 14.0.1 for Ubuntu canonical
Par contre, sur chromium, aucun soucis.
Commentaire de kopax.
Pour info je viens de tester sur chrome et firefox windows, ce bug a également lieu dans firefox windows.
Si dans firebug je viens a cliqué sur l’input il réapparait, mais dans l’état je pense que ces checkbox ne sont pas utilisable. Qu’en pensez vous ?
Commentaire de Greg.
Si tu parle des vrais checkbox, elles ne devraient même pas apparaitre puisque c’est le label qui les simule (mais je pense que tu le savais).
De mon côté je n’ai pas de problème avec le dernier Firefox sur Mac, donc je suppose que ça vient de tes modifications (à moins que ça le fasse aussi sur la démo ici ?).
Commentaire de kopax.
Je ne parles pas des vrais checkbox, on ne les vois jamais :)
Je n’ai pas modifier la sémantique html, donc à priori, il n’y a aucune raison.
Je pense déjà avoir identifier le problème.
Lorsque tu crée ta liste dans mon exemple avec des mois de l’année :
123456
Si je ne crée que un seul li, et que je met tout les checkbox à la suite, il n’y aura aucun bug d’affichage, les checkbox seront affiché en inline dans le li.
Par contre lorsque je crée autant de li que de checkbox, je ne comprends pas encore pourquoi et je me pencherai demain au boulot, mais je ne peux pas mettre le li en display block. (l’affichage sera inline mais avec un jolie bug)
Du coup et seulement sur firefox, des checkbox + label peuvent disparaitre.
Toi dans ta demo, tu as bien une liste pour chaque checkbox. Mais ton li se comporte bien en block.
Pour info, je n’ai aucune règle css sur ce li.
Du coup, aurai tu une idée du pourquoi ta feuille de style m’empeche de regler mon li ?
Commentaire de Greg.
Les labels sont en float: left; est-ce que ça n’aurait pas un lien ?
PS : le premier coup était bon pour
<pre>
, mais à l’intérieur il faut écrire les balises avec & lt; et & gt; ;) (du coup, tu me permet de supprimer ton commentaire suivant ? (puisque ça a foiré aussi ^^)