Chez Jérémie

Chez Jérémie, parfois c'est sérieux, parfois non !

Le modèle de boite flexible en CSS 3

- Web - Lien permanent

La future norme CSS3 introduits plusieurs façons différentes d'appréhender la mise en page d'un document web. Il y a quelques temps, je vous ai parlé du module "Template Layout". Cette fois je vais vous présenter le module "Flexible Box Layout" qui est sans doute le plus prometteur de tous, d'autant plus qu'il commence déjà à être implémenté (non, pas dans IE, arrêtez de rêver !)

Update: Dear English readers, I've made an English version of this article available on hacks.mozilla.org.

Update 2: There's a Chinese version available... amazing!

Un modèle de boite flexible

CSS3 introduit donc un nouveau modèle de boite en plus du modèle de boite traditionnel issu de CSS 1 et 2. Un modèle de boite flexible permet de déterminer la façon dont des boites sont distribuées au sein d'autres boites (horizontalement ou verticalement) et comment elles se répartissent l'espace disponible entre elles.

Pour ceux qui connaissent, ce modèle de boite est utilisé par XUL (le langage de définition d'interface des produits Mozilla). Des mécanismes similaires sont utilisés par de nombreux autres langages de description d'interface comme XAML ou GladeXML.

Pour résumer de manière un peu simpliste, le modèle de boite flexible est exactement ce qu'il vous faut si vous voulez réaliser des designs liquides (qui s'adaptent automatiquement à la taille de la fenêtre du navigateur) ou élastiques (qui s'adaptent à la taille de la police typographique de l'utilisateur)

Pour la suite de l'article, tous mes exemples se baseront sur le code HTML suivant :

<body>
    <div id="boite1">1</div>
    <div id="boite2">2</div>
    <div id="boite3">3</div>
</body>

Distribuer des boites, kézaco ?

Par défaut, le modèle de boite CSS traditionnel distribue les boites verticalement, les unes au dessous des autres selon l'ordre du flux HTML. Dans le cadre du modèle de boite flexible, on peut spécifier cela explicitement et même inverser l'ordre d'affichage des boites. Le passage au modèle de boite flexible se fait de manière explicite en utilisant la propriété display avec la valeur box (ou inline-box) sur une boite qui en contient d'autres.

Distribution horizontale ou verticale

La propriété box-orient permet de spécifier l'axe de distribution. Cette propriété prend plusieurs valeurs, mais les plus simple à comprendre sont vertical et horizontal. Les autres valeurs (inline-axis et block-axis) ont les mêmes effets, mais permettent en plus de jouer sur la ligne de base de l'alignement (les boites sont alors considérées comme des boites "en ligne").

body{
    display : box;
    box-orient : horizontal;
}

Distribution inversée

La propriété box-direction permet de spécifier l'ordre de disposition des boites. En effet, une fois que vous avez spécifier votre axe de distribution, les boites se disposent selon l'ordre du flux HTML, de haut en bas dans le cadre d'une distribution verticale et de gauche à droite dans le cas d'une distribution horizontale. En utilisant la valeur reverse, vous pouvez inverser l'ordre de disposition des boites par rapport à leur apparition dans le flux HTML. J'ignore ce que cela donnera au point de vue accessibilité, cependant dans la mesure ou ces déclarations sont explicites quand à l'ordre du flux à suivre (contrairement à ce qui se fait avec les techniques du modèle de boites CSS traditionnel), les navigateurs qui implémentent déjà ce modèle de boites arrivent sans trop de difficultés à gérer les mécanismes d'accessibilité de base (l'ordre des tabulations par exemple).

Méfiez-vous de cette propriété car elle influe sur le comportement d'autres propriétés et peut donc donner lieu à des résultats contre intuitif.

body{
    display : box;
    box-orient : vertical;
    box-direction : reverse;
}

Distribution explicite

La propriété box-ordinal-group permet de définir explicitement l'ordre de distribution des boites. Là on obtient l'ultime degrés de la personnalisation en pouvant définir soit même l'ordre de distribution des boites, quelque soit l'ordre des boites dans le flux HTML. Ces groupes sont définie par un nombre commençant à 1 (la valeur par défaut). Le modèle de boite va donc commencer par distribuer ces groupes puis ensuite les boites au seins d'un même groupe. La distribution se fait du plus petit (le groupe 1) au plus grand (le groupe 2). Il y a un petit flou dans la spécification (en tout cas je n'ai pas su la décrypter avec certitude) sur l'influence exacte de box-direction vis à vis du positionnement des groupes, donc attention si vous mélanger l'usage de box-ordinal-group et box-direction.

