Jouons avec Flexbox et les Quantity Queries

Ayant lu l’article sur les quantity queries, Aaron Gustafson expérimente leur mise en oeuvre sur son site... avec Flexbox bien sûr, pour aller jusqu’au bout de l’aventure!

Par

Après avoir lu l’article de Heydon Pickering sur les Quantity Queries, j’ai commencé à gamberger sur leurs utilisations possibles pour nos layouts. J’ai décidé de jouer un peu avec le concept sur mon site, pour en améliorer le design qui n’a pas évolué depuis un certain temps. Et puis, vu que je me lance dans l’expérimentation, j’ai pensé que ce serait drôle d’y joindre également Flexbox.

Voici un survol rapide du projet :

L’heureuse élue : ma page speaking engagements.
Le défi : La liste “Futur” s’agrandira ou se rétrécira à mesure que les engagements évolueront, donc je ne sais jamais combien il y en aura. La liste “Passé” doit aussi s’agrandir, mais je suis moins intéressé par elle.
L’idée : une grille de mise en page flexible mettant en valeur 1 ou 2 événements à venir et qui permet aux autres de fluctuer avec une dimension définie par défaut. Elle doit pouvoir s’adapter à toutes les situations, depuis un événement unique jusqu’à une douzaine ou plus.

Mon esquisse du projet

Le markup était assez simple, juste une liste d’événements. Une fois ce point réglé, je me suis mis au travail.

En file indienne

Pour commencer, j’ai utilisé une syntaxe Flexbox très basique en m’intéressant au container et à la vue la plus simple, en pleine largeur, sur écran réduit :

.listing--events { 
    display: flex;
} 
.event { 
    box-sizing: border-box;
    padding: 1.25rem; /* 20px padding */
    margin: 0 0 1.25rem 0; /* 20px bottom margin */
    flex: 0 0 100%;
}

Vous vous demandez peut-être où sont passées toutes les règles de styles expérimentales. J’utilise Autoprefixer pour tous les préfixes constructeurs et mon code reste clean et basé sur les standards.

Ce CSS tout simple nous donne exactement ce que nous attendions : une liste verticale d’événements séparés par un espace de 20px.

Des événements superposés

Deux par deux

Ensuite, je me suis attaqué au premier point de rupture à 28,75em :

.listing--events { 
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
}
.event {
    box-sizing: border-box;
    padding: 1.25rem; /* 20px */
    margin: 0 0 1.25rem 0; /* 20px v */
    flex: 0 0 100%;
}
@media only screen and (min-width: 28.75em) {
    /* 20px gap divided by 2 events per row */
    .event {
    flex: 0 0 calc( 50% - 1.25rem / 2 );
    margin-left: 1.25rem; /* 20px < */
    }
    /* Supprimer la marge à gauche pour le 1er dans la rangée */
    .event:nth-child(odd) {
        margin-left: 0;
    }
    /* Reset des marges sur les événements "futurs" */
    .event--future:nth-child(odd) {
        margin-left: 1.25rem;
    }
    .event--future:nth-child(even) {
        margin-left: 0;
    }
    /* Quantity Query - quand il y en a plus d’1, le 1er événement s’étend sur les deux colonnes */
    .event--future:nth-last-child(n+1):first-child {
        flex: 0 0 100%;
        font-size: 1.5em;
        margin-left: 0;
    }
}

Dans cette deuxième passe, j’ai fixé les blocs d’événements à 50% de leur container parent — en fait 50% moins la gouttière de 1,25rem entre eux, en utilisant calc(). Pour permettre aux enfants de passer à la ligne (wrap) de manière à former des rangées, j’ai fait flex-wrap: wrap sur la liste .listing--events. Puis, pour donner à tous les enfants une hauteur égale sur chaque rangée, j’ai utilisé align-items: stretch. La gouttière a été réalisée via des marges à gauche sur tous les événements sauf les premiers de la rangée : .event:nth-child(odd).

Il est important de noter que dans la pleine page j’ai deux ensembles de listings d’événements : les événements passés et futurs. La classe “event” est utilisée pour toutes les instances. La classe modifiée “future” est ajoutée aux événements qui ne sont pas encore arrivés.

J’utilise ensuite une quantity query pour sélectionner le premier événement futur lorsqu’il y en a plus d’un dans la liste et je lui donne une étendue de 100% de la largeur de son parent. Pour que les gouttières restent exactes, j’ai modifié les marges là où on les appliquait et j’ai ajouté une marge à .event--future:nth-child(odd) et je l’ai supprimée de .event--future:nth-child(even).

Le premier événement occupe toute la largeur de la page, les suivants occupent 50% de leur rangée

À trois on est une foule

Enfin, j’ai pu m’attaquer au troisième layout, le plus compliqué. Les choses commençaient à être trop larges aux alentours de 690px, donc j’ai fixé un breakpoint à 43,125em

.listing--events { display: flex; flex-wrap: wrap; align-items: stretch; }
.event { box-sizing: border-box; padding: 1.25rem; /* 20px */ margin: 0 0 1.25rem 0; /* 20px v */ flex: 0 0 100%; }

@media only screen and (min-width: 28.75em) { 
    /* 20px gap divided by 2 events per row */
    .event { flex: 0 0 calc( 50% - 1.25rem / 2 ); margin-left: 1.25rem; /* 20px < */ }
    /* Supprimer la marge à gauche pour le 1er dans la rangée */
    .event:nth-child(odd) { margin-left: 0; }
    /* Reset des marges sur les événements "futurs" */
    .event--future:nth-child(odd) { margin-left: 1.25rem; }
    .event--future:nth-child(even) { margin-left: 0; }
    /* Quantity Query - quand il y en a plus d’1, le 1er événement s’étend sur les deux colonnes */
    .event--future:nth-last-child(n+1):first-child { flex: 0 0 100%; font-size: 1.5em; margin-left: 0; } 
}

