aller au contenu principal

Personnaliser l'apparence d'un input de type range en CSS

Avec des préfixes CSS (pas bien 😢)


Publié le 08/02/2024 dans BlogMis à jour le 10/02/2024
Crédit photo - Unsplash - Tina WitherspoonCrédit photo : Unsplash - Tina Witherspoon
Je suis en train d'écrire un cours complet sur Flexbox. Il y aura un module entier sur la compréhension de ce mode de layout, et un module 100% pratique avec plusieurs exercices.Tu peux consulter le programme et à te préinscrire dès maintenant ! ⬇️Se préinscrire

⚠️ Avant de commencer, je tiens à préciser que cet article montre des exemples d'utilisation de préfixes CSS (non standard). Il faut être prudent lors de l'utilisation de ces préfixes car ils ne sont pas standardisés et peuvent changer à tout moment. Il est préférable de les utiliser avec parcimonie et de les retirer dès que possible.

Dans un précédent article, nous avons exploré les possibilités de personnalisation CSS sur l'input de type file.

Je te propose maintenant de s'attaquer à un autre input : l'input de type range.

Le CSS offre également un bon niveau de personnalisation pour cet élément, grâce à l'utilisation de préfixes CSS qui assurent une apparence uniforme sur différents navigateurs.


L'apparence par défaut

Commençons par analyser l'apparence par défaut de l'input de type range, pour cela il suffit de le créer en HTML :

<input type="range" />

En fonction du navigateur, l'apparence de l'input de type range est bien différente :

L'apparence par défaut d'un input de type range sur les différents navigateurs (Chrome, Forefox, Safari et Edge)

Les largeurs ne sont pas les mêmes, les hauteurs non plus, et les couleurs non plus (ça fait beaucoup de différences 😅).

Heureusement, le CSS permet de prendre le contrôle de tout cela.

Mais avant, je te propose de comprendre de quoi est composé un input de type range.

Anatomie d'un input de type range

Deux éléments composent un input de type range :

  • l'input track qui est la barre sur laquelle on fait glisser l'input thumb
  • l'input thumb qui est le curseur que l'on fait glisser sur l'input track

L'input de type range est donc la combinaison de ces deux éléments.

Il faudra donc cibler en CSS ces deux éléments pour pouvoir les styliser.

Un input de type range est composé de deux éléments : un input track et un input thumb

Styliser l'input de type range avec accent-color ?

Il est possible d'utiliser une propriété encore relativement nouvelle et très bien supportée pour styliser l'input de type range : la propriété CSS accent-color.

Pour rappel : cette propriété permet de changer la couleur d'accentuation, si elle existe, d'un élément de formulaire.

Si tu regardes la première image de cet article, tu remarqueras que l'input a une couleur d'accentuation différente en fonction du navigateur : bleu sur Chrome et Firefox, grise sur Edge, et sans couleur sur Safari.

Essayons d'utiliser cette propriété pour voir ce que ça donne :

input[type="range"] {
  accent-color: red;
}

Une seule ligne de CSS permet d'obtenir ce résultat :

Utilisation de la propriété CSS accent-color sur un input de type range

Cette propriété fonctionne très bien et elle est très rapide à mettre en place, mais elle ne permet pas de prendre le contrôle complet de l'apparence de l'input de type range, ce n'est pas son rôle.

On peut d'ailleurs observer qu'elle n'a pas d'effet sur Safari (macOS).

Aussi, sur Firefox elle ne permet pas de changer la couleur de l'input thumb qui reste gris.

On ne conservera donc pas cette propriété dans la suite de l'article. Si on veut la même apparence sur nos inpits, il faut styliser l'input track et l'input thumb.

La propriété accent-color est une bonne option pour avoir des éléments de formulaire aux couleurs de ton choix, tout en gardant l'apparence proposée par les navigateurs (cela peut être une volonté de garder ces apparences par défaut).

Retirer l'apparence par défaut

Avant de pouvoir styliser l'input de type range, il faut retirer son apparence par défaut, en faisant un reset en quelque sorte.