body{
    display : box;
    box-orient : vertical;
}

#boite1{
    box-ordinal-group : 2;
}

#boite2{
    box-ordinal-group : 2;
}

#boite3{
    box-ordinal-group : 1;
}

Et la flexibilité dans tout ça ?

Si déjà, pouvoir influer sur l'ordre du flux HTML est énorme, là ou ça devient vraiment fun c'est quand on commence à gérer l'espace disponible.

La taille des boites

Par défaut une boite n'est pas flexible. Elle n'est flexible que si elle à la propriété box-flex avec une valeur supérieur ou égal à 1.

Si une boite n'est pas flexible, elle prendra le maximum de place possible afin que sont contenu soit affiché sans débord (overflow). Ce maximum pouvant être fixé avec les propriétés width et height (ou leur version min-* et max-*).

Si une boite est déclarée flexible, sa taille va être calculée en fonction de 2 critères :

  1. Les déclaration de taille explicites (width, height, min-* et max-*)
  2. La taille du conteneur et l'espace restant.

De cette manière, si aucune des boites n'a de déclaration de taille explicite, elle auront toute une taille proportionnel à celle du conteneur. Cela se fera sur le principe suivant , la taille d'une boite est égale à la taille du conteneur multipliée par la valeur de la propriété box-flex de la boite divisée par la somme des valeurs des propriétés box-flex de toutes les boites incluse dans le conteneur.

Par contre, si une ou plusieurs boites ont une taille explicitement définie, la taille de toutes ses boites est cumulé et les boites flexibles se répartissent l'espace restant sur le principe précédent.

Ça à l'aire un peu compliqué comme ça, mais vous allez voir, avec quelques exemple, on comprend mieux.

Toutes les boites sont flexibles

Dans l'exemple ci-dessous, la boite 1 est deux fois plus grande que la boite 2 et la boite 2 à la même taille que la boite 3. Ça ressemble beaucoup à ce que l'on aurait obtenu si on avait utilisé des boites avec des largeurs exprimées en pourcentage. Il y a néanmoins une différence fondamental. Si vous rajoutez une boite, vous n'avez pas besoin de recalculer le pourcentage correspondant de chaque boite. Avec le modèle de boite flexible, à chaque fois que vous allez rajouter une boite, toute les autres se feront plus petites pour laisser de la place à la nouvelle sans intervention de votre part.

body{
    display : box;
    box-orient : horizontal;
}

#boite1{
    box-flex : 2;
}

#boite2{
    box-flex : 1;
}

#boite3{
    box-flex : 1;
}

Certaines boites ont une taille prédéfinie

Dans l'exemple ci-après, la boite 3, qui n'est pas flexible, fera 160px de large et il restera 240 px d'espace libre à distribuer entre la boite 1 et la boite 2, ce qui donnera une largeur de 160px pour la boite 1 (240px x 2/3) et 80px pour la boite 2 (240px x 1/3). Rien ne vous empêche de faire en sorte que la boite 3 soit également flexible. Dans ce cas, le comportement sur la taille de cette boite ressemble à l'application de la propriété CSS min-width.

body{
    display : box;
    box-orient : horizontal;
    width : 400px;
}

#boite1{
    box-flex : 2;
}

#boite2{
    box-flex : 1;
}

#boite3{
    width : 160px;
}

La gestion du débord

Dans la mesure, ou l'on peut mélanger des boites flexibles, des boites rigides et des boites flexibles avec des tailles prédéfinies, on peut se retrouver avec des boites dont la taille cumulé est supérieur ou inférieur à la taille de la boite qui les contient. Donc soit on a trop d'espace, soit on en a pas assez !

Il y a trop d'espace, qu'est-ce que j'en fait ?

L'espace surnuméraire va se répartir en suivant les règles imposées par les propriétés box-align et box-pack.

La propriété box-pack va gérer la répartition horizontal de l'espace et peut prendre 4 valeurs : start, end, justify et center :

  1. start : les boites sont positionné à gauche et tout l'espace restant est positionné à droite ;
  2. end : les boites sont positionné à droite et tout l'espace restant est positionné à gauche ;
  3. justify : l'espace se répartis de manière équitable entre les boites ;
  4. center : l'espace se répartis de manière équitable aux extrémités du conteneur.

La propriété box-align va gérer la répartition vertical de l'espace et peut prendre 5 valeurs : start, end, center, baseline et stretch :

  1. start : Les boites s'alignent sur le bord haut du conteneur et l'espace restant est placé en dessous des boites ;
  2. end : Les boites s'alignent sur le bord bas du conteneur et l'espace restant est placé au dessus des boites ;
  3. center : les lignes médiane des boites s'alignent sur la ligne médiane du conteneur et l'espace restant se répartie équitablement au dessus et au dessous des boites ;
  4. baseline : le bord bas des boites s'alignent sur la ligne de base typographique du conteneur (c'est une interprétation simplifié de la spécification, mais ça donne une idée du comportement)
  5. stretch : Les boites sont étiré pour combler l'espace restant.

Attention à l'interprétation de ces deux propriétés. Elles sont fortement influencé par l'usage des propriétés box-orient et box-direction et peuvent donner lieu à des comportements contre-intuitifs (les comportements des valeur start et end peuvent être complètement inversés par exemple). J'espère que d'ici à la version final de la spécification, on aura plus d'éclaircissement (et d'exemple) sur ces interactions entre propriétés.

body{
    display : box;
    box-orient : horizontal;

    /* Le contenu de body est centré horizontalement */
    box-pack : center;
    /* et verticalement ... \o/ */
    box-align : center;

    width : 100%;
    height : 100%;
}

Il n'y a pas assez d'espace, comment est gérer le débord ?

Comme dans le modèle de boite traditionnel, c'est la propriété overflow qui permet de spécifier le mode de débord des éléments surnuméraire, pas de surprise à ce niveau là.

Néanmoins, là aussi il faut faire attention. En effet, l'usage des propriété box-orient et box-direction peut avoir des effets surprenant comme le fait de voir les élément déborder à droite plutôt qu'à gauche ou en haut plutôt qu'en bas. Prenez le temps d'expérimenter ça tranquillement avant de vous arracher les cheveux sur une mise en page complexe.

Enfin, cerise sur le gâteau, il est possible d'éviter un débord en privilégiant un affichage sur plusieurs lignes (ou colonnes en fonction de l'orientation) grâce à la propriété box-lines : multiple.

Et sinon, ça marche dans la vraie vie ?

Oui, Gecko et Webkit implémente tous les deux une version expérimental de ce modèle de boite (les propriétés sont respectivement préfixées par -moz- et par -webkit- et leur comportement n'est pas garanti). Ce qui signifie que Firefox, Safari, Chrome et les autres navigateurs utilisant ces moteurs de rendu sont capables de s'en servir (à priori depuis un certain temps déjà, Firefox 3.0 et Safari 3 supportant déjà cette fonctionnalité). Si vous avez le bon gout d'utiliser un de ces navigateurs moderne, voici une petite démo en ligne du modèle de boite flexible.

Pour les autres, voila comment devrait s'afficher l'exemple précédent :

Conclusion

Si vous n'avez que faire de Internet Explorer, vous pouvez d'ors et déjà commencer à utiliser cette méthode pour gérer vos mises en page. Attention tout de même. Il s'agit de la première itération d'un "Working Draft" du W3C. Il va donc nécessairement y avoir des changements. Ceci dit, les implémentation de Gecko et de Webkit sont extrêmement cohérente et aboutis donc, si changement il y a, ils ne devraient pas être trop important.

Ce modèle de boite permet de résoudre simplement des problèmes assez récurrent de web design (gestion des formulaires, placement des pieds de page, centrage vertical, dissociation du flux rendu et du flux HTML, etc.). Je vous encourage vivement à vous familiariser avec car il pourrait bien faire partie de notre quotidien de Webdesigner assez rapidement (si Microsoft décide de l'implémenter, ça peut arriver très vite).

Les implémentations disponibles sont déjà suffisamment complètes pour faire beaucoup de choses. Néanmoins, la collision entre le modèle de boite traditionnel et le modèle de boite flexible n'est pas encore très claire (par exemple, impossible d'utiliser la propriété position : relative avec une propriété left ou top sur un élément ayant la propriété box-ordinal-group). Cela va s'affiner, mais ne vous étonnez pas si vos réflexes habituels sont un peu mis à mal. Autre point un peu délicat, l'interaction entre les différentes propriétés spécifiques à ce modèle de boite peuvent parfois donner lieux à des résultats franchement déroutant. Ça vous rappellera sans doute l'époque ou vous avez découvert les boites flottantes ;-)

A lire également sur le sujet