TD4 – HTML / CSS avancé 2/2 display et mise-en-page

Nous allons continuer de modifier notre site pour obtenir le rendu suivant target_TD4.png, nous allons aussi construire un menu déroulant comme ceci :

first_menu.gif

Ordre d’application des sélecteurs CSS

Comme vous vous en souvenez, les sélecteurs servent à sélectionner un ensemble de balises sur lesquels on applique une règle CSS. Nous avons appris lors du TD2 les sélecteurs de base et la combinaison de sélecteurs.

Plusieurs règles CSS peuvent porter sur un même élément HTML. Si ces règles peuvent coexister, elles sont toutes appliquées. Par exemple, si vous avez le code CSS suivant :

div    { background-color:blue; }
.skill { color:red; }

alors un <div class="skill"> aura les deux propriétés background-color:blue et color:red.

Mais il peut aussi arriver que ces dernières soient contradictoires. Par exemple, si vous avez le code CSS suivant, de quelle couleur sera le texte d’un <div> de classe skill ?

.skill { color:red; }
div    { color:blue; }

Pour régler ces conflits, le CSS définit une notion de priorité basée sur l’emplacement des règles CSS puis sur la spécificité des sélecteurs CSS. Dans l’exemple précédent, c’est la première règle qui prévaut comme nous le verrons dans la suite.

Différents emplacements

Il y a plusieurs emplacements possibles pour déclarer du style CSS :

  1. Style externe : (Conseillé) On peut utiliser un fichier de style externe et le lier au document HTML avec la balise <link> dans le <head> :

    <html>
      <head>
        <link rel="stylesheet" type="text/css" href="css/styles.css">
      </head>
      <body> ... </body>
    </html>
    
  2. Style interne : (Déconseillé) On peut inclure des règles CSS directement dans le <head> à l’aide de la balise <style> :

    <html>
      <head>
        <style type="text/css">
          p {font-size: 12pt; color: pink}
        </style>
      </head>
      <body> ... </body>
    </html>
    
  3. Style inline : (Fortement déconseillé) On peut inclure du style CSS directement dans une balise avec l’attribut style :

    <p style="font-size: 12pt; color: fuchsia">
       Aren't style sheets wonderful?
    </p>
    

    Attention à ne pas confondre le style inline avec le futur display:inline.

Enfin les navigateurs appliquent un style par défaut sur les éléments. Cela permet de ne pas avoir à définir à chaque fois les styles les plus classiques. On peut observer le style par défaut dans les outils de développement de Chrome : ce sont les règles de styles associées à user agent stylesheet.

Pour prendre de bonnes habitudes, on préférera les styles externes comme styles.css qui permet une séparation plus claire entre les rôles du HTML (contenu avec des balises pour donner du sens) et du CSS (présentation / mise en page). Comme dit à la fin du TD 2, cette séparation est indispensable et très puissante :

Priorité des sélecteurs

Reprenons l’exemple précédent :

.skill { color:red; }
div    { color:blue; }

Afin de savoir la couleur qui sera appliquée sur les éléments <div class="skill">, des priorités sont définies sur les sélecteurs CSS. Dans l’idée, comme le sélecteur .skill est plus spécifique que le sélecteur div, on va appliquer en priorité la règle color:red;.

La priorité d’un sélecteur CSS est une valeur (a,b,c,d) définie comme suit :

Pour revenir à l’exemple précédent, les règles ont donc comme priorité (en supposant qu’elles sont écrites dans un fichier de style externe) :

 div    /* -> (1,0,0,1) (un sélecteur de balise div)   */
 .skill /* -> (1,0,1,0) (un sélecteur de classe skill) */

L’ordre de priorité

Il reste maintenant à expliquer comment on classe les priorités (a,b,c,d). L’ordre sur les priorités est défini comme l’ordre du dictionnaire (ordre lexicographique) :

Ce mécanisme de priorité s’appelle la cascade et correspond au C de CSS (Cascading Style Sheet).

Exemple : div.skill (priorité (1,0,1,1)) est plus prioritaire que div (priorité (1,0,0,1)) car on a égalité sur a et b, mais c est plus grand pour div.skill.