Pourquoi faire cela ? Car sinon on ne pourra pas le styliser, certaines propriétés CSS n'auront tout simplement aucun effet.

C'est le cas de la propriété height ou de background-color qui ne fonctionneront pas si au préalable l'apparence par défaut n'est pas retirée.

Il existe une propriété CSS pour cela, c'est la propriété appearance, sur laquelle on va venir mettre la valeur none :

input[type="range"] {
  appearance: none;
}

Faire cela aura un impact sur l'input lui même, mais aussi sur l'input track en retirant certains styles par défaut comme l'arrondi des coins, le degradé sur Safari, etc.

Ce que tu ne verras pas si tu observes l'input sur un fond blanc, c'est que l'input lui même a un fond blanc :

Attention : ce n'est pas l'input track qui a un fond blanc, mais l'input lui même.

Donc sur un fond autre que blanc cela se voit :

L'input de type range a un fond blanc par défaut

Mettons donc un background: transparent sur l'input pour régler cela :

input[type="range"] {
  appearance: none;
  background: transparent;
}

Les largeurs restent sensiblement différentes, on peut donc les harmoniser.

On peut mettre également un cursor: pointer pour améliorer le retour visuel au survol de l'input :

input[type="range"] {
  appearance: none;
  background: transparent;
  width: 15rem;
  cursor: pointer;
}

Les hauteurs ne sont pas les mêmes non plus, mais on harmonisera cela avec l'input track en lui donnant une hauteur.

Styliser l'input track

C'est à partir de maintenant que nos inputs vont commencer à se ressembler, car pour le moment, leurs apparences sont encore bien différentes.

Pour styliser l'input track, nous allons nous servir des pseudo-éléments ::-webkit-slider-runnable-track et ::-moz-range-track.

Le premier est pour les navigateurs Chromium (Chrome, Safari, Edge (Chromium)), le second pour Firefox.

Modifions la hauteur, la couleur de fond (on peut même mettre un dégradé 🤩) et ajoutons des arrondis sur l'input track :

