Choisir le HTML natif pour mettre fin aux hacks CSS dans la création de formulaires

Le besoin de prendre le contrôle de l’apparence d’éléments natifs a toujours été présent chez les développeurs et les développeuses. Par envie de vouloir des éléments à l’apparence unique, parfois par obligation, car l’interface doit être identique aux maquettes.

La personnalisation d’éléments natifs peut provoquer des comportements inattendus, avec des impacts parfois négatifs sur l’accessibilité.

La question que j’ai souhaité traiter dans cet article est la suivante : faut-il prendre, en tant que développeurs et développeuses, la responsabilité de laisser les hacks CSS derrière nous ? En particulier dans la création de formulaires, car les éléments qui les composent sont souvent malmenés !

Explorons ensemble les différentes possibilités de personnalisation, celles qui entraînent des effets indésirables et celles à privilégier.

Quand on parle de CSS modernes, on cite souvent Flexbox ou Grid, mais leur implémentation dans les navigateurs commence à dater !

Alors oui, c’est vrai que comparé à l’utilisation des float, on a gagné en simplicité et on a plus cette impression de « tordre » les CSS pour faire de la mise en page.

Pour rappel : les float permettent de faire sortir un élément du flux normal pour le faire « flotter » autour d’un autre élément, comme une image :

Exemple d'utilisation de la propriété CSS float. Le texte s'écoule le long de l'image.
Avec un float: left sur le texte, ce dernier sort du flux normal pour « flotter » autour de l’image.

Les float sont toujours utiles, simplement il faut arrêter s’en servir pour autre chose.

Je prends cet exemple, car je pense que l’on peut considérer leur utilisation pour faire des mises en page complexes comme une sorte de hack, car on détourne leur fonction initiale pour en faire autre chose.

La vraie définition d’un hack CSS

Il est important de définir ce qu’est un hack CSS, en se basant sur une définition.

Définition : un hack CSS est une technique pour contourner un problème, soit en détournant une propriété de son usage, soit en utilisant des codes supplémentaires pour pallier les manques. Un hack peut permettre d’éviter les bugs d’interprétation des navigateurs, qui n’implémentent pas tous parfois de la même manière les nouvelles propriétés CSS.

Donc un hack CSS n’est pas une astuce. Centrer un élément horizontalement et verticalement avec Flexbox n’est pas un hack, c’est juste… du CSS !

Il y a eu plusieurs types de hacks. On ne va pas tous les évoquer, car certains, comme les commentaires conditionnels pour Internet Explorer, n’ont plus vraiment d’intérêt, ce dernier n’étant plus maintenu.

Il en existe un encore très utilisé aujourd’hui : les préfixes vendeurs (vendors prefixes en anglais), qui permettent de cibler un type de navigateur.

Par exemple :

  • -webkit- pour les navigateurs Chromium (Chrome, Safari, Edge…) ;
  • -moz- pour Firefox.

Les préfixes sont là pour permettre aux développeurs et aux développeuses de tester et d’expérimenter des fonctionnalités CSS expérimentales qui ne sont pas encore standardisées ou qui sont en voie de l’être.

Si on reprend l’exemple des float, cela rentre dans cette définition, car leur utilisation pour faire de la mise en page complexe sort de l’usage prévu initialement.
Mais après tout, les développeurs et les développeuses qui utilisaient les float de cette manière ne faisaient rien d’autre que d’utiliser la propriété à leur disposition.
Même si, sur le rendu final, on perdait cet effet flottant.

Les formulaires sont parmi les éléments les plus sujets aux hacks CSS, pour une raison simple : on veut de la personnalisation, même sur des éléments natifs. Le HTML et les CSS modernes permettent d’atteindre un bon niveau de personnalisation, via des hacks ou non. Explorons ces possibilités !

Les hacks CSS dans la personnalisation de formulaire

