Un gabarit de site à page unique sans javascript
Cet article a subi une importante refonte en 2018 afin de coller aux techniques & navigateurs actuels.
- Si vous visez la compatibilité IE8-9 (~1% des utilisateurs mondiaux en Juin 2018), vous pouvez continuer la lecture.
- Sinon, je vous invite à consulter la version 2018, qui repose sur l’utilisation de Flexbox et de l’unité
vh
, et fonctionne sur tous les navigateurs actuels et IE10-11.
Le but de cet article est de partager une technique de mise en page simple et efficace qui remplit les conditions suivantes :
- Une rubrique (page) occupe tout l’espace du viewport,
- Son contenu est centré verticalement,
- Une hauteur de contenu supérieure à la hauteur du viewport ne doit pas pénaliser la mise en page,
- Mise en page en CSS sans javascript,
- Compatible IE8+
<!DOCTYPE html>
<html>
<head></head>
<body>
<ul class="menu">
<li><a href="#page-1">Page1</a></li>
<li><a href="#page-2">Page 2</a></li>
</ul>
<div id="page-1" class="sectionWrapper">
<div class="contentWrapper">
<!-- Contenu Page 1 -->
</div><!-- /contentWrapper -->
</div><!-- /sectionWrapper -->
<div id="page-2" class="sectionWrapper">
<div class="contentWrapper">
<!-- Contenu Page 2 -->
</div><!-- /contentWrapper -->
</div><!-- /sectionWrapper -->
</body>
</html>
html, body { height:100%; }
.sectionWrapper { display:table; height:100%; }
.contentWrapper { display:table-cell; vertical-align:middle; }
Un élément sur lequel on applique une valeur en pourcentage prendra comme hauteur référence celle de son parent. On remonte donc l’arborescence HTML en appliquant height:100%
sur les éléments parents pour permettre aux rubriques d’occuper toute la hauteur du viewport.
Le centrage vertical du contenu est obtenu grâce à l’utilisation du vertical-align
sur un conteneur de type table-cell
.
Allez comme c’est bientôt noël, je laisse en cadeau pourri le fix IE7 :
.ie7 .sectionWrapper { height:auto; min-height:100%; }
Si vous voulez ajouter un effet de défilement à la navigation (de type « smooth scroll »), c’est par ici que ça se passe.
Et voilà, plus simple (et performant) que d’utiliser un script qui va calculer la dimension du viewport et adapter les tailles des conteneurs en conséquence, non ?
Cependant cette technique peut poser quelques soucis, notamment lorsqu’il s’agit d’afficher le contenu dans une colonne centrale. Heureusement nous allons voir que ce problème n’a rien d’insoluble.
Limites de la technique et solutions
Une colonne centrale
Supposons que l’on souhaite afficher le contenu de nos pages dans une colonne de largeur fixe centrée horizontalement et disposant d’un arrière-plan. Côté HTML/CSS, on est tenté de partir sur quelque chose comme ça :
<div id="global">
<ul class="menu">[...]
<div id="page-1" class="sectionWrapper">[...]
<div id="page-2" class="sectionWrapper">[...]
</div><!-- /global -->
#global { width:990px; margin:0 auto; background:lightblue; }
Le centrage horizontal fonctionne bien, par contre on perd notre effet de mise en page full viewport pour les rubriques. Cela est logiquement dû au fait que nos conteneurs n’ont aucune référence de hauteur sur laquelle se baser (la hauteur de #global
n’étant pas précisée). On va donc corriger cela :
#global { height:100%; [...] }
On constate que l’on retrouve bien l’effet de mise en page, mais on perd l’arrière-plan sur les pages secondaires. En limitant la hauteur de #global
à 100%
, on l’empêche également de s’adapter en hauteur aux éléments qu’il contient !
Et là, même en bidouillant les éléments avec du min-height
, impossible d’obtenir quelque chose de convaincant. Il va donc falloir ruser au moyen d’une des feintes ci-dessous :
-
En appliquant l’arrière-plan sur les conteneurs des pages.
.sectionWrapper { background:lightblue; [...] }
-
En utilisant un conteneur en position fixe pour simuler l’arrière-plan (le code ci-dessous propose une solution à base de pseudo-élément mais rien ne vous empêche d’utiliser à la place un conteneur supplémentaire).
#global:before { content:''; width:990px; height:100%; position:fixed; top:0; left:50%; margin-left:-495px; /* = width/2 */ z-index:-1; background:lightblue; }
-
En utilisant une image sur
<body>
pour simuler la colonne.body { background:url(colonne.png) repeat-y center; }
Centrer horizontalement et verticalement la page (sans colonne)
Mais peut-être souhaitez-vous limiter la hauteur d’une page à son contenu, en laissant libre l’espace (au dessus et en dessous) dégagé par le centrage vertical ?
Un conteneur en table-cell
reproduit le comportement standard d’une cellule de tableau (<td>
), et occupe donc (en largeur et en hauteur) tout l’espace de son parent.
Pour appliquer un arrière-plan (qui n’occupe pas toute la surface disponible) à notre contenu, on devra donc utiliser un conteneur supplémentaire (dédié au centrage vertical) :
<div id="page-1" class="sectionWrapper">
<div class="vAlignWrapper">
<div class="contentWrapper">
<!-- Contenu Page 1 -->
</div><!-- /contentWrapper -->
</div><!-- /vAlignWrapper -->
</div><!-- /sectionWrapper -->
html, body { height:100%; }
.sectionWrapper { display:table; height:100%; width:100%; }
.vAlignWrapper { display:table-cell; vertical-align:middle; }
.contentWrapper { width:990px; margin:0 auto; background:lightblue; }
Dans cet exemple le centrage horizontal est réalisé directement sur .contentWrapper
, mais on aurait pu également l’appliquer sur .sectionWrapper
ou sur un conteneur global comme vu précédemment.
Voilà voilà. Si vous êtes confrontés à un autre problème ou si vous avez trouvé une feinte supplémentaire pour la colonne ou le centrage vertical, n’hésitez pas à partager via les commentaires !
Cet article a été récemment mis à jour, consultez la version 2018.
Commentaires (2)
Lâcher un com'
Bonjour,
Je vous remercie pour cet article qui m’a permis de confirmer mon développement actuel.
Par contre avec ces tables qui prennent toute la hauteur de l’écran, comment peut-on mettre un footer en bas de page ?
Actuellement celui-ci se positionne juste après le #global qui fait 100% et non la taille réelle de l’ensemble des pages.
Merci de votre aide.
Paul
Bonjour Paul,
Effectivement le positionnement du footer en bas de page peut être problématique, mais on peut y remédier en utilisant un peu de positionnement absolu :
<div id="page-3" class="sectionWrapper">
<div class="contentWrapper">[...]</div>
<div id="footer">[...]</div>
</div>
#page-3 {
position:relative;
}
#page-3 .contentWrapper {
padding-bottom:100px; /* hauteur footer */
}
#footer {
position:absolute; bottom:0; left:0;
width:100%; height:100px;
}
(le footer doit être enfant de la dernière page, ici #page-3)
Laisser un commentaire