La Cascade

Rechercher

Listes CSS, marqueurs et compteurs

par Rachel Andrew, 29 juillet 2019, css, pseudo-elements, article original paru le 9 juillet 2019 dans Smashing Magazine


Les listes en CSS ont des propriétés propres qui nous donnent le style de liste standard que nous attendons : une liste non ordonnée gagne une puce de liste, de type disque, et les listes ordonnées sont numérotées. Mon intérêt pour l'exploration plus détaillée des listes est né du travail que j'ai effectué pour documenter le pseudo-élément ::marker pour MDN. Ce pseudo-élément est livré dans Firefox 68 et il est publié aujourd'hui. Avec le pseudo-élément ::marker à notre disposition, nous pouvons commencer à faire des choses intéressantes avec les listes, et dans cet article, je vais vous en dire plus.

Déconstruire une liste

Vous n'avez peut-être pas beaucoup exploré les listes, bien que nous les utilisions fréquemment dans notre balisage. De nombreuses choses peuvent être balisées de manière assez logique sous forme de liste : les instructions étape par étape ou les éléments classés peuvent être décrits assez naturellement par une liste ordonnée <ol>, de nombreuses choses dans une conception de page peuvent être décrites à l'aide d'une liste non ordonnée <ul>. Par exemple, ne utilisation très courante de l'élément liste consiste à baliser la navigation, puisqu'il s'agit d'une liste de destinations sur le site. Pour notre exploration, commençons par découvrir ce qu'est exactement une liste en CSS.

Comme pour beaucoup de choses en CSS, les listes se voient appliquer certaines valeurs initiales. Ces valeurs leur donnent l'apparence d'une liste. Ces valeurs spéciales commencent par l'information selon laquelle un élément de liste possède la propriété display avec une valeur de list-item. Cela crée une boîte de niveau bloc, ainsi qu'une boîte de marqueur supplémentaire. La boîte de marqueur est l'endroit où la puce, ou le numéro de la liste, est ajouté.

Les listes ont été définies très tôt dans CSS, et une grande partie de la définition des listes telles que nous les utilisons aujourd'hui provient de CSS2. La spécification CSS2 décrit un élément de liste comme suit :

"Un élément affiché avec display : list-item génère un bloc principal pour le contenu de l'élément et, selon les valeurs de list-style-type et list-style-image, éventuellement aussi un bloc marqueur comme indication visuelle que l'élément est un élément de liste."

La boîte de bloc principale est la boîte principale de l'élément et contient tous les enfants, car un élément de liste peut contenir d'autres balises. La boîte de marqueur est placée par rapport à cette boîte principale. La spécification détaille ensuite le fait que toute couleur d'arrière-plan s'appliquera uniquement derrière cette boîte principale, et non derrière le marqueur. Elle précise également que le marqueur peut être défini sur l'une des valeurs prédéfinies :

  • disc
  • circle
  • square
  • decimal
  • decimal-leading-zero
  • lower-roman
  • upper-roman
  • lower-greek
  • lower-latin
  • upper-latin
  • armenian
  • georgian
  • lower-alpha
  • upper-alpha
  • none
  • inherit

La spécification d'affichage de niveau 3 définit display : list-item avec les autres valeurs possibles pour la propriété display. Elle renvoie à CSS 2.1 — comme de nombreuses propriétés et valeurs CSS qui proviennent de CSS2 — mais décrit le mot-clé list-item comme "faisant en sorte que l'élément génère un pseudo-élément ::marker".

La spécification de niveau 3 introduit également la possibilité de créer un élément de liste en ligne (inline) avec la syntaxe à deux valeurs utilisée display : inline list-item. Ceci n'est pas encore implémenté par les navigateurs.

Création de boîtes marqueur sur des éléments autres que des listes

Comme pour les autres valeurs de display, il est parfaitement valable de donner à tout élément HTML un type d'affichage de list-item (si vous souhaitez générer un pseudo-élément ::marker sur l'élément). L'élément ne deviendra pas sémantiquement un élément de liste, mais il ne s'affichera visuellement que comme un élément de liste, et pourra donc avoir un ::marker. Lorsque nous aborderons le pseudo-élément ::marker ci-dessous, vous découvrirez certains cas où donner à d'autres éléments display : list-item peut être utile.