/* Chrome, Safari, Edge (Chromium) */
input[type="range"]::-webkit-slider-runnable-track {
  background: linear-gradient(to right, red 0%, #053a5f 50%, blue 100%);
  height: 0.5rem;
  border-radius: 3px;
}

/* Firefox */
input[type="range"]::-moz-range-track {
  background: linear-gradient(to right, red 0%, #053a5f 50%, blue 100%);
  height: 0.5rem;
  border-radius: 3px;
}

Tu noteras qu'il n'est pas nécessaire de mettre un appearance: none sur ces pseudo-éléments.

Comme tu peux le voir, changer la hauteur de l'input track va provoquer un problème de positionnement sur l'input thumb, mais seulement sur les navigateurs Chromium : Chrome, Safari, et Edge.

Firefox est le seul qui n'a pas ce problème :

Changer la hauteur de l'input track va créer un problème de positionnement (sauf sur Firefox)

Pour régler ce problème il faut cibler l'input thumb.

Styliser l'input thumb

Comme on l'a vu précédemment, l'input thumb est le curseur que l'on fait glisser sur l'input track.

Pour cet élément aussi, il y a deux pseudo-éléments pour le styliser : ::-webkit-slider-thumb et ::-moz-range-thumb.

Contrairement à l'input track, il faudra ici lui mettre un appearance: none pour pouvoir le styliser.

Aussi par défaut sur Firefox, le thumb à des bords arrondis, et ça même après le appearance: none.

Il faut donc soit les enlever, soit les rajouter pour les navigateurs Chromium :

/* Chrome, Safari, Edge (Chromium) */
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  background: #0075FF;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
}

/* Firefox */
input[type="range"]::-moz-range-thumb {
  appearance: none;
  background: #0075FF;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
}

Pourquoi ne pas regrouper les styles des deux pseudo-éléments en une seule règle, puisque ce sont les mêmes propriétés ? 🤔

C'est une bonne question ! 😉

La raison est très simple : si un sélecteur n'est pas compris par un navigateur, alors il doit l'ignorer.

Si on prend l'exemple du navigateur Firefox, il ne comprend pas le sélecteur ::-webkit-slider-thumb, il doit donc ignorer tout le sélecteur, c'est pour cette raison que l'on ne peut pas grouper les deux pseudo-éléments.

Il faut maintenant centrer l'input thumb sur l'input track, mais uniquement pour les navigateurs Chromium :

Changer la hauteur de l'input track va créer un problème de positionnement (sauf sur Firefox)

Nous allons utiliser pour cela la propriété margin-top en mettant une valeur négative.

La formule est la suivante : margin-top = (hauteur du l'input track en pixel / 2) - (hauteur de l'input thumb en pixel / 2).

Pour rappel, il ne faut faire cela que pour les navigateurs Chromium, donc uniquement sur le pseudo-élément ::-webkit-slider-thumb.

Ajoutons des variables CSS pour rendre le code plus maintenable :

:root {
  --track-height: 6px;
  --thumb-height: 12px;
  --thumb-width: 12px;
}

/* Chrome, Safari, Edge (Chromium) */
input[type="range"]::-webkit-slider-thumb {
  width: var(--thumb-width);
  height: var(--thumb-height);
  margin-top: calc((var(--track-height) / 2) - (var(--thumb-height) / 2));
}

Il subsiste une différence sur Firefox, l'input thumb a une bordure grise :

Sur Firefox, l'input thumb a une bordure grise

Il faut donc au choix :

  • retirer la bordure
  • styliser la bordure

Choisissons la deuxième option en mettant sur l'input thumb un fond blanc et une bordure colorée :

/* Chrome, Safari, Edge (Chromium) */
input[type="range"]::-webkit-slider-thumb {
  background: #fff;
  border: 3px solid #3353fe;
}

/* Firefox */
input[type="range"]::-moz-range-thumb {
  background: #fff;
  border: 3px solid #3353fe;
}

Voici le code complet de ce qui a été réalisé jusqu'à présent :

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/styles.css" />
  </head>

  <body>
    <input type="range" />
  </body>

</html>

Comme tu peux le voir, les inputs ont désormais une apparence identique sur les différents navigateurs 😎 :

Exemple d'input de type range sur plusieurs navigateurs avec une apparence identique

Améliorer le focus

Comme tous les éléments de formulaire, ces derniers ont un focus par défaut, celui proposé par le navigateur.

Ce focus a lui aussi une apparence différente en fonction du navigateur.

Retirer le focus par défaut

Parce qu'il est différent sur les différents navigateurs, il est préférable de le retirer pour ensuite le styliser nous même ! 😎

On peut noter que sur Safari il n'y a pas de focus par défaut sur l'input de type range (c'est aussi le cas sur les balises <button>) :

Exemple d'input de type range avec un focus

Pour retirer le focus d'un élément, il suffit d'utiliser la propriété outline en lui mettant la valeur none :

input[type="range"]:focus {
  outline: none;
}

Styliser le focus

On ne va pas styliser le focus de l'input, mais plutôt celui de l'input thumb, car je trouve que cela a plus de sens. Après tout, c'est cet élément que l'utilisateur déplace, pas l'input lui-même :

/* On retire le focus par défaut sur l'input */
input[type="range"]:focus {
  outline: none;
}

/* Chrome, Safari, Edge (Chromium) */
input[type="range"]:focus::-webkit-slider-thumb  {
  outline: 2px solid #3353fe;
  outline-offset: 2px;
}

/* Firefox */
input[type="range"]:focus::-moz-range-thumb {
  outline: 2px solid #3353fe;
  outline-offset: 2px;
}

Voici le résultat final :

Exemple d'input de type range avec un focus sur l'input thumb

Le code complet :

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/styles.css" />
  </head>

  <body>
    <input type="range" />
  </body>

</html>

Je te remercie de m'avoir lu, n'hésites pas à me donner ton avis sur cet article sur Twitter !

Si tu souhaites être prévenu de la sortie des prochains articles et contenu du site, tu trouveras le formulaire d'inscription à ma newsletter un peu plus bas.

À bientôt, Seb 🤘

2024 Sébastien Imbert