Dans un fichier texte ou sur papier, écrivez les priorités des sélecteurs suivants et classez-les du plus prioritaire au moins prioritaire. On suppose que toutes ces règles sont définies dans un fichier externe, donc a=1, et les valeurs recherchées commencent toujours par (1,...).

 .titi span
 div span
 nav.titi .tata div div div div div
 ul li div.skill
 #id
 div > a
 div + a

Quelle est la couleur du texte “Priorité CSS” dans les deux cas suivant ? Quelle règle de priorité CSS explique votre réponse ?

  1. Le fichier styles.css contient p {color:blue;}, et le fichier index.html contient

    <html>
      <head>
        <style type="text/css">
          p {color: pink}
        </style> 
        <link rel="stylesheet" type="text/css" href="css/styles.css">
      </head>
      <body><p style="color:red">Priorité CSS</p></body>
    </html>
    
  2. Le même fichier styles.css et le fichier index.html sans la règle inline

    <html>
      <head>
        <style type="text/css">
          p {color: pink}
        </style> 
        <link rel="stylesheet" type="text/css" href="css/styles.css">
      </head>
      <body><p>Priorité CSS</p></body>
    </html>
    

La propriété display

Comme nous l’avons vu au TD précédent, à chaque balise correspond quatre boîtes (content, padding, border et margin, voir la section sur le modèle de boîte du TD précédent).

Box model

La façon dont ces boîtes vont occuper l’espace est gérée par l’attribut display. Nous allons voir dans cette partie les trois valeurs principales de la propriété display.

display:block

Les éléments block sont des éléments :

En pratique, on utilise des éléments de display block :

Notez que les balises de structure que l’on a présentées au TD précédent ont display:block comme style par défaut dans le navigateur, ce qui explique qu’elles s’empilent verticalement comme on l’avait expliqué.

display:inline

Les éléments inline sont des éléments :

En pratique, on utilise des éléments de display inline :

  1. dans du texte, pour ajouter de la sémantique sans interrompre la lecture du lecteur (mettre en exposant un nombre par <sup>, préciser l’importance d’une partie du texte par <strong>, associer un lien avec <a>),
  2. lorsque l’on veut positionner des éléments à la suite.

Puisque associés au texte (<strong>, <a>, …), on trouve en majorité les éléments inline comme feuilles de l’arborescence du HTML.

Notez que les balises au niveau du texte que l’on a présentées au TD précédent ont display:inline comme style par défaut dans le navigateur, ce qui explique qu’elles se comportent comme du texte.

Exemple

Le code HTML suivant

<p style="display:block;">display:block</p> 
<p style="display:inline;">display:inline</p> 
...
<p style="display:inline;">display:inline</p> 
<p style="display:block;">display:block</p>
<p style="display:block;">display:block</p> 
<p style="display:inline;">display:inline</p> 

s’affiche comme suit :

display:block1

display:inline1

display:inline2

display:inline3

display:inline4

display:inline5

display:inline6

display:inline7

display:inline8

display:inline9

display:inline10

display:inline11

display:block2

display:block3

display:inline12

On remarque bien que les display:block prennent toute la largeur, avec un saut de ligne avant et après. Tandis que les display:inline s’affichent les uns à la suite des autres comme le texte d’un paragraphe. Faites varier la largeur de la fenêtre pour voir comment l’affichage s’adapte.

Note (optionnelle) – Règle d’inclusion des éléments inline et block du point de vue du HTML et du CSS :

Inclure des éléments block dans des éléments inline n’est pas conforme en HTML1, mais cela l’est du point de vue du CSS. En modifiant la propriété display d’un élément, nous pouvons donc inclure des éléments block dans des éléments inline. Mais modifier sempiternellement le display naturel du HTML signifie que l’on n’a pas utilisé la bonne méthode (et que le code risque d’être incompréhensible). Nous nous imposons donc de respecter la règle HTML.

Exercices

Nous allons mettre en page le menu de navigation de notre site en mode block puis en mode inline.