La spécification des listes CSS de niveau 3, ::marker et les compteurs

La spécification d'affichage étend et clarifie la définition des listes que nous trouvons dans CSS2, cependant, il existe également une spécification qui définit le comportement des listes en détail : la spécification CSS Lists Level 3. Comme le comportement de base des éléments de liste est défini dans display, cette spécification détaille la boîte de marqueurs générée lorsque quelque chose a display : list-item ainsi que les compteurs qui sont utilisés par défaut lorsque vous créez une liste ordonnée. Ces caractéristiques permettent d'accéder à certaines fonctionnalités potentiellement utiles.

Le pseudo-élément ::marker

Le pseudo-élément ::marker vous permet de cibler le marqueur de liste — séparément du contenu de l'élément de liste. Cela n'était pas possible dans les versions précédentes de CSS. Par conséquent, si vous modifiez la couleur ou la taille de la police du ul ou du li, cela modifie également la couleur et la taille de la police des marqueurs. Pour faire quelque chose d'apparemment simple comme avoir des puces de liste de couleur différente de celle du texte, il faudrait soit envelopper le contenu de l'élément de liste dans un span (ou utiliser une image pour le marqueur).

ul {
  color: #00b7a8;
}

ul span {
  color #333;
}

Avec le pseudo-élément ::marker, la chose la plus simple que vous pourriez vouloir essayer est d'avoir une couleur de puce à texte différente, ce qui signifie qu'au lieu du code dans l'exemple ci-dessus, vous pouvez utiliser :

ul {
  color: #333;
}

ul ::marker {
  color: #00b7a8;
}

Vous pouvez également utiliser une taille et une police de caractères différentes pour la numérotation d'une liste ordonnée.

ol ::marker {
  font-size: 200%;
  color: #00b7a8;
  font-family: 'Comic Sans MS', cursive, sans-serif;
}

Vous pouvez voir tout cela dans un navigateur compatible en utilisant mon exemple CodePen :

Vous pouvez utiliser le pseudo-élément ::marker sur les éléments qui ne sont pas des listes. Dans le code ci-dessous, j'ai défini un titre pour display : list-item. Cela lui donne une puce et donc une boîte ::marker à cibler.

J'ai changé la puce pour utiliser un emoji :

h1 {
  display: list-item;
}

h1::marker {
  content: '🐱';
}
Dans firefox on peut voir l'emoji utilisé comme marqueur
voir Smashing heading and ::marker de rachelandrew dans CodePen

Dans l'exemple ci-dessus, j'ai utilisé du contenu généré dans les règles relatives au marqueur. Seul un petit sous-ensemble de propriétés CSS est disponible pour une utilisation sur ::marker. Il s'agit notamment des propriétés de police et de la couleur, mais aussi de la propriété content, permettant d'inclure du contenu généré.

L'ajout de la propriété content comme propriété autorisée pour ::marker est récent, cependant, elle est incluse dans l'implémentation de Firefox. Cet ajout nous permet de faire des choses comme inclure une chaîne de texte dans un ::marker. Elle offre également des possibilités supplémentaires pour le formatage des marqueurs lorsque vous combinez l'utilisation de compteurs avec ::marker.

Prise en charge par les navigateurs et solutions de repli

Pour les navigateurs qui ne prennent pas en charge le pseudo-élément ::marker, la solution de repli est le marqueur ordinaire qui aurait été affiché de toute façon. Malheureusement, nous ne pouvons pas actuellement utiliser les Feature Queries pour détecter la prise en charge de sélecteurs tels que ce pseudo-élément, bien qu'un "ticket" ait été ouvert concernant l'ajout de cette fonctionnalité à la spécification. Cela signifie que vous ne pouvez pas avoir une "fourche" dans votre code pour faire une chose lorsque vous avez la prise en charge et autre chose si vous ne l'avez pas. Dans la plupart des cas, se rabattre sur le marqueur ordinaire sera une solution raisonnable.