Les hacks dans la personnalisation de formulaire sont monnaie courante, voici quelques exemples de ce que l’on peut croiser :

  • un input de type fichier caché pour être recréé de toutes pièces en HTML, CSS et JavaScript, pour changer le texte du bouton par exemple (« Choisir un fichier » sur Google Chrome, « Parcourir… » sur Firefox).
  • utiliser un input de type texte et utiliser une bibliothèque d’affichage de calendrier (c’est dommage, il existe l’élément input type="date" pour cela).
  • un menu déroulant entièrement créé en HTML, CSS et JavaScript, car l’élément select ne permet pas assez de personnalisation.
  • l’utilisation des préfixes pour changer l’apparence de certaines parties d’un élément : changer la couleur du curseur d’un input type="range" par exemple.

Si on prend ces exemples, on peut se demander, pour certains, si c’est vraiment utile. Est-ce qu’on a vraiment besoin de changer le texte du bouton d’un input de type fichier ?

Pour atteindre un certain niveau de personnalisation, il faut parfois passer par des hacks CSS (préfixes) et parfois il faut complètement recréer l’élément en cachant en CSS l’élément natif.

Explorons ensemble les différentes possibilités qui existent pour personnaliser des éléments de formulaire.

Les différentes possibilités de personnalisation des formulaires

Il existe plusieurs façons de personnaliser les éléments de formulaire. Certaines techniques sont plus radicales que d’autres, explorons-les ensemble.

Cacher un élément natif

Cacher un élément natif pour le recréer de toute pièce, c’est quelque chose que l’on peut voir assez souvent et c’est vraiment dommage, car souvent ce n’est absolument pas nécessaire d’en arriver là.

Reprenons l’exemple de l’élément input de type fichier : il n’existe pas de possibilité native pour changer le texte du bouton.

Il faut pour cela créer un input de type fichier, qui sera rendu invisible en CSS et créer un autre élément avec des balises HTML, comme des div ou des span, auxquelles on pourra donner l’apparence de notre choix avec du code CSS.

Cette approche pose problème pour plusieurs raisons :

  • il faudra être vigilant et rendre l’élément accessible (navigation au clavier, contrastes…) ;
  • les textes du bouton et du placeholder ne seront pas traduits nativement ;
  • il faudra rendre l’élément interactif : le clic doit fonctionner. Il faudra donc ajouter du JavaScript ou utiliser une balise label.

L’élément input de type fichier est par défaut accessible au clavier. Les textes sont également affichés dans la langue du navigateur de l’utilisateur ou de l’utilisatrice.

On comprend donc que cette approche est à proscrire. Même si elle permet d’avoir un élément identique sur tous les navigateurs, les exemples que j’ai trouvés sont tous inaccessibles au clavier.

Voici un exemple de design qu’il est possible d’atteindre, avec en comparaison la version native de chaque navigateur :

Une balise input de type fichier personnalisée et son apparence en fonction des navigateurs (Chrome, Edge, Safari et Firefox)

Détourner un élément natif

D’autres approches de ce genre existent. C’est le cas des input de type date. Par défaut les navigateurs proposent des calendriers interactifs.

Si le design ne convient pas, c’est souvent un input de type texte qui est utilisé, sur lequel une bibliothèque JavaScript de calendrier est branchée.

Cette approche peut être justifiée par un besoin bien spécifique, par exemple l’affichage des vacances scolaires dans le calendrier.

Il y a souvent moyen de se passer d’une bibliothèque. Après tout, on veut que les utilisateurs et les utilisatrices puissent saisir une date. L’élément natif permet cela.
L’affichage d’informations supplémentaires peut se faire en dehors du calendrier.

Il faut garder en tête que toutes les bibliothèques ne garantissent pas l’accessibilité et toutes les traductions possibles. C’est aussi une ressource supplémentaire à télécharger que l’on impose à l’utilisateur ou l’utilisatrice.

Pour information, le calendrier de l’input de type date de Firefox prend en compte les préférences d’apparence clair/sombre de l’utilisateur ou de l’utilisatrice :

capture d'écran du calendrier d'un input de type date en mode sombre et en mode clair
Sur Firefox, le calendrier de l’input de type date s’adapte au mode d’apparence clair/sombre choisi par l’utilisateur ou l’utilisatrice.

Créer un élément de toute pièce

Parfois, certains éléments sont entièrement créés par les développeurs et les développeuses, car il n’existe pas vraiment d’équivalent natif.

