Après des années à décevoir les élèves sur ce composant intouchable, après moult pleurs face aux maquettes trop ambitieuses des designers, nous avons été entendu. Les balises <select> sont personnalisables. Sans workaround compliqué, juste un peu de CSS et la possibilité d’y insérer le HTML que vous voulez !

Des opportunités assez intéressantes s’offrent à nous, tant en terme de design que d’accessibilité ! Alors chauffez vos doigts, on va mettre le paquet et découvrir tout ce qui nous est désormais possible de faire.

La structure de base de notre select

Pour commencer, créons juste notre base : un fichier HTML de base avec un select qu’on vient centrer rapidement :

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Select</title>
	<style>
		body {
			height: 100vh;
			display: flex;
			justify-content: center;
			align-items: center;
			background-color: #FAFAFA;
			font-family: sans-serif;
		}
	</style>
</head>
<body>
	<select class="super-fancy-select"></select>
</body>
</html>

Rien de bien fou pour le moment. Vous pouvez afficher votre rendu et pour l’instant vous tombez normalement sur quelque chose de bien commun.

Pimp my select

On va commencer par ajouter un peu de corps à tout ça. Ajoutons deux optgroups dans notre select, un pour afficher des développeurs, un pour afficher des designers. Ajoutons à chacun d’entre eux deux options.

Et là, l’interdit. Ce que vous vous êtes refusé durant toute votre carrière. Dans chaque option, nous allons venir ajouter une image (du picsum avec 4 tailles différentes pour bien avoir 4 images différentes) et un paragraphe pour afficher nos utilisateurs :

<select class="super-fancy-select">
	<optgroup label="Developpers">
		<option>
			<img alt="Freddie Schimmel" src="https://picsum.photos/40">
			<p>Freddie Schimmel</p>
		</option>
		<option>
			<img alt="Laura Reichel" src="https://picsum.photos/41">
			<p>Laura Reichel</p>
		</option>
	</optgroup>
	<optgroup label="Designers">
		<option>
			<img alt="Lorenzo Strausa" src="https://picsum.photos/42">
			<p>Lorenzo Strausa</p>
		</option>
		<option>
			<img alt="Roberta Klein" src="https://picsum.photos/43">
			<p>Roberta Klein</p>
		</option>
	</optgroup>
</select>

Si vous venez rafraichir votre page, actuellement rien de tout ça ne s’affiche et vos balises sont juste remplacées par le contenu textuel. Ajoutons un dernier élément qui n’apparaîtra pas non plus : un button. Oui. Et hors de nos optgroups. Oui, oui ! On va le mettre tout en haut, mettre un texte dedans et ajouter une balise mystère : le selectedcontent.

<select class="super-fancy-select">
	<button>User : <selectedcontent></selectedcontent></button>
	<optgroup label="Developpers">
		<option>
			<img alt="Freddie Schimmel" src="https://picsum.photos/40">
			<p>Freddie Schimmel</p>
		</option>
		<option>
			<img alt="Laura Reichel" src="https://picsum.photos/41">
			<p>Laura Reichel</p>
		</option>
	</optgroup>
	<optgroup label="Designers">
		<option>
			<img alt="Lorenzo Strausa" src="https://picsum.photos/42">
			<p>Lorenzo Strausa</p>
		</option>
		<option>
			<img alt="Roberta Klein" src="https://picsum.photos/43">
			<p>Roberta Klein</p>
		</option>
	</optgroup>
</select>

Que l’apparence soit

C’est bien beau mais jusque là on reste sur exactement ce qu’on avait avant. Alors activons tout ça grâce à quelques lignes de CSS.

Déjà pour notre select, on va lui ajouter une petite bordure, un joli fond blanc, le cursor qu’il faut et un petit flex en prévision de la suite:

.super-fancy-select {
  	display: flex;
  	align-items: center;
  	color: #0F0F0F;
  	border: 2px solid #F7F7F7;
  	background-color: #fff;
  	cursor: pointer;
  	border-radius: 15px;
}

Jusque là rien de bien fou. Jusqu’à ce que :

.super-fancy-select {
  	[...] // le reste du code

  	&, &::picker(select) {
    	    	appearance: base-select;    
  	}
}

Ce petit sélecteur et cette petite ligne fait tout le travail. Le premier & sert à activer le style sur tout le select (si vous ne le mettez pas, vous gardez l’apparence de base partout). La seconde partie « &::picker(select) » sert à activer le style custom sur la partie popup de votre select. Et à partir de là, tout devient possible.

Améliorons déjà les options :

option, selectedcontent {
	display: flex;
	align-items: center;
	gap: 4px;
}

Ici je cible à la fois la balise option (donc ce qui est affiché dans la popup) mais aussi mon selectedcontent. Parce que oui : tout le contenu HTML de mon option sera injecté dedans, et vous pouvez tout à fait décider de faire un style différent pour chacun !

Venons maintenant améliorer un peu l’affichage de l’image :

option img, selectedcontent img {
	display: block;
	height: 30px;
	width: 30px;
	border-radius: 50%;
}

Maintenant je veux laisser un peu respirer mon contenu de popup :

optgroup {
	padding: 5px;
}

Et comme ma popup est assez laide, une syntaxt particulière me permet de venir l’améliorer via le pseudo-élément ::picker :

super-fancy-select::picker(select) {
	border-radius: 15px;
  	border: 2px solid #F7F7F7;
  	box-shadow: 0 0 8px #F7F7F7;
}

Conclusion

Attention ! Comme pour toute chose nouvelle, il est bon de consulter son Can I Use, et de se rendre compte de ses limites. Je ne partirais pas tout de suite sur cette méthode vu que les workarounds rendront mieux sur certains navigateurs mais on peut garder bon espoir de pouvoir bientôt se simplifier la vie. Je ne vous ai mis ici qu’un aperçu mais si vous consultez cette doc, vous vous rendrez compte de la liberté dont nous allons maintenant pouvoir profiter !