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 infinitX.
Merci pour ta réponse.
« voir l’appartée en début d’article. »
D’accord, je croyais qu’il était possible de cibler cette échelle uniquement sur le #foo
« serait-ce plus du paluchage avec CSS3 ? ^^ »
Certainement ! Mais quand on voit ça comparé aux boutons d’origine, y’a pas photo ! Et puis, c’est ces détails qui font la différence non ? :)
« Avec quel navigateur ? »
Chrome 20 et Firefox 12.
« Oui, mais ça enlève tout intérêt à la méthode =D »
C’est vrai, mais les rendus sont encore trop divergents d’un navigateur à l’autre. Si je peux obtenir ce même résultat avec des images, compatible tous navigateurs, ça me conviendrait parfaitement. Je vais essayer de trouver ça ;)
Commentaire de mattou21.
Superbe travail, bravo! Je confirme cependant que cela (la démo en tout cas) sous IE 9.
Commentaire de Greg.
Que cela…
– ne marche pas ?
– ne fonctionne pas ?
– foire ?
– part en cou*** ?
(cocher la/les bonne(s) réponse(s))
^^
Commentaire de Geoffrey @ Geoffrey.Crofte.fr.
Je pensais à un truc : appliquer ces styles en les appliquant via media queries, dans le genre :
@media screen and (min-device-width:20px) {
(le screen est optionnel si tu es déjà dans une CSS en media screen)/* styles des inputs */
}
Du coup tu appliques tes styles customs sur tous les navigateurs, toujours pas sur IE8 et inférieur, et IE9 n’est plus dérangé par la compréhension de la moitié des styles qui rendent les inputs incontrôlables.
Qu’en penses-tu ?
Commentaire de Greg.
Pas bête. Par contre inapplicable sur un site responsive donc ^^
Commentaire de Geoffrey @ Geoffrey.Crofte.fr.
En fait j’étais persuadé que IE9 ne comprenais pas min-device-width, mais je me suis planté… au temps pour moi :/
Bon ben il ne reste plus qu’à cibler précisément IE9 du coup, et annuler les styles pour lui :
<!--[if IE 9 ]> <html class="no-js ie9" lang="fr-FR"> <![endif]-->
(remplacer les slashes et anti-slashes par les chevrons correspondant)[Edit Greg] Ajout des chevrons (faignant va!).
Désolé pour le faux espoir :p
Commentaire de Greg.
Un jour, je m’achèterais un PC portable avec seven dessus pour pouvoir faire mes tests IE9 ^^ (mais ça fait ch*** de claquer autant de fric dans cette chose).
Commentaire de Geoffrey @ Geoffrey.Crofte.fr.
Mais non mais si je mettais des < (test) il allait forcément m’échapper automatiquement l’esperluette non ? #noob :p
Sinon tu installes une machine virtuelle avec Seven + IE9, ça doit bien exister quelque part ça non ?
Commentaire de Greg.
Nan, on peut utiliser les & lt; :)
Oui je pourrais utiliser une machine virtuelle mais :
– j’ai pas envie de « salir » mon mac avec du crosoft #troll,
– j’ai pas envie d’alourdir le système avec une machine virtuelle qui tourne pendant que je bosse.
Dans l’idéal, et quand j’aurais assez de fric, je ferais une concession : acheter un macbook air et installer une machine virtuelle dessus, histoire de pouvoir aussi me servir de cet ordi en déplacement au lieu d’avoir une brique qui ne sert qu’aux tests IE (mais faut quand même acheter seven >_<).
Commentaire de paul.
Merci pour ce tuto, c’est pile ce que je cherchais, je teste ça de suite!