C’est le cas des menus select, sur lesquels on a besoin d’ajouter de la personnalisation. Par exemple, un champ de recherche pour filtrer les options et une disposition en grille :

Un menu déroulant personnalisé. Il permet de choisir son fruit préféré parmi une sélection.
Ce menu select ne peut pas être construit avec l’élément select, le HTML natif ne le permet pas (pas encore mais ça arrive).

Il ne sera pas possible de faire cela avec select, l’élément n’a pas été prévu pour cela.

Il faut donc créer le menu nous-mêmes, en veillant à ce que l’élément reste accessible, respecte les préférences de l’utilisateur ou de l’utilisatrice en termes d’apparence ou d’animations, et intègre une gestion des langues si besoin. Il faut aussi penser à ce que le menu se positionne correctement dans le viewport.

Utiliser la propriété accent-color

C’est une propriété qui permet de changer la couleur d’accentuation des éléments natifs de formulaire, et ça en une seule ligne de code :

input {
  accent-color: hotpink;
}

Il est également possible d’utiliser cette propriété sur le sélecteur :root pour affecter tous les éléments de formulaires :

Exemple d'utilisation de la propriété CSS accent-color. Les cases à cocher, boutons radio, slider et barre de progression sont roses au lieu de leur couleur par défaut.

Cette propriété est déjà très bien supportée par les différents navigateurs, avec un support supérieur à 90% (source Can I Use).

C’est typiquement le genre de propriété que tu peux implémenter dès maintenant !

Pourquoi ? Pas seulement car le support est excellent, mais aussi parce que si le navigateur ne comprend pas cette propriété, alors il ignorera la règle et utilisera la couleur par défaut. Il y a donc déjà un fallback.

Autre point non négligeable, cela n’altère pas l’accessibilité et respecte les préférences de l’utilisateur ou de l’utilisatrice (mode sombre et clair dans les paramètres du système d’exploitation).

Si tu veux aller plus loin, voici un article dédié à cette propriété, ainsi qu’un CodePen pour que tu puisses tester et comprendre son fonctionnement.

Retirer l’apparence par défaut d’un élément

La propriété accent-color ne permet pas de prendre le contrôle de l’apparence des éléments de formulaire.

Or, on peut vouloir changer leur apparence, après tout, ce que proposent les navigateurs est relativement neutre, voyons ce qu’il est possible de faire en CSS.

La propriété appearance permet de réinitialiser les styles natifs d’un élément en indiquant au navigateur d’effacer tous les styles par défaut. Cela nous permet de repartir de zéro et de créer l’apparence de notre choix en CSS.

C’est une propriété qui existe depuis longtemps, mais qui n’était pas supportée par tous les navigateurs.

input {
  appearance: none;
}

Elle n’est pas toujours nécessaire, car on peut aisément styliser un élément en écrasant les styles par défaut du navigateur.

Aussi, tu devras recréer certains éléments entièrement, comme les cases à cocher ou les boutons radio qui… disparaissent !

L'apparence par défaut de plusieurs éléments HTML (bouton, case à cocher, sélecteur de couleur, champ texte, champ date et champ date/heure) comparée à l'apparence après utilisation de la règle CSS appearance: none.
La propriété apperance peut être utilisée pour réinitialiser l’apparence de certains éléments, mais attention aux effets de bords.

C’est beaucoup de travail et il faut être vigilant sur certains éléments.

Sur certains éléments, il faudra nécessairement passer par une appearance: none, car certaines propriétés n’auront aucun effet sinon. C’est le cas de l’input type="range", la propriété background-color n’aura aucun effet si au préalable on ne met pas appearance: none.

Utiliser des pseudo-éléments (standards et non standards)

Les pseudo-éléments peuvent nous permettre de cibler certaines parties d’un élément.

Par exemple, le pseudo-élément ::file-selector-button permet de cibler le bouton d’un input de type fichier.

Comme tu peux le voir, il n’y a pas de préfixe sur ce pseudo-élément, c’est normal, c’est un standard. Il fait partie d’une spécification : CSS Pseudo-Elements Module Level 4.