Dans ce premier exercice, nous allons créer un menu dans un style block.

  1. Changez le menu de votre site par le suivant

    <nav>
    	<div><a href="./index.html">Accueil</a></div>
    	<div><a href="./facts.html">Facts</a></div>
    	<div><a href="./news.html">Actualités</a></div>
    	<div><a href="./contact.html">Contact</a></div>
    </nav>
    
  2. Puisque <nav> est display:block par défaut (le vérifier sur Chrome si possible), il doit prendre toute la largeur. Inspectez donc votre <nav> pour voir si c’est le cas. Que constatez-vous ?

    Explication : En fait, un élément display:block ne prend pas toute la largeur de <body>, mais de son plus proche block parent. Ici, le père de <nav> est le bloc <header> et donc <nav> prend toute la largeur de <header>.
    Le plus proche block parent s’appelle le containing block.

  3. Puisque <nav> est de type block, nous pouvons fixer ses dimensions. Donnez-lui la largeur 75%. Que constatez-vous ?

    Explication : La largeur d’une balise en display:block est relative à celle de son containing block (<header> ici). Inspectez <header> puis <nav> pour connaître leur largeur. Vérifiez qu’on a bien un rapport de 75%.

  4. Pour centrer le <nav> dans son parent <header>, on va lui donner des marges horizontales auto (gardez les marges verticales à 0).

    Rappel : Nous avons vu dans le dernier TD comment centrer horizontalement. Pour centrer un display:block dont le containing block est plus large, il faut mettre les marges horizontales en auto : elles se règlent alors automatiquement pour compléter la largeur manquante entre le block courant et le containing block. Ceci n’est pas valable pour les marges verticales.

  5. Rajoutez des règles CSS pour que les fils <div> enfants de <nav> aient une couleur de fond #5BBDBF,
  6. Ajoutez une règle CSS pour que les éléments <a> descendants de <nav> aient la couleur de fond #c0d5c2.

Nouvelle mise en page du menu en display:inline cette fois-ci.

  1. Donnez aux <div> enfants de <nav> le display inline.
  2. Vous constatez des espaces entre les entrées du menu, ces derniers sont dus aux espaces dans le HTML, qui sont affichés lorsque les éléments sont inline.
    Une solution temporaire (en attendant display:flex) : pour supprimer les espaces, changez le code des <div> enfant de la balise <nav> en mettant des commentaires :

    <nav>
    	<div><a href="./index.html">Accueil</a></div><!--
     --><div><a href="./facts.html">Facts</a></div><!--
     --><div><a href="./news.html">Actualités</a></div><!--
     --><div><a href="./contact.html">Contact</a></div>
    </nav>
    
  3. Donnez au <nav> une hauteur de 50px (<nav> est block donc on peut lui donner une hauteur),
  4. (Optionnel) Ajoutez du padding horizontal de 10px sur les éléments <a>.
  5. (Optionnel) Ajoutez à ces mêmes éléments <a> une bordure sur la gauche de 2px de style solid et de couleur noire.
  6. (Optionnel) Enlevez la bordure sur le premier de ces éléments.
    Astuce : Il faut utiliser une pseudo-classe vue au TD dernier.

display:none

La valeur display:none enlève complètement un élément du rendu, sans laisser d’espace à l’endroit où il aurait dû être. Nous allons coder en exercice un menu déroulant comme ceci :

first_menu.gif

Dans un premier temps, nous allons juste positionner les sous-menus en dessous de leur titre. Cet exercice met en pratique les position que nous avons vu au TD précédent et que nous vous rappelons.

Position

La propriété CSS position offre de nouvelles possibilités pour le positionnement des éléments. Ses valeurs sont :

Un élément est dit positionné s’il a une position autre que static (qui est la valeur par défaut). Pour indiquer le décalage de position, on utilise les propriétés top, left, right et bottom. Par exemple, les propriétés

position:relative; 
top:20px; 
left:20px; 

vont positionner un élément 20px plus à droite et en bas qu’il n’aurait dû l’être.