Compteurs

Les listes ordonnées ont une numérotation de liste — ce qui est réalisé par le biais d'un compteur CSS. La spécification CSS Lists décrit donc également ces compteurs. Nous pouvons accéder aux compteurs et les créer nous-mêmes, ce qui, combiné au pseudo-élément ::marker, peut nous donner des fonctionnalités utiles. Ces compteurs peuvent également être utilisés dans du contenu généré ordinaire (non ::marker).

Si j'ai une liste d'étapes numérotées (et que je souhaite écrire "Étape 1", "Étape 2", etc.), je peux le faire en utilisant le contenu généré dans mon marqueur et en ajoutant le compteur list-item, qui représente le compteur intégré :

::marker {
  content: 'Etape ' counter(list-item) ': ';
}
Dans firefox on peut voir le compteur, préfixé par le mot 'Step'
voir Smashing heading and ::marker de rachelandrew dans CodePen

Compteurs imbriqués

Si vous avez des listes imbriquées, une façon courante de les numéroter est de donner à l'élément de premier niveau un nombre entier, (1), puis aux éléments enfants (1.1, 1.2) et à leurs enfants (1.1.1, 1.1.2), et ainsi de suite. Vous pouvez y parvenir en utilisant d'autres fonctionnalités des compteurs.

Lorsque vous imbriquez des listes HTML, vous vous retrouvez avec plusieurs compteurs du même nom — imbriqués les uns dans les autres. Vous pouvez accéder à l'imbrication des compteurs à l'aide de la fonction counters().

Dans le code ci-dessous, j'utilise counters() pour formater mes marqueurs de liste comme décrit ci-dessus. Le premier argument de counters() est le nom du compteur à utiliser. J'utilise le compteur intégré list-item. Le deuxième argument est une chaîne — c'est ce qui sera concaténé entre les compteurs de sortie (j'utilise un .). Enfin, j'ajoute un : à l'extérieur de la fonction compteur mais à l'intérieur de la valeur du contenu afin que la sortie de mon compteur soit séparée du contenu par un deux-points.

::marker {
  content: counters(list-item, '.') ':';
  color: #00b7a8;
  font-weight: bold;
}

Cela me donne une sortie comme dans l'image. Si vous utilisez un navigateur qui prend en charge ::marker et les compteurs, vous pouvez le voir fonctionner dans l'exemple CodePen - essayez de changer la chaîne de caractères d'un . à autre chose pour voir comment cela change la sortie.

Dans firefox on peut voir la numérotation des listes et sous-listes séparées par un point
voir Nested Counters de rachelandrew dans CodePen

Quelle est la différence entre counter() et counters() ?

La fonction counter() que nous avons utilisée dans le premier exemple pour écrire nos étapes utilise uniquement le compteur le plus intérieur. Par conséquent, dans la situation où vous avez un ensemble de listes imbriquées, vous écrirez le compteur qui correspond au niveau où vous vous trouvez actuellement.

