Quelques pièges classiques en HTML/CSS

Alexandre Niveau
GREYC — Université de Caen

Pourquoi les items ne tiennent-ils pas sur la ligne ?

Voir la démo

Réponse : Ne pas oublier les marges et paddings par défaut des éléments ! Ici, ce sont les 40px de padding-left du ul qui sont en cause, mais aussi le fait que box-content n'est pas border-box par défaut : le padding de chaque li s'ajoute à la largeur demandée !

Voir la solution

Pourquoi les deux blocs ne sont-ils pas côte-à-côte ?

Voir la démo

Réponse : display: inline-block transforme le bloc en élément inline, c'est-à-dire qu'il se comporte plus ou moins comme un mot… en particulier les espaces dans le HTML autour de l'élément se voient dans le rendu. Ne pas utiliser inline-block pour faire de la mise en page précise ! Ici une solution est de mettre la taille de la police à 0 dans le wrapper puis de la remettre à la taille voulue (typiquement 1rem) dans les blocs, mais c'est du bricolage…

Voir la solution

Pourquoi les items ne changent-ils pas de couleur au survol ?

Voir la démo

Réponse : Pas à cause de l'ordre des règles, mais à cause de la spécificité : a:hover a pour spécificité 0,1,1 (1 sélecteur de catégorie « élément », 1 sélecteur de catégorie « classe ») et .liste li a a pour spécificité 0,1,2 (2 sélecteurs de catégorie « élément », 1 sélecteur de catégorie « classe »), c'est donc le second qui gagne quand les deux règles s'appliquent. Pour que ça fonctionne comme on veut, le plus logique est de rendre les sélecteurs cohérents entre eux : .liste li a et .liste li a:hover par exemple. NB: ici le li ne sert à rien, on pourrait aussi simplement l'enlever et changer l'ordre des règles, mais ce genre de choix dépend de tout le reste du CSS.

Voir la solution

Que se passe-t-il avec la hauteur du bloc ?

Voir la démo

Réponse : La hauteur d'un élément ne peut être fixée en pourcentage que si un de ses parents a une hauteur fixe. Dans l'exemple, ce qui ne marche pas, c'est qu'une valeur pour min-height n'est pas considéré comme une valeur pour height, même si le résultat est le même ! Astuce : mettre html { height: 100%; } permet de fixer librement la hauteur du body en fonction de la hauteur de la fenêtre (moins utile maintenant avec l'unité vh).

Voir la solution

D'où vient cette marge ?

Voir la démo

Réponse : du margin collapsing. La marge de l'élément fils fusionne avec celle de l'élément parent car elle n'est pas bloquée (par exemple par une bordure, un padding, du contenu…). Ça arrive aussi entre deux éléments frères. Voir par exemple MDN.

Voir la solution

Pourquoi je n'arrive pas à enlever le soulignement ?

Voir la démo

Réponse : text-decoration s'applique sur tous les mots contenu dans l'élément, y compris les éléments fils. Ce n'est pas un héritage classique. Voir MDN et cette explication.

Voir la solution

Que se passe-t-il avec l'image de fond ?

Voir la démo

Réponse : le background sur le body est un cas particulier, pour des raisons de rétrocompatibilité. Il s'applique en fait à l'élément racine, sauf si celui-ci a déjà un background. Une solution est donc de mettre un fond blanc à l'élément html. Voir la spécification W3C.

Voir la solution

Pourquoi y a-t-il de l'espace vertical sous les items ?

Voir la démo

Réponse : L'explication est longue, mais la solution est simple.
  • La valeur par défaut de vertical-align d'un élément inline-block est baseline
  • Ça signifie que la baseline de l'élément est alignée avec la baseline du parent
  • Or, la baseline d'un inline-block est celle de sa dernière ligne de texte… sauf si elle est en overflow autre que visible !
  • Dans ce cas, sa baseline est définie comme étant le bas de sa boîte.
  • dans notre cas, nos items vont donc être « posés » sur la baseline du conteneur
  • Or la baseline d'un bloc est toujours un peu plus haute que le bas du bloc (il faut garder de la place pour les caractères qui descendent sous la baseline, comme « j » ou « g »)
  • c'est cet espace vertical que l'on retrouve sous nos items

Tout ça était abordé dans la page du cours consacrée à inline-block et vertical-align !

Solutions possibles :

  • Changer la valeur du vertical-align des enfants. Avec top, middle ou bottom, le problème disparaît. C'est la solution « propre ».
  • En mettant la taille de police à 0 dans le conteneur (et en la remettant, par exemple à 1rem, dans les enfants), la baseline se colle au bas du conteneur (puisque les caractères ont une hauteur nulle, ils n'ont pas besoin de place pour dépasser).

Voir la solution

Pourquoi ne puis-je pas cliquer sur le lien ?

Voir la démo

Réponse :

La line-height dans les items de la bannière est inférieure à 1, donc le contenu est plus grand que la boîte. Habituellement ce n'est pas gênant, mais comme c'est un lien en display:block dans un élément inline-block, la surface cliquable dépasse de la boîte (essayer d'enlever le display:block du lien ou le display:inline-block de l'item, et observer ce qui se passe). (NB : je ne sais pas ce qui justifie ce comportement dans les spécs, mais je n'ai pas non plus trop cherché.)

C'est déjà embêtant, mais ici on a un problème plus grave : étant donné que le bloc principal flotte, le lien qui est dedans est situé derrière la zone cliquable du titre. C'est encore un problème d'interaction : si on enlève le float:left, le lien devient cliquable ! (NB : encore une fois, je ne sais pas ce qui justifie ce comportement dans les spécs, mais je n'ai pas non plus trop cherché.)

Ce n'est cependant pas une solution propre, car le lien dépasse toujours, même s'il n'empêche pas de cliquer sur les autres liens. Pour empêcher la zone cliquable de dépasser du bloc, overflow:hidden suffit.

Voir la solution

Comment faire changer de couleur la première ligne au survol ?

Voir la démo

Réponse : Le problème est « tout simplement » que div:hover::first-line ne marche pas si div::first-line n'est pas utilisé (c'est comme si le pseudo-élément n'était pas défini). Voir cette réponse sur StackOverflow, mais elle n'explique rien : je ne comprends pas ce qui justifie ce comportement dans les spécs. (On ne peut pas qualifier ça de piège classique, mais pour débugger du CSS il faut prendre l'habitude de se préparer à toutes les possibilités !)

Voir la solution

Autres pièges

  • Une valeur en pourcentage dans un margin ou un padding fait référence à la largeur de l'élément parent… y compris les marges et paddings verticaux ! (spéc W3C)
  • Mettre un conteneur en display:table-cell et vertical-align:middle permet de centrer son contenu verticalement, mais attention: ça ne fonctionnera pas si le conteneur est position absolute ou fixed, ou s'il flotte, car son display sera automatiquement considéré comme block (spéc W3C)