@media only screen and (min-width: 43.125em) { 
    /* 1/3 width with 20px gutter */
    .event { flex: 0 0 calc( 100% / 3 - .875rem ); }
    /* Reset des marges */
    .event:nth-child(even), .event:nth-child(odd) { margin-left: 1.25rem; }
   /* Suppression de la marge de la grille normale */
    .event:nth-child(3n+1) { margin-left: 0; }
    /* Marges correctes pour les événements futurs */
    .event--future:nth-child(3n+1) { margin-left: 1.25rem; }
    /* Quantity Query - quand il y en a plus de 2, les deux 1ers occupent 50% */
    .event--future:nth-last-child(n+2):first-child, .event--future:nth-last-child(n+2):first-child + .event--future { flex: 0 0 calc( 50% - 1.25rem / 2 ); font-size: 1.5em; }
    /* Quantity + nth pour suppression des marges */
    .event--future:nth-last-child(n+2):first-child ~ .event--future:nth-child(3n) { margin-left: 0; }
}

Dans cette passe finale, j’ai utilisé un calcul un peu plus compliqué pour fixer la largeur de chaque élément à 1/3 du parent moins la gouttière entre eux (100%/3 −0,875rem).

Si vous regardez bien vous vous demanderez peut-être pourquoi la gouttière utilisée dans le calcul fait 0,875rem plutôt que 1,25rem. Eh bien la raison (pour faire court) est : les arrondis. Pour obtenir la largeur flexible remplissant l’élément parent sans entraîner un passage à la ligne, 14px (0,875rem) m’a semblé être le nombre magique.

Notez bien que si je permettais à l’événement de s’agrandir (en utilisant flex-grow: 1 ou son équivalent dans le raccourci), la colonne se remplirait parfaitement mais la dernière rangée serait elle aussi toujours remplie complètement. On pourrait se retrouver avec deux événements dans la dernière rangée ayant 50% de large ou un événement ayant 100%, ce que je ne voulais pas. Ce que je voulais, c’est avoir tous les événements de dimension égale, sauf les deux premiers. En réglant flex comme je l’ai fait, on réalise cet effet.

J’ai continué en remettant les marges standards pour les événements (à la fois sur .event:nth-child(even) et sur .event:nth-child(odd)). Puis j’ai supprimé les marges sur le premier de chaque groupe de trois événements grâce à .event:nth-child(3n+1).

Tout ceci étant en place, je me suis intéressé aux événements futurs, en faisant un reset des marges là aussi. Puis j’ai utilisé une quantity query pour sélectionner les deux premiers membres lorsque la liste est supérieure à 2 et je leur ai donné une largeur de 50% de leur parent, moins la gouttière.

Pour gérer les marges dans l’instance de quantity query, j’ai ajouté toutes les marges à nouveau, puis supprimé les marges à gauche des premiers éléments dans une rangée.

Les deux premiers événements se partagent à 50/50 toute la largeur de la page, les suivants occupent 1/3 de leur rangée

Ta-da !

Et voilà. en quelques 80 lignes de code CSS généreusement espacé et commenté, nous avons un layout flexbox basé sur une grille, flexible, avec des améliorations visuelles injectées via des quantity queries. Je suis sûr que je vais continuer à bricoler encore un peu, mais je suis déjà bien content du résultat.

Vous pouvez voir la page finale ou regarder cette vidéo de l’interaction :

J’ai aussi créé cette démo sur CodePen avec laquelle vous pouvez vous amuser :

See the Pen Quantity Queries + Flexbox by Aaron Gustafson (@aarongustafson) on CodePen.


Intéressé par CSS ? Retrouvez une liste des meilleurs articles et ressources du web.

Tous les articles sur CSS parus dans la Cascade.

Tous les articles sur Flexbox parus dans la Cascade.


Articles sur les mêmes thèmes dans la Cascade :

Quantity Queries pour CSS, par Heydon Pickering

Articles du Dico CSS dans la Cascade :

flex-basis
flex-direction
flex-grow
flex-shrink


Article original paru le 26 mars 2015 dans le notebook d’Aaron Gustafson.

Sur l’auteur : *est consultant web, il travaille en étroite relation avec les équipes de Microsoft. Il apporte ses conseils aux entreprises qui souhaitent résoudre les nombreux défis posés par les terminaux et écrans de tous genres via Easy Designs et Rosenfeld Media. Il est souvent sur la route, à présenter des conférences et organiser des ateliers un peu partout dans le monde.

Il travaille avec le W3C Responsive Issues Community Group, il a fondé le Chatanooga Open Device Lab, co-fondé la série d’événements Code & Creativity et Retreats 4 Geeks. Dans une vie antérieure, il a géré le Web Standards Project (WaSP) et édité Web Standard Sherpa.

Il a écrit le livre de design qualifié par Jeffrey Zeldman de “classique moderne” et par Jeremy Keith de “plus claire et plus belle explication de l’amélioration progressive que j’aie jamais lue” : Adaptive Web Design (en français : Adaptive web design : Créer des sites riches avec l’amélioration progressive)*. Il a également contribué à de nombreux autres titres, a écrit une douzaine d’articles, et a aidé nombre d’auteurs lorsqu’il était éditeur technique de A List Apart.

Traduit avec l’aimable autorisation de l’auteur.
Copyright Aaron Gustafson © 2015.