La fonction counters() écrit essentiellement cette branche entière et vous donne la possibilité de concaténer une chaîne entre les compteurs de la branche. Ainsi, si vous avez un élément de liste avec un compteur de 2 (qui fait partie d'une liste imbriquée dans un élément de liste avec un compteur de 4), alors la branche contient :

  • 4
  • 2

Vous pouvez l'afficher sous la forme 4.2 dans le marqueur en utilisant :

::marker {
  content: counters(list-item, '.');
}

Compteurs sur d'autres éléments

Les compteurs peuvent être utilisés sur des éléments qui ne sont pas des listes — soit pour afficher un marqueur — auquel cas l'élément devra avoir display : list-item - soit pour afficher du contenu généré régulièrement. Les compteurs sont largement utilisés dans la production de livres, afin de permettre la numérotation des chapitres et des figures, entre autres choses. Il n'y a aucune raison de ne pas adopter une approche similaire sur le Web, en particulier pour les articles plus longs.

Les propriétés CSS définies dans la spécification CSS Lists qui traitent de ces compteurs sont :

  • counter-set
  • counter-reset
  • counter-increment

Pour voir comment elles fonctionnent en dehors des listes, nous pouvons examiner un exemple d'utilisation de compteurs pour numéroter les titres d'un document.

La première chose à faire est de créer un compteur d'en-têtes sur l'élément body, prêt à être utilisé. Pour ce faire, j'utilise la propriété counter-reset. Les propriétés counter-reset et counter-set sont très similaires. La propriété counter-reset créera un nouveau compteur si un compteur du nom spécifié n'existe pas déjà, mais créera également des compteurs imbriqués comme décrit ci-dessus si un compteur de ce nom existe. La propriété counter-set, quant à elle, créera un nouveau compteur uniquement s'il n'existe pas de compteur de ce nom. Cependant, counter-set n'est pas aussi bien supporté par les navigateurs que counter-reset. Je choisis donc la voie la plus pratique :

body {
  counter-reset: heading-counter;
}

Maintenant que j'ai un compteur, je peux utiliser la propriété counter-increment sur le sélecteur pour les en-têtes ; cela devrait incrémenter le compteur chaque fois que le sélecteur correspond.

h2 {
  counter-increment: heading-counter;
}

Pour voir la valeur, je dois la sortir dans le document. Je peux le faire en utilisant le contenu généré et en l'ajoutant avant le titre, comme le montre l'exemple CodePen suivant :

h2::before {
  content: counter(heading-counter) ' : ';
  color: #00b7a8;
  font-weight: bold;
}
voir Smashing: headings and counters de rachelandrew dans CodePen

Sinon, je pourrais transformer l'élément h2 en élément de liste et utiliser ensuite ::marker, comme illustré ci-dessous. Comme déjà détaillé, l'utilisation de l'élément ::marker a une prise en charge limitée par les navigateurs. Dans Firefox, vous devriez voir le compteur utilisé comme marqueur pour le titre, tandis que les autres navigateurs afficheront la puce par défaut.

h2 {
  display: list-item;
}

h2::marker {
  content: counter(heading-counter) ': ';
  color: #00b7a8;
  font-weight: bold;
}

Compteurs sur les éléments de formulaire

Il y a aussi un peu d'interactivité que vous pouvez réaliser en utilisant les compteurs CSS — des choses que vous imaginiez peut-être possible uniquement avec JavaScript :

J'ai un formulaire qui comporte un certain nombre de champs obligatoires. Le statut obligatoire peut être sélectionné en CSS avec une pseudo-classe :required, et le fait qu'un champ n'ait pas été rempli peut être détecté au moyen de la pseudo-classe :invalid. Cela signifie que nous pouvons vérifier les champs qui sont à la fois requis et invalides, et incrémenter un compteur. Il suffit ensuite de l'afficher sous forme de contenu généré.

L'utilité de cette valeur dans la réalité est discutable — étant donné que nous ne pouvons pas vraiment en faire autre chose que de la coller dans le contenu généré. Il existe également des inquiétudes quant à l'inaccessibilité du contenu généré pour certains lecteurs d'écran. Par conséquent, toute utilisation plus que décorative devra prévoir d'autres moyens d'accéder à ces informations. Lisez "Accessibility Support For CSS Generated Content" et les informations plus récentes, "CSS Content Property Screen Reader Compatibility" pour plus de détails concernant l'accessibilité et le contenu généré.

Toutefois, cela démontre que les compteurs peuvent réaliser des choses plus utiles que la simple numérotation de listes. Il se peut qu'un jour ces connaissances vous soient utiles pour résoudre un problème sur lequel vous travaillez.

En savoir plus

Cet article s'est avéré assez éloigné de la stylisation des listes, bien que tout ce que j'ai décrit se trouve dans la spécification CSS Lists. Vous pouvez trouver plus d'informations sur les choses décrites dans les liens ci-dessous.

Voir la liste des articles de Rachel Andrew traduits dans La Cascade.
Article original paru le 9 juillet 2019 dans Smashing Magazine
Traduit avec l'aimable autorisation de Smashing Magazine et de Rachel Andrew.
Copyright Smashing Magazine © 2019