Tu peux donc l’utiliser sans risque, car même si un navigateur ne supporte pas ce pseudo-élément, il ignorera la règle et gardera le style par défaut du bouton.

Voici le niveau de personnalisation que tu peux atteindre, tout en utilisant un élément natif avec un pseudo-élément standard :

Visualisation d'un élément input de type fichier personnalisé dans les navigateurs Chrome, Firefox, Safari et Edge.
En ajoutant des padding sur le bouton, le texte du placeholder n’a plus suffisamment de place et doit être tronqué sur certains navigateurs.

Sauf que… on a ajouté un problème, le texte du placeholder est maintenant tronqué
On a ce comportement, car des padding ont été ajoutés au bouton et la largeur de l’input étant fixe, le texte ne rentre plus dans l’espace prévu sur certains navigateurs.

On peut toujours corriger cela en mettant une largeur fixe à l’input avec la propriété width, mais on risque d’aller au-devant d’autres effets de bord.
D’une manière générale, il est préférable de laisser le navigateur déterminer les largeurs et hauteurs des éléments.

Prenons un autre exemple, la personnalisation d’un input de type range.

Cet input est composé de deux éléments :

  • la piste de l’input (aussi appelée input track), qui est la barre sur laquelle on fait glisser le curseur ;
  • le curseur (aussi appelée input thumb) que l’on fait glisser sur la piste.

Si l’on veut personnaliser ces éléments, il faudra utiliser des préfixes non standards :

  • ::-webkit-slider-runnable-track et ::-moz-range-track pour l’input track ;
  • ::-webkit-slider-thumb et ::-moz-range-thumb pour l’input thumb.

Comme on peut le voir, ces pseudo-éléments ont un préfixe, car ils ne sont pas des standards, ni en voie de l’être. La documentation MDN précise même qu’il ne faut pas les utiliser pour des sites accessibles, et surtout, leur comportement peut être modifié dans le futur :

Message d'avertissement sur la page de la documentation MDN de la propriété non standard ::-webkit-slider-runnable-track.
La documentation MDN préconise de ne pas utiliser les pseudo-éléments qui sont des non standards.

Des pseudo-éléments non standards comme ceux-là, il en existe beaucoup, vraiment beaucoup.

Par exemple, savais-tu que l’on peut cibler les « / » présents dans un input de type date ?

C’est le pseudo-élément non standard ::-webkit-datetime-edit-text qui permet de les cibler, on peut alors prendre la liberté de leur changer leur couleur, et même de les cacher :

Démonstration d'un élément input de type date, dont la couleur des / a été modifiée en CSS pour du rouge.
Le pseudo-élément non standard ::-webkit-datetime-edit-text permet de cibler le « / » de la date affichée dans l’élément input de type date, mais est-ce que c’est vraiment utile ?

D’ailleurs une petite astuce si tu veux découvrir ces éléments en les observant dans l’inspecteur, il te faut activer le Shadow-DOM, sur Google Chrome tu peux faire un Ctrl+Shift+I, te rendre dans les préférences, dans la section « Élément » et « Activer Shadow-DOM ».

Dans ton inspecteur d’éléments, tu pourras alors voir de quoi sont composés certains éléments natifs :

Car c’est parfois ce que l’on voit dans la personnalisation de formulaire : des éléments sont cachés pour être ensuite recréés de toutes pièces en HTML, CSS et même en JavaScript pour ajouter de l’interactivité.

C’est quelque chose que l’on croise assez souvent, surtout sur les input de type fichier natif cachés et complètement recréés.

Une question que l’on peut se poser : pourquoi ces pseudo-éléments non standards sont considérés comme des hacks, après tout s’ils sont là, autant s’en servir !

Le HTML évolue lui aussi

Ces dernières années, les CSS ont connu énormément de nouveautés, mais on oublie souvent que le HTML a lui aussi eu les siennes.

Oui, on ne peut pas aujourd’hui prendre le contrôle du calendrier des input de type date, ou modifier l’apparence du sous-menu d’un élément select.

Encore que, depuis peu il est désormais possible de mettre des balises hr pour faire des séparations entre les balises option depuis Chrome 117 et Safari 17.

Une liste déroulante d'options avec des séparateurs.
Avec les versions 117 de Chrome et 17 de Safari, il est possible de mettre des balises hr entre les balises option.

Open UI : un collectif pour améliorer le natif

Parler de la personnalisation des formulaires me donne l’occasion de te parler d’Open UI : un groupe communautaire de développeurs et de développeuses et graphistes membres du W3C, qui propose des solutions pour permettre aux développeurs et aux développeuses de nouvelles approches.

Les spécifications que le groupe produit respectent évidemment les prérequis en ce qui concerne les standards du Web.

Open UI
La page d’accueil du site Open UI.

Parmi leurs propositions récentes, il y a l’élément selectlist : une alternative à l’élément select.

L’élément select est un des éléments natifs les plus difficiles à personnaliser. C’est l’une des raisons qui a poussé le collectif à proposer l’ajout d’un nouvel élément : selectlist.

L’élément est entièrement personnalisable.

Contrairement à la propriété accent-color, on est ici sur quelque chose d’expérimental et qui n’est fonctionnel pour le moment que sur Chrome Canary (avec le flag « Experimental Web Platform features » activé).

Donc cela veut dire que si on souhaite l’utiliser pour découvrir les possibilités que nous offre ce nouvel élément, on peut !
Open UI fournit d’ailleurs une documentation très détaillée.
Est-ce que cela veut dire que l’élément select va devenir obsolète ?

Non pas du tout, l’élément selectlist est une alternative, un élément en plus à notre disposition.

L’élément select restera encore utile pour plusieurs raisons :

  • il est utilisé depuis très longtemps, on ne peut pas du jour au lendemain le rendre obsolète ;
  • le menu contextuel des options a la possibilité de « sortir » du navigateur et son affichage est différent en fonction du système d’exploitation (iOS, macOS, Windows, Android…).

Avec l’élément selectlist, il sera possible de créer ces menus de façon native (et accessibles) :

Deux exemples de listes déroulantes personnalisées créée avec l'élément selectlist.
Ces deux menus peuvent être construits avec du HTML natif, avec l’élément selectlist (fonctionnalité encore expérimentale).
capture d'écran d'un menu de sélection de langue
Une reproduction du menu de sélection de langue du site State of CSS 2023 avec l’élément selectlist.

Si on laissait les hacks CSS derrière nous ?

Comme on l’a vu, il existe plusieurs types de hacks pour personnaliser un élément de formulaire :

  • on peut le cacher ;
  • le détourner ;
  • le créer de toutes pièces ;
  • retirer l’apparence par défaut et utiliser des pseudo-éléments standards ou non standards pour styliser les éléments.

Cacher, détourner ou créer un élément comme on l’a vu est plutôt à proscrire, ou alors il faut que cela soit justifié.

Mais pour ce qui est des pseudo-éléments standards ou non standards, pourquoi s’en priver ?

Les vendors prefixes étant considérés comme un non standard et un hack, je serais d’avis de ne pas les utiliser, ou alors uniquement dans un cadre spécifique.

Si on prend le pseudo-élément ::file-selector-button, c’est un standard, on peut l’utiliser sans problème.

Pour ce qui est des non standards, il y a un risque. En effet, les vendors prefixes permettent aux développeurs et aux développeuses de tester des fonctionnalités expérimentales, mais les navigateurs peuvent en arrêter le support à tout moment.

Autre chose, je suis plutôt d’avis de garder au maximum l’apparence par défaut de certains éléments, on peut toujours les modifier avec des CSS, les pseudo-éléments standards à notre disposition et avec la propriété accent-color.

Les éléments auront une apparence différente d’un navigateur à l’autre, et c’est une bonne chose. Un utilisateur ou une utilisatrice habitué·e à utiliser Safari pour sa navigation ne sera pas surpris·e de voir un input de type fichier comme Safari le propose.

Par exemple il est possible sur Chrome de prendre le contrôle de l’apparence de la barre de défilement, avec des pseudo-éléments non standards.
Mais parfois, cela peut gêner l’expérience utilisateur : une barre de défilement trop fine, des couleurs pas assez contrastées, les flèches haut et bas qui disparaissent sur Windows (les utilisateurs et les utilisatrices Windows sont habitués à avoir des flèches pour faire défiler la barre).

Ce sont ces raisons qui me font dire que c’est aux développeurs et aux développeuses de prendre la responsabilité de laisser les hacks CSS derrière eux.

D’où vient ce besoin d’avoir des éléments identiques sur tous les navigateurs ?

En écrivant l’article je me suis posé cette question : mais d’où vient ce besoin de vouloir prendre le contrôle des éléments natifs, et de vouloir absolument qu’ils soient identiques d’un navigateur à l’autre ?

Je pense que cela vient d’une certaine exigence : le pixel perfect sur tous les navigateurs.

Pour une raison qui n’a pas vraiment de sens, on veut des interfaces identiques sur tous les navigateurs.

Or les éléments de HTML natifs, comme les éléments de formulaires, n’ont pas la même apparence d’un navigateur à l’autre.

Les navigateurs ont parfois des moteurs de rendu différents.

Josh Comeau l’explique très bien dans son article « Chasing the Pixel-Perfect Dream ».

Travailler davantage avec les graphistes web

Un graphiste qui va faire le design d’un site Web peut trouver joli de créer un élément select personnalisé, ou un input de type fichier personnalisé, mais ces éléments ne pourront pas être intégrés avec des éléments natifs.

C’est aussi aux designers et aux développeurs et développeuses de faire le choix de prioriser le HTML natif. Dès le départ, au moment de la phase de design, en évitant d’intégrer et en faisant valider des interfaces qui ne peuvent pas être reproduites avec des éléments natifs.

Les formulaires peuvent maintenant avoir la couleur d’une charte graphique avec la propriété accent-color, c’est génial et déjà largement suffisant.

Et si on laissait les éléments natifs tranquilles, que ce soit les barres de défilement, l’apparence du curseur de la souris, mais aussi les éléments de formulaire ?

Je ne suis pas contre la personnalisation (c’est très bien sur des sites expérimentaux), mais pour un site accessible, je pense qu’il faut faire attention.

3 commentaires sur cet article

  1. KC, le dimanche 3 décembre 2023 à 10:04

    Pour grouper des options de select il existe déjà « optgroup » depuis… au moins tout ça !

  2. Raphaël Goetter, le dimanche 3 décembre 2023 à 12:19

    Merci pour cet article plein de bon sens et d’idées.
    Attention dans l’introduction sur float, c’est bien l’image ici qui est flottante à gauche et non le texte.

  3. Nico, le vendredi 8 décembre 2023 à 10:24

    Merci pour l’article, je ne connaissais pas selectlist :)

    Sinon, attention avec appearance : none, aussi dingue que ça puisse paraitre, j’ai vu des bugs d’accessibilité avec ‑webkit-appearance : none sur des checkboxes/radios, pour ces cas, j’ai dû repréciser ‑webkit-appearance : checkbox ; etc. pour que ça marche normalement.

    Sur le vocabulaire, utiliser les propriétés préfixées n’est pas un hack selon moi (pour moi, le hack c’est utiliser qqch qui n’est pas prévu pour ça pour contourner un problème/fixer un bug), c’est plus une façon de proposer des styles non-standards : tant qu’on sait que ça ne sera que pour certains navigateurs et que ça ne pète pas le cas standard, pourquoi pas.

    Après, le côté ultra-personnalisation, àmha, c’est plus valable pour une web app (là où tu peux proposer des paramètres : scroll fin/normal, etc.) que pour un site classique : dans ces cas, tu as des contraintes de branding/design/espace qui font que les éléments un peu cracras de certains navigateurs sont compliqués à gérer (l’input file que tu as donné est un excellent exemple, notamment avec la L10N, ceci dit, en se basant dessus, on peut avoir le meilleur des deux mondes : un graphisme maitrisé et la base native qui marche).

Laisser un commentaire

Les commentaires sont modérés manuellement. Merci de respecter : la personne qui a écrit l'article, les autres participant(e)s à la discussion, et la langue française. Vous pouvez suivre les réponses par flux RSS.