Pour résumer ce que nous avons vu dans l'article précédent, nous avons :

La brute : certains éléments sont plus difficiles à mettre en forme et nécessitent du CSS plus complexe ou quelques astuces :

  • Les cases à cocher ( checkbox )
  • Les boutons radio
  • <input type="search">
  • Le truand : certains éléments ne peuvent pas être complètement mis en forme à l'aide CSS. Parmi ceux-là, nous avons :

  • Les éléments qui créent des menus déroulants dont <select> , <option> , <optgroup> et <datalist>
  • <input type="color">
  • Les contrôles pour les dates comme <input type="datetime-local">
  • <input type="range">
  • <input type="file">
  • <progress> et <meter>
  • Pour commencer, abordons la propriété appearance qui s'avère plutôt utile pour simplifier la mise en forme de l'ensemble des éléments listés ci-avant.

    appearance : contrôler la mise en forme liée au système d'exploitation

    Dans l'article précédent, nous avons vu que la mise en forme des contrôles de formulaire provenait historiquement du système d'exploitation sous-jacent, ce qui explique en partie la difficulté à personnaliser l'aspect de ces contrôles.

    La propriété appearance a été créée comme une méthode pour contrôler les styles provenant du système d'exploitation et qui étaient appliqués aux contrôles de formulaire. Malheureusement, le comportement de cette propriété avec les implémentations initiales variait grandement d'un navigateur à l'autre, elle n'était donc que peu utilisable. Les implémentations plus récentes sont plus cohérentes et les différents navigateurs (que ce soit ceux basés sur Chromium comme Chrome, Opera, et Edge ; Safari ; et Firefox) prennent en charge la version préfixée avec -webkit- ( -webkit-appearance ).

    Si on consulte la page de référence, on verra que -webkit-appearance peut prendre de nombreuses valeurs différentes. Toutefois, la valeur la plus utile et celle que vous utiliserez probablement est none . Cela empêche, autant que possible, l'utilisation des styles provenant du système, vous permettant ainsi de construire vos propres styles avec CSS.

    Prenons l'exemple suivant avec ces différents contrôles :

    html
    <form>
        <label for="search">search:</label>
        <input id="search" name="search" type="search" />
        <label for="text">text:</label>
        <input id="text" name="text" type="text" />
        <label for="date">date:</label>
        <input id="date" name="date" type="datetime-local" />
        <label for="radio">radio:</label>
        <input id="radio" name="radio" type="radio" />
        <label for="checkbox">checkbox:</label>
        <input id="checkbox" name="checkbox" type="checkbox" />
      <p><input type="submit" value="submit" /></p>
      <p><input type="button" value="button" /></p>
    </form>
    

    Appliquer la règle CSS suivante permettra de retirer la mise en forme provenant du système.

    css
    input {
      -webkit-appearance: none;
      appearance: none;
    

    Note : Mieux vaut utiliser les deux déclarations (celle avec le préfixe et celle sans) lorsqu'on utilise une propriété préfixée. En effet, la version préfixée signifie généralement que du travail de standardisation est en cours et qu'il pourrait y avoir plus tard un consensus pour abandonner la version préfixée. Dans l'exemple qui précède, on se prémunit ainsi contre une telle situation.

    L'aperçu qui suit montre : le rendu avec les styles système à gauche et le rendu avec appearance: none à droite (vous pouvez également voir cet exemple sur cette page si vous voulez le tester sur d'autres systèmes).

    Dans la plupart des cas, l'effet correspond au retrait de la mise en forme de la bordure, ce qui rend l'application de CSS plus simple, mais ce n'est pas réellement essentiel. Pour d'autres en revanche, comme les champs de recherche, les boutons radio et les cases à cocher, cela s'avère beaucoup plus utile. Voyons de quoi il en retourne.

    Dompter les champs de recherche

    <input type="search"> prend généralement la forme d'un champ texte, pourquoi donc appearance: none; devient-il utile ici ? En fait, sur macOS, les navigateurs basés sur Chromium ont des restrictions quant aux styles des boîtes de recherche : par exemple, on ne peut pas ajuster librement leur hauteur ( height ) ou la taille de la police ( font-size ). Cela est dû au fait que les navigateurs basés sur Chromium n'utilisent plus le moteur de rendu WebKit , ce qui a activé l'apparence « Aqua » par défaut pour certains contrôles de formulaires. Avec Aqua activé, certains contrôles de formulaire ne sont plus redimensionnables .

    Cela peut être corrigé avec appearance: none; , qui désactive cette apparence Aqua par défaut :

    css
    input[type="search"] {
      -webkit-appearance: none;
      appearance: none;
    

    Dans l'exemple qui suit, on peut voir deux champs de recherche identiques. Celui de droite est mis en forme avec appearance: none; et celui de gauche n'utilise pas cette propriété. Si vous consultez cet exemple sur Chrome sur macOS, vous verrez que l'exemplaire à gauche n'est pas dimensionné correctement.

    De façon intéressante, définir la boardule ou l'arrière-plan du champ de recherche permet aussi de résoudre ce problème, car cela désactive ou « casse » l'apparence Aqua. L'exemple qui suit n'utilise pas du tout appearance: none;, mais, sur Chrome pour macOS, on peut voir qu'il ne souffre pas du même problème que l'exemple précédent.

    Note : Vous pourrez remarquer que, dans le champ de recherche, l'icône de croix pour la suppression disparaît quand le champ perd le focus sur Edge et Chrome mais qu'il reste sur Safari. Pour la retirer en CSS (y compris lorsqu'il y a le focus), vous pouvez utiliser input[type="search"]::-webkit-search-cancel-button { display: none; }.

    Mettre en forme les cases à cocher et les boutons radio

    La mise en forme d'une case à cocher ou d'un bouton radio s'avère délicate par défaut. Les dimensions des cases ou des boutons ne sont pas prévues pour être changées et les navigateurs réagissent différemment si vous essayez.

    Prenons un cas de test simple :

    html
    <span><input type="checkbox" /></span>
    
    css
    span {
      display: inline-block;
      background: red;
    input[type="checkbox"] {
      width: 100px;
      height: 100px;
    

    Les différents navigateurs gèrent cela différemment, et souvent avec un résultat inadapté :

    Navigateur Rendu

    Utiliser appearance: none sur les boutons radio et les cases à cocher

    Comme nous l'avons vu, il est possible de retirer l'apparence par défaut d'une case à cocher ou d'un bouton radio avec appearance:none;. Prenons cet exemple en HTML:

    html
    <form>
      <fieldset>
        <legend>Fruits préférés</legend>
          <label>
            <input type="checkbox" name="fruit-1" value="cherry" />
            J'aime les cerises
          </label>
          <label>
            <input type="checkbox" name="fruit-2" value="banana" disabled />
            Je ne peux pas aimer les bananes
          </label>
          <label>
            <input type="checkbox" name="fruit-3" value="strawberry" />
            J'aime les fraises
          </label>
      </fieldset>
    </form>
    

    Utilisons une mise en forme personnalisée pour les cases à cocher. Commençons par retirer le style du système :

    css
    input[type="checkbox"] {
      -webkit-appearance: none;
      appearance: none;
    

    Nous pouvons ensuite utiliser les pseudo-classes :checked et :disabled pour changer l'apparence de nos cases à cocher lorsque leur état change :

    css
    input[type="checkbox"] {
      position: relative;
      width: 1em;
      height: 1em;
      border: 1px solid gray;
      /* Ajuste la position de la case à cocher sur la ligne de base du texte */
      vertical-align: -2px;
      /* On définit ici afin que le mode de contraste élevé de Windows puisse
         surcharger */
      color: green;
    input[type="checkbox"]::before {
      content: "✔";
      position: absolute;
      font-size: 1.2em;
      right: -1px;
      top: -0.3em;
      visibility: hidden;
    input[type="checkbox"]:checked::before {
      /* On utilise `visibility` plutôt que `display` pour éviter le
         recalcul de la disposition */
      visibility: visible;
    input[type="checkbox"]:disabled {
      border-color: black;
      background: #ddd;
      color: gray;
    

    Nous en verrons plus sur les pseudo-classes dans le prochain article. Voyons celles qui sont utilisées ici :

  • :checked permet d'appliquer une mise en forme lorsque la case à cocher ou le bouton radio est dans un état sélectionné.
  • :disabled permet d'appliquer une mise en forme lorsque la case à cocher ou le bouton radio est désactivé et qu'on ne peut pas interagir avec.
  • Voici le résultat de cet exemple :

    Voici quelques autres exemples pour vous donner d'autres idées 

  • Mise en forme de boutons radio : avec une mise en forme personnalisée pour des boutons radio
  • Exemple d'interrupteur : une case à cocher mise en forme afin de ressembler à un interrupteur.
  • Si vous consultez ces exemples dans un navigateur qui ne prend pas en charge appearance, votre conception ne sera pas visible, mais les éléments ressembleront à des cases à cocher et seront utilisables.

    Note : Bien qu'Internet Explorer ne prenne pas en charge appearance, input[type=checkbox]::-ms-check permet de cibler les cases à cocher dans IE. Cette technique fonctionne également pour les boutons radio malgré le nom -ms-check.

    Quid des éléments dans la catégorie des « truands » ?

    Voyons maintenant les contrôles qui tombent dans la catégorie des « truands » et qui sont très difficiles à mettre en forme. Il s'agit des contrôles avec des listes déroulantes ou de contrôles complexes comme color , datetime-local , et des contrôles de feedback comme <progress> et <meter> .

    Le problème est que ces éléments ont des apparences bien différentes entre les navigateurs et bien que certaines parties soient personnalisables, d'autres parties internes sont purement impossibles à mettre en forme.

    Si quelques différences d'apparence ne vous dérangent pas, vous pourrez vous en sortir avec une mise en forme simple afin d'avoir un dimensionnement et une mise en forme cohérente pour des choses comme les couleurs d'arrière-plan. appearance vous permettra d'enlever la mise en forme système.

    Prenons l'exemple suivant, qui illustre certaines fonctionnalités des formulaires parmi les « truands » :

    Dans cet exemple, on utilise le CSS suivant :

    css
    body {
      font-family: "Josefin Sans", sans-serif;
      margin: 20px auto;
      max-width: 400px;
    form > div {
      margin-bottom: 20px;
    select {
      -webkit-appearance: none;
      appearance: none;
    .select-wrapper {
      position: relative;
    .select-wrapper::after {
      content: "▼";
      font-size: 1rem;
      top: 6px;
      right: 10px;
      position: absolute;
    button,
    label,
    input,
    select,
    progress,
    meter {
      display: block;
      font-family: inherit;
      font-size: 100%;
      margin: 0;
      box-sizing: border-box;
      width: 100%;
      padding: 5px;
      height: 30px;
    input[type="text"],
    input[type="datetime-local"],
    input[type="color"],
    select {
      box-shadow: inset 1px 1px 3px #ccc;
      border-radius: 5px;
    label {
      margin-bottom: 5px;
    button {
      width: 60%;
      margin: 0 auto;
    

    Note : Si vous souhaitez tester ces exemples sur différents navigateurs en même temps, vous pouvez les retrouver ici (vous pouvez également voir leur code source).

    Il faut également garder à l'esprit que nous avons ajouté du JavaScript à la page qui liste les fichiers sélectionnés par le sélecteur de fichier (directement après le contrôle). Il s'agit d'une version simplifiée de l'exemple trouvé sur la page de référence pour <input type="file">.

    Comme vous pouvez le voir, on arrive à avoir un aspect relativement uniforme pour les différents navigateurs modernes.

    Nous avons appliqué quelques règles CSS pour normaliser les contrôles et les libellés associés afin que leur dimensionnement, le choix de la police, etc. soient cohérents (voir l'article précédent pour plus d'explications) :

    css
    button,
    label,
    input,
    select,
    progress,
    meter {
      display: block;
      font-family: inherit;
      font-size: 100%;
      margin: 0;
      box-sizing: border-box;
      width: 100%;
      padding: 5px;
      height: 30px;
    

    Nous ajoutons également des ombres et des coins arrondis aux contrôles qui le nécessitent :

    css
    input[type="text"],
    input[type="datetime-local"],
    input[type="color"],
    select {
      box-shadow: inset 1px 1px 3px #ccc;
      border-radius: 5px;
    

    Cette dernière règle, sur d'autres contrôles comme les intervalles, barres de progression, n'auraient pas sens, car ils ajouteraient une boîte moche autour de la zone du contrôle.

    Voyons quelques points relatifs à chaque type de contrôle et les difficultés associées.

    Sélecteurs et listes de données

    Pour les navigateurs récents, les sélecteurs et listes de données ne sont pas trop compliqués à mettre en forme tant que vous ne souhaitez pas trop vous écartez des réglages par défaut.

    Nous sommes parvenus à avoir un aspect de base uniforme et cohérent. Le contrôle de sélection est <input type="text"> , nous savions donc que ce ne serait pas un gros problème.

    Deux choses sont plus difficiles. Pour commencer, l'icône de flèche de sélection pour la liste déroulante qui varie d'un navigateur à l'autre. Elle a également tendance à changer si on augmente la taille de la boîte de sélection. Pour corriger ceci, on utilisera notre vieil ami appearance: none afin de supprimer l'icône :

    css
    select {
      -webkit-appearance: none;
      appearance: none;
    

    Nous allons créer notre propre icone à l'aide de contenu généré. On place un élément contenant le contrôle afin que ::before/::after puissent fonctionner (en effet, ils n'ont pas d'effet sur les éléments <select>, car le contenu généré est placé relativement à la boîte de formatage d'un élément et que les champs de formulaires fonctionnent comme des éléments remplacés et ils n'ont donc pas de boîte de formatage) :

    html
    <div class="select-wrapper">
      <select id="select" name="select">
        <option>Banane</option>
        <option>Cerise</option>
        <option>Citron</option>
      </select>
    

    On utilise alors du contenu généré afin de créer une flèche qui pointe vers le bas et on la situe à la bonne place avec du positionnement :

    css
    .select-wrapper {
      position: relative;
    .select-wrapper::after {
      content: "▼";
      font-size: 1rem;
      top: 6px;
      right: 10px;
      position: absolute;
    

    Le deuxième problème rencontré est l'absence de contrôle sur la boîte de choix qui apparaît lorsqu'on clique sur le sélecteur. Vous pourrez voir que les options de choix n'héritent pas de la police de leur parent. Il est également impossible de définir de façon cohérente l'espacement et les couleurs. Ainsi, Firefox appliquera color et background-color sur <option>, mais Chrome ne le fera pas. Aucun n'appliquera d'espacement (par exemple celui-ci créé avec padding). Il en va de même pour la liste de suggestion pour l'autocomplétion qui apparaît sur une liste de données.

    Si vous avez besoin de contrôler complètement la mise en forme, il vous faudra utiliser une bibliothèque tierce ou construire votre propre contrôle. Une autre alternative pour l'élément <select> consiste à utiliser l'attribut multiple qui fait apparaître l'ensemble des options sur la page, contournant ainsi le problème :

    html
    <select id="select" name="select" multiple>
    </select>
    

    Les champs de date

    Les champs pour les dates et heures ( datetime-local , time , week , month ) partagent le même problème. La boîte englobante et le texte sont assez simples à mettre en forme et ce que nous avons jusqu'à présent est suffisant.

    Toutefois, les parties internes du contrôle (le calendrier qui s'affiche pour sélectionner la date, le sélecteur pour incrémenter/décrémenter les valeurs) ne sont pas du tout personnalisables et on ne peut pas s'en débarrasser avec appearance: none; . Si vous devez maîtriser la forme de 1 à Z, vous devrez utiliser une bibliothèque tierce ou construire votre propre contrôle.

    Note : On notera que <input type="number"> peut souffrir du même problème quant au mécanisme d'incrémentation/décrémentation. Toutefois, on peut contourner certains problèmes, lorsque les données collectées par le contrôle sont simples, en utilisant un champ de type text à la place.

    Les champs d'intervalle

    <input type="range"> est plutôt embêtant à mettre en forme. Vous pouvez utiliser ce qui suit pour retirer la piste par défaut et la remplacer avec une mise en forme personnalisée (ici une fine ligne rouge) :

    css
    input[type="range"] {
      appearance: none;
      -webkit-appearance: none;
      background: red;
      height: 2px;
      padding: 0;
      outline: 1px solid transparent;
    

    Il est toutefois très compliqué de personnaliser le style du curseur. Pour le personnaliser complètement, vous devrez utiliser beaucoup de CSS, y compris des pseudo-éléments non-standards, spécifiques à certains navigateurs. Vous pouvez lire Styling Cross-Browser Compatible Range Inputs with CSS sur CSS-Tricks pour un article détaillé sur ce qu'il faut faire.

    Les sélecteurs de couleur

    Les sélecteurs de couleur ne sont pas trop difficiles. Pour les navigateurs qui les prennent en charge, ils sont généralement affichés avec un carré de couleur entouré d'une bordure.

    Vous pouvez retirer la bordure afin de ne laisser que le bloc de couleur avec une règle comme :

    css
    input[type="color"] {
      border: 0;
      padding: 0;
    

    Toutefois, pour aller plus loin, vous devrez utiliser un contrôle personnalisé de A à Z.

    Les sélecteurs de fichier

    Les sélecteurs de fichier sont plutôt corrects, comme nous l'avons vu dans notre exemple, il est plutôt facile d'obtenir quelque chose qui s'inscrit correctement dans le reste de la page.

    Le seul problème concerne le bouton qui est fourni pour ouvrir l'explorateur de fichier. Ce dernier est complètement hors de contrôle, on ne peut pas le dimensionner ou changer sa couleur, voire changer sa police.

    Une façon de contourner ce problème consiste à se reposer uniquement sur le libellé du contrôle. En effet, lorsqu'un libellé est associé à un contrôle de formulaire, cliquer sur le libellé entraînera l'activation du contrôle. On peut alors choisir de masquer le contrôle même avec quelque chose comme :

    css
    input[type="file"] {
      height: 0;
      padding: 0;
      opacity: 0;
    

    Ensuite, on peut mettre en forme le libellé afin qu'il agisse comme un bouton qui ouvrira le sélecteur lorsqu'on appuiera dessus :

    css
    label[for="file"] {
      box-shadow: 1px 1px 3px #ccc;
      background: linear-gradient(to bottom, #eee, #ccc);
      border: 1px solid rgb(169, 169, 169);
      border-radius: 5px;
      text-align: center;
      line-height: 1.5;
    label[for="file"]:hover {
      background: linear-gradient(to bottom, #fff, #ddd);
    label[for="file"]:active {
      box-shadow: inset 1px 1px 3px #ccc;