Référence : Mozilla Developer Network (MDN)

  1. Ajoutez les sous-menus suivants aux éléments de la navigation. Ces sous-menus doivent être les petits frères des liens <a>, c.-à-d. qu’ils se placent juste après <a> tout en ayant le même père <div>.

    • pour l’ancre “Accueil”

      <div class="submenu">
        <div><a href="./one.html">one</a></div>
        <div><a href="./two.html">two</a></div>
        <div><a href="./three.html">three</a></div>
      </div>
      
    • pour l’ancre “Contact”

      <div class="submenu">
        <div><a href="./other.html">other</a></div>
        <div><a href="./another.html">another</a></div>
      </div>
      
  2. Positionnons bien ces sous-menus : nous souhaitons que l’affichage du reste de la page fasse comme si ces sous-menus n’existaient pas. De plus, nous souhaitons que les sous-menus se placent sous leur titre de menu (le <div> parent “Accueil” ou “Contact”). Nous allons procéder en plusieurs étapes :

    1. Quelle valeur de position correspond à ce comportement des sous-menus ?
    2. Créez la règle CSS qui affecte cette valeur de position aux balises de classe submenu avec un décalage de 50px par rapport au haut et de 0px par rapport à la gauche.
    3. Les sous-menus ne sont pas encore bien placés car ils se positionnent par rapport à la mauvaise balise. Quelle est cette balise par rapport à laquelle ils se sont positionnés ? Relisez la section sur les éléments dits positionnés pour confirmer votre impression.
    4. Nous souhaitons que nos sous-menus se placent par rapport à leur <div> parent. Il va donc falloir rendre ce <div> positionné, sans que cela ne le déplace ni que le reste de la page ne bouge.
      Quelle valeur de position donnée aux <div> parent correspond à la description précédente ? Créez la règle CSS et le menu doit être enfin bien placé.

Nous allons maintenant nous servir de display:none pour afficher le sous-menu juste quand la souris est au-dessus du titre correspondant.

  1. Donnez aux sous-menus la couleur de fond #aca. Puis masquez-les par défaut en CSS.

  2. Réaffichez le premier sous-menu (resp. le deuxième) avec display:block lorsque la souris passe au-dessus de “accueil” (resp. “contact”) en CSS.

    Aide : En pratique, nous allons vérifier si le père <div> du sous-menu est survolé (:hover). Nous souhaitons donc un sélecteur CSS qui sélectionne les éléments de classe submenu qui sont fils d’un <div> survolé qui sont fils d’un <nav>.

À ce stade les sous-menus apparaissent bien lorsque l’on survole les éléments “Accueil” et “Contact”. Par contre, il n’est pas possible d’entrer dans ces sous-menus. Nous consacrons toute la prochaine section au comportement display:flex qui va permettre d’améliorer l’apparence de notre menu et de remédier à ce problème d’accessibilité.

display:flex

La valeur de display flex correspond au layout appelé FlexBox. Appliquée à un élément, la valeur de display flex va permettre de modifier la disposition de ses enfants. C’est donc une différence fondamentale avec les valeurs block et inline qui eux avaient un impact directement sur l’élément lui-même.

Lorsque le display est flex, on peut par exemple :

Nous précisons par la suite quelques-unes de ces contraintes/propriétés.

La propriété flex-direction

La propriété flex-direction permet de préciser si les enfants vont se mettre en ligne ou en colonne et dans quel ordre. Ses valeurs sont :

L’attribut flex-direction s’appliquant aux enfants, il rentre en conflit avec les valeurs de display block ou inline de ces derniers. L’exercice suivant va nous permettre entre autres de savoir qui surcharge l’autre en cas de conflit.

  1. Donnez à l’élément <nav> la valeur de display flex.
  2. Changez la valeur du display des <div> directement enfants de <nav> en block puis en inline. Que cela change-t-il ?
  3. Donnez à l’élément <nav> le bloc de déclaration flex-direction:column, que cela change-t-il ?
  4. Remettez la direction dans sa valeur par défaut, row.
  5. Donnez une largeur fixe de 100px à tous les <div> descendants de <nav> pour que les menus et les sous-menus soient de la même largeur.

La propriété align-items

La propriété align-items permet de préciser comment les enfants vont venir occuper l’espace perpendiculairement à la direction donnée par flex-direction. Dans notre cas (flex-direction:row), align-items va donc nous permettre de préciser comment occuper l’espace vertical des <div> contenus dans le <nav>.

Ses valeurs sont :

Référence : Mozilla Developper Network

Dans cet exercice, nous souhaitons que les <div> titres des menus soient centrés verticalement mais qu’ils prennent toute la hauteur.

  1. Pour mieux voir le comportement, nous allons donner jusqu’à la fin de cet exercice une hauteur de 200px et la couleur de fond #FF00FF à <nav> et, pour les sous-menus, un décalage de 200px vers le bas.

  2. Centrez les éléments enfants de <nav> à l’aide de la propriété align-items de <nav>. Que constatez-vous sur la hauteur des <div> et l’accessibilité des sous-menus avec la souris ? Est-ce que le <div> est centré verticalement ?

    Note : Si rien de ne se passe, utiliser l’inspecteur du navigateur pour comprendre ce qui est centré (la marge automatique placée sur le <nav> par exemple peut expliquer des choses).

  3. Utilisez maintenant la valeur stretch. Que constatez-vous sur la hauteur des <div> et l’accessibilité des sous-menus avec la souris ? Est-ce que le <div> est centré verticalement ?

  4. Faites en sorte que les sous-menus restent accessibles et que les textes “Accueil” et “Contact” soient centrés verticalement dans la barre de navigation. Pour ceci :

    1. Faites en sorte que les <div> prennent toute la hauteur à l’aide des 2 questions précédentes.
    2. Centrez verticalement les liens <a> au sein des <div> : il faut pour cela que les <div> enfants du <nav> soient flex, ce qui va permettre de centrer verticalement ses enfants <a> à l’aide de l’un des 2 comportements vus précédemment.
  5. Annulez (commentez) les propriétés temporaires de la question 1.

La propriété justify-content

Cette propriété permet de préciser la façon dont les enfants vont se disposer dans la direction donnée par flex-direction.

Les valeurs possibles sont :

  1. Justifiez les blocs de titres de menus non à gauche mais à droite de <nav>, avec votre display favori. Colorez temporairement le fond de <nav> pour pouvoir vérifier que les titres se sont bien déplacés sur la droite de <nav>.

Flexbox, une valeur relativement récente

Si ces dernières possibilités offertes par flex semblent triviales voire naturelles pour le néophyte, elles représentent en pratique une avancée majeure dans le monde du CSS. Avant flex, certaines propriétés relevaient d’une expertise véritable de l’intégrateur (exemple : le centrage vertical), ou étaient même confinées dans le domaine du fantasme (les justifications, le comportement des éléments sur l’espace restant, etc.).

Aujourd’hui flexbox est bien implémenté dans les différents navigateurs. Nous ne vous présenterons donc pas d’autres valeurs de display, car elles sont devenues inutiles (display:inline-block, display:table), ni encore moins des techniques d’alignement avec des float, qui ont toujours été techniquement merdiques.

Il y a d’autres propriétés intéressantes autour de flexbox, la référence suivante est très instructive : https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Mise en page globale

En-tête

  1. Placez la citation et le menu de navigation sur la même ligne (la citation sera à gauche et le menu à sa droite) toujours avec votre display favori.
  2. Repositionnez le menu pour qu’il soit en bas de la balise <header> (oui, encore avec flexBox). Si ça n’a pas l’air de fonctionner, pensez à utiliser l’inspecteur pour verifier les marges des différentes boîtes.
  3. Mettez un fond blanc au <header>.
  4. Enlevez la couleur de fond des liens dans le menu.

Deux colonnes

Il est temps d’avoir un layout (aménagement de l’espace) pour notre site.

  1. Donnez au body la width de 900px.
  2. Déplacez dans le HTML la section contenant la <table> dans <aside> si cela n’est pas déjà fait.

  3. Utilisez la valeur de display flex sur la balise <main> pour que ses enfants <article> et <aside> s’affichent comme deux colonnes côte à côte.
  4. Fixez la largeur de <article> à 60%, et celle de <aside> à 35%. Ce dernier élément aura une marge gauche de 5%.
  5. Donnez à <aside> et à <article> la couleur de fond #CCC.

Améliorer la présentation de la table

La table est trop large pour le <aside> et le résultat est assez peu satisfaisant. On va améliorer ça en ajoutant une barre de défilement horizontal dans la table pour obtenir le résultat suivant

Pour cela on va s’intéresser à la propriété CSS overflow-x qui permet de décrire le comportement d’une boite qui dépasse horizontalement de son conteneur (devinez ce que fait overflow-y). On peut lui donner 4 valeurs différentes :

Cette propriété n’est pas compatible avec les éléments en display:inline, car on ne peut pas spécifier leur largeur. Cette propriété doit donc s’utiliser sur des balises en display:block ou display:flex.

Modifiez les propriétés CSS de la table pour faire apparaître la barre de défilement. Attention, il faudra peut-être changer le display de la table.

Comme la table est trop large par rapport à son conteneur, le navigateur essaie de minimiser sa largeur et les noms de la première colonne se retrouvent sur deux lignes au lieu d’une seule. On peut interdire ce comportement au navigateur en ajoutant la propriété white-space: nowrap; à la table.

Finalement, on aimerait bien que la colonne de gauche reste visible quand on fait défiler la page. Ainsi le tableau reste toujours lisible. On va utiliser un autre positionnement que l’on n’a pas encore vu position: sticky;.

Ce positionnement “collant” fonctionne comme relative (c’est-à-dire qu’il occupe sa place normale dans le document) jusqu’à ce qu’un certain seuil soit franchi ; l’élément devient alors positionné de façon fixe. Par exemple, en indiquant position: sticky; et top: 10px;, l’élément concerné sera affiché normalement dans la page jusqu’à ce qu’il se retrouve à 10px du bord haut de son ancêtre positionné et il devient alors fixe.

  1. Utilisez position: sticky; avec la bonne valeur de positionnement sur la première case de chaque ligne du tableau (on pourra utiliser la pseudo-classe :first-child).
  2. Pour améliorer le résultat, il est peut-être necessaire de donner un fond coloré à chaque case (<th> ou <td>) pour qu’elle cache celle qu’elle recouvre.
    Ajouter aussi une bordure à droite des premières cases de chaque ligne.
  3. Pour éliminer les espaces entre les cases, on pourra utiliser la propriété border-spacing:0px; sur la table. Elle indique la quantité d’espace à mettre entres les bordures des cases dans une table.

Cacher ou enlever un élément du rendu

Il existe plusieurs façons de faire disparaitre de l’écran un élément HTML avec un bloc de déclaration :

Nous avons déjà utilisé le bloc de déclaration display:none dans le menu pour cacher un sous-menu sans que ce dernier marque la page par son absence. Le bloc de déclaration visibility:hidden quant à lui laisse l’espace inoccupé à la place de l’élément.

Voyons un usage de visibility:hidden :

Nous voulons marquer visuellement le menu sous la souris par une petite puce dans les <a> du sous-menu.

<span class="puce"></span>

Elle se positionne à gauche du texte, elle n’est visible que lorsque la souris survole le <div> contenant. Dans le cas contraire l’espace reste occupé (pour ne pas faire un effet de flicker/tremblement au survol du menu)

  1. Ajoutez cette puce devant le texte des liens <a>,
  2. Ajoutez visibility:hidden à l’élément de classe puce et la valeur visible sur le survole de la souris sur le <div> parent.
  3. Supprimez la couleur de fond sur les balises <a> pour plus de lisibilité.
  4. (Optionnel) Styliser les sous-menus.

Fini !

Le mot de la fin à propos de flex ?

Moi je stoppe sur mon flex. Quand je sticks ma vibes le dancefloor se breaks et se fluxe dans la vibes.

Gad Elmaleh qui chante du R’n’B

  1. En fait le HTML5 permet cette inclusion dans certains cas