Des select personnalisés en CSS

Comme convenu il y a 3 ans (hum…) voici un petit tuto pour personnaliser en CSS (et sans javascript) les menus déroulants des formulaires, l’élément select.

Un peu de code HTML

Tout d’abord voyons comment écrire le code. Il va falloir encapsuler le select dans un conteneur, plutôt deux en fait : le premier .formfield-select contiendra le label et le conteneur du select, le second .formfield-select--container contiendra le select.

Voici le code complet :

<div class="formfield-select">

<label for="mon_select" >Choisir un truc</label>

<div class="formfield-select–container">

<select id="mon_select">
<option>Faire des travaux</option>
<option>Cuisiner des légumes</option>
<option>Tondre la pelouse</option>
<option>Manger des nouilles</option>
<option>Se lever tard</option>
<option>Trouver l’ouverture facile</option>
</select>

</div>

</div>

On n’oubliera pas l’attribut for sur le label pour le lier à l’ID du select.

Et un peu de CSS

Au niveau du CSS nous allons tout d’abord définir les deux conteneurs :

.formfield-select {
position: relative;
}

.formfield-select–container {
position: relative;

background-color: #fff;
border: #777 1px solid;
margin: 0 0 1.5em 0;

overflow: hidden;
/*
Le select natif pourra
dépasser sans être vu
*/

Ensuite, nous allons supprimer les styles par défaut du select à l’aide de la propriété appearance: none, mais aussi le faire dépasser du conteneur afin de masquer les icones flèches natives (pour les navigateurs qui ne prennent pas en charge la propriété précédente.

.formfield-select–container select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;

width: 110%;
/*
On est sûr de ne plus voir
la flèche native
*/

height: auto;
border: 0;
margin: 0;
padding: .75em;
border-radius: 0;


overflow: hidden;
text-overflow: ellipsis;
/*
On empêche le texte d’aller
jusqu’au bout s’il est trop long
*/

Et enfin, nous allons ajouter une icone de pointeur à la place des icones natives. Nous allons styler le pseudo-element ::after du conteneur pour créer un petit triangle.

.formfield-select--container::after { 
/* Le pointeur du select */
content: '';
position: absolute;
top: 50%;
margin-top: -3px;
right: .75em;
display: block;
width: 0; height: 0;
border-color: transparent;
border-top-color: #444;
border-width: 6px;
border-style: solid;
pointer-events: none;
}

Et voilà, notre select est personnalisé sans une once de javascript 🙂 L’autre avantage de cette technique est que ce select reste parfaitement accessible.

capture d'écran du select stylé en css

Voir la démo

J’utilise cette technique dans mon starter theme WordPress : From Scratch, disponible sur Github. Ce petit tuto vient enfin compléter celui sur les checkbox et boutons radio personnalisés en CSS (même si une petite mise à jour serait bienvenue, pour créer les éléments graphiques sans image, uniquement à l’aide des pseudo-elements).

Télécharger les fichiers sources

Ils en parlent

  1. Dorian

    Salut,

    Attention car tu sembles partir du principe que la flèche est située à droite sur tout `select`. (cf. le `width: 110%;` que tu utilises pour la cacher).
    Certaines langues se lisent pourtant de droite à gauche, auquel cas la flèche ne se situera guère à droite. Dans ce cas précis, ta technique donnerait un résultat différent de ce que tu expliques ici pour nous autres FR, et faux.

    Néanmoins je ne pense pas qu’il y ait de technique miracle qui couvre correctement ce sujet. Peut-être qu’il y aurait moyen de détecter la langue et de nuancer la tienne ?

    Bien cdt,

    • Hello,
      Oui, pour détecter les langues tu as des attributs à appliquer à la balise HTML : `lang`, `dir`…
      Pour l’exemple que tu donnes, une langue qui se lit de droite à gauche, tu ajoutes l’attribut `dir` avec comme valeur RTL (right to left) : dir=’rtl’
      Ensuite en CSS tu peux alors inverser le placement de la flèche en fonction de cet attribut 🙂

  2. Sbamonom

    Merci de partager ton savoir.
    Cependant une question me taraude : en tant que mec qui gère en html css, pourquoi avoir fait un truc moche dur à lire sans scroller les parties de ‘code’ ?

    Mais sincèrement, merci (pas d’ironie malgré ma remarque de c*****d)

    • Hello, de rien. Oui c’est vrai que les aperçus de code sont pas top… c’est ça de faire le malin avec un site boxé en 640 pixels de largeur ^^ Faudrait que j’essaye Codepen la prochaine fois. Là, le mieux c’est encore d’aller sur la page démo et d’inspecter le code avec le navigateur 🙂

  3. David

    Le site web n’utilise peut-être même pas 30% de la largeur disponible ! Pénible à scroller pour avoir les infos… qui restent toutefois très utiles.

  4. Nagrad

    Bonjour,

    Avez-vous des solutions pour la modification de l’apparence du menu déroulant des attributs – déclinaisons des produits sous Prestashop 1.6.1.6 ? Le but est de modifier la hauteur des champs select ainsi que de supprimer le cadre noir qui entoure les champs parcourus.

    Quels sont les fichiers qui gèrent ça ?

    Par avance merci,

    • Bonjour, je n’ai travaillé que 2 fois avec Prestashop et les 2 fois ce fut un calvaire… je ne saurais pas quoi te conseiller. Si les sélecteurs ne sont pas générés par du JS alors tu dois pouvoir écraser certaines règles CSS pour faire passer le style que tu veux. Maintenant, l’astuce de cet article de fonctionne que si le markup HTML est similaire ou si tu peux le modifier !

  5. Fabien

    Au top ! Tu m’a fait gagné un temps précieux.

    Par contre j’ai pu relevé une petite erreurs de frappe dans le code que tu fourni :
    « ::after » en « :after »

    j’ai du passer « background-color: #fff; » dans le « .formfield-select–container select » pour que mon select soit a fond blanc.

    merci pour le tuto.

    Et j’ai une petite question (autres) : Comemnt tu met le fondu/ degradé en haut de ton site, des qu’on commence a scrollé vers le bas ?

    Merci par avance

    • De rien 😀
      Pour les pseudo-elements ::before, ::after, ::first-letter… on utilise deux fois deux points (::) pour les différencier des pseudo-classes :hover, :active… 🙂

    • Ah et pour les dégradés sur le site, je détecte en JS quand on commence à scroller et quand on est en bas de l’écran, j’ajoute alors une classe sur la div wrapper qui affiche les dégradés sur des ::after/::before 🙂

On en discute ?

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *