Grille CSS: saisie d'une mise en page de magazine réactive de 20 lignes


Récemment, j'ai travaillé sur une implémentation moderne d'un blog roll (une liste de blogs externes utiles / intéressants). L'idée était de fournir aux lecteurs une sélection des derniers articles de ces blogs, regroupés dans une présentation de magazine, plutôt qu'une liste sèche de liens dans la barre latérale.

La partie la plus simple de la tâche consiste à obtenir une liste des articles et de leurs extraits (extrait - texte d'introduction avant le kat) à partir de nos flux RSS préférés. Pour ce faire, nous avons utilisé le plugin WordPress Feedzy lite , qui peut regrouper plusieurs flux en une seule liste, triés par heure - la solution idéale dans notre cas. La partie difficile est de tout rendre beau.

L'interface de liste standard du plugin est probablement insipide, donc je voulais le styliser comme un site Web de journal ou de magazine avec un mélange de grands et de petits blocs «sélectionnés».

Cela semble être l'occasion idéale d'utiliser CSS Grid! Créez une disposition de grille pour différentes dispositions, disons, une à cinq colonnes et une à trois colonnes, puis basculez entre elles à l'aide de requêtes multimédias sur différentes tailles d'écran. Droite? Mais avons-nous vraiment besoin de ces requêtes multimédias, et de tous ces problèmes avec la définition des points de contrôle, si vous pouvez simplement utiliser le paramètre Grid auto-fit, qui fera la grille adaptative pour nous?

Cette idée m'a semblé tentante, mais quand j'ai commencé à ajouter des éléments couvrant plusieurs colonnes de la grille ( travées), la grille a commencé à sortir de la page sur des écrans étroits. Les requêtes des médias semblaient être la seule solution. Mais j'ai trouvé quelque chose de mieux!

Après avoir étudié un certain nombre d'articles sur CSS Grid, j'ai constaté qu'ils sont principalement divisés en deux types:

  1. Comment créer une mise en page intéressante avec des étendues, mais avec un nombre donné de colonnes.
  2. Comment créer une mise en page adaptative sur une grille, mais avec des colonnes de même largeur (c'est-à-dire sans travées).

Je veux que la grille implémente ceci et cela: une mise en page entièrement adaptative utilisant des éléments multi-colonnes de redimensionnement adaptatif.

La beauté est que dès que vous commencez à comprendre les limites des grilles adaptatives et pourquoi et quand les portées cassent l'adaptabilité, il est facile de créer une mise en page de magazine avec des dizaines de lignes de code et une requête multimédia (ou même sans elles si vous souhaitez limiter la variété des portées).

Voici une comparaison claire du plugin RSS de la boîte et du résultat de notre travail (cliquable):


Il s'agit d'une mise en page de magazine entièrement adaptative avec des blocs «sélectionnés» colorés qui s'adaptent dynamiquement à la mise en page en fonction du nombre de colonnes. La page affiche environ 50 articles, mais le code de mise en page ne dépend pas du nombre d'éléments. Vous pouvez facilement augmenter le nombre de messages à 100 dans les paramètres du plugin, et la mise en page restera intéressante tout en bas.

Tout cela est réalisé exclusivement via CSS et en utilisant une seule requête multimédia pour afficher le contenu dans une colonne sur les écrans les plus étroits (moins de 460 pixels).

Ce qui est le plus incroyable, la mise en page entière n'a pris que 21 lignes de CSS (sans compter les styles généraux du site). Cependant, afin d'obtenir une telle flexibilité en utilisant si peu de code, j'ai dû plonger profondément dans les profondeurs les plus sombres de la grille CSS et apprendre à contourner certaines de ses limites.

Le code sur lequel repose toute la mise en page est incroyablement court, et tout cela grâce à la splendeur de la grille CSS:

.archive {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  grid-gap: 32px;
  grid-auto-flow: dense;
}

/*    */
.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
}
.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
}
.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
}

/*     */
@media (max-width: 459px) {
  .archive {
    display: flex;
    flex-direction: column;
  }
}

La technique décrite dans cet article peut être utilisée en toute sécurité pour styliser tout contenu généré dynamiquement, que ce soit la sortie du widget de publications récentes, de pages d'archives ou de résultats de recherche.

Créer un maillage adaptatif


J'ai créé 17 éléments pour mettre en valeur la diversité du contenu futur - titres, images et extraits, et enveloppé dans <div></div>

<div class="archive">
  <article class="article">
    <!--  -->
  </article>

  <!--  16  -->

</div>

Le code pour transformer ces éléments en une grille adaptative est particulièrement compact:

.archive {
  /*     - */
  display: grid;
  /*        ,       180 . */
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  /*     */
  grid-gap: 1em;
}

→ Démo sur CodePen

Notez comment la hauteur de la ligne s'ajuste automatiquement au bloc de contenu le plus élevé de la ligne. Si vous modifiez la largeur dans la démo à partir du lien ci-dessus, vous verrez que les éléments augmentent et diminuent automatiquement et le nombre de colonnes passe de 1 à 5, respectivement.

Ici, en action, nous voyons la magie de la grille CSS appeléeauto-fit. Ce mot-clé fonctionne conjointement avec la fonctionminmax()appliquée àgrid-template-columns.

Comment ça fonctionne


La disposition à cinq colonnes elle-même peut être obtenue comme ceci:

.archive {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

Cependant, cela crée une disposition à cinq colonnes qui s'étire et se contracte à différentes largeurs d'écran, mais reste toujours à cinq colonnes, ce qui conduit à des colonnes terriblement étroites sur les petits écrans. La première chose qui me vient à l'esprit est d'écrire un tas de requêtes multimédias et de redéfinir une grille avec un nombre différent de colonnes. Cela fonctionnerait, mais le mot auto-fit- clé fait tout automatiquement.

Pour auto-fitutiliser la fonction dont nous avons besoin minmax(). Ainsi, nous indiquons en quelque sorte au navigateur combien vous pouvez compresser les colonnes et combien vous pouvez étirer. Lorsque l'une des limites est atteinte, le nombre de colonnes augmente ou diminue, respectivement.

.archive {
  grid-template-columns: repeat (auto-fit, minmax(180px, 1fr));
}

Dans cet exemple, le navigateur tentera de prendre en charge autant de colonnes que 180 pixels de large. S'il y a un excès d'espace, toutes les colonnes se développeront, le répartissant également entre elles. C'est ce qui dicte le sens 1fr: rendre les tailles de colonnes égales aux fractions ( actions fr ) de la largeur disponible.

Si vous étirez la fenêtre du navigateur, toutes les colonnes augmenteront de manière égale avec l'augmentation de l'espace libre. Dès que le nouvel espace atteint 180 pixels, une nouvelle colonne apparaît à sa place. Et si vous réduisez la fenêtre du navigateur, tout se passe dans l'autre sens, ajustant parfaitement la grille jusqu'à ce qu'elle se transforme en une disposition à une seule colonne. La magie!

→ Démo vidéo

Et toute cette adaptabilité grâce à une seule ligne de code. Eh bien cool?

Création de portées avec «autoflow: dense»


Donc, pour le moment, nous avons déjà une grille adaptative, c'est juste tous ses éléments - la même largeur. La mise en page d'un journal ou d'un magazine implique la présence de blocs sélectionnés, dans ce contexte, ceux qui couvriraient deux, trois, voire toutes les colonnes disponibles.

Pour créer des étendues à plusieurs colonnes, nous pouvons utiliser la propriété grid-column: spandans les éléments qui devraient occuper plus d'espace. Supposons que nous voulons que le troisième élément de la liste ait deux colonnes de large:

.article:nth-child(3) {
  grid-column: span 2;
}

Cependant, après l'ajout de portées, de nombreux problèmes peuvent apparaître. Premièrement, des trous peuvent se former dans la grille dans les cas où l'élément large ne tient pas sur sa ligne et le auto-fittransfère comme suit:


Ceci est facilement résolu en ajoutant une propriété grid-auto-flow: denseà la grille. En raison de cette propriété, le navigateur comprend que les trous doivent être remplis avec d'autres éléments. Cela crée un flux autour des éléments plus larges et plus étroits:


Attention: l'ordre des éléments est cassé, maintenant le quatrième élément est devant le troisième. Pour autant que je sache, cela ne peut pas être contourné, c'est l'une des limitations de CSS Grid qui doit être acceptée.

Façons d'identifier les travées


Il existe plusieurs façons de spécifier le nombre de colonnes qu'un élément doit occuper. Il est plus facile de l'appliquer grid-columns: span [n]à l'un des éléments, où nest le nombre de colonnes que l'élément occupera. Le troisième élément de notre mise en page a une propriété enregistrée grid-column: span 2, ce qui explique pourquoi sa largeur est deux fois plus grande que les autres éléments.

Pour utiliser d'autres méthodes, vous devez spécifier les lignes de la grille . Les lignes de la grille sont numérotées comme suit:


Les lignes de la grille peuvent être indiquées de gauche à droite avec des nombres positifs (par exemple, 1, 2, 3) ou de droite à gauche avec des nombres négatifs (-1, -2, -3). Ils peuvent être utilisés pour placer des éléments dans la grille à l'aide d'une propriété grid-column, comme ceci:

.grid-item {
  grid-column: ( ) / ( );
}

Ainsi, les lignes de la grille élargissent notre capacité à définir des portées. La flexibilité est ajoutée par la possibilité de remplacer la valeur initiale ou finale par un mot-clé span. Par exemple, le bloc bleu à trois colonnes dans l'exemple ci-dessus peut être créé en appliquant l'une de ces propriétés au huitième élément de grille:

  • grid-column: 3 / 6
  • grid-column: -4 / -1
  • grid-column: 3 / span 3
  • grid-column: -4 / span 3
  • grid-column: span 3 / -1
  • etc.

Dans une grille non adaptative (c'est-à-dire avec un nombre fixe de colonnes), chacune de ces propriétés donne le même résultat (comme dans l'exemple avec le bloc bleu ci-dessus). Mais si la grille est adaptative et que le nombre de colonnes change, la différence devient très sensible. Certaines plages cassent la mise en page avec le retour automatique activé, ce qui donne à penser que ces deux solutions sont incompatibles. Heureusement, certaines astuces nous permettront de les combiner en toute sécurité.

Mais d'abord, nous devons comprendre le problème.

Problèmes de défilement horizontal


Voici quelques «éléments en vedette» créés à l'aide de la méthode de la ligne de grille (cliquable):


Sur toute la largeur (cinq colonnes), tout semble bien, mais si vous réduisez l'écran à une taille à laquelle il devrait y avoir deux colonnes, la disposition se rompt de cette façon:


Comme vous pouvez le voir, notre grille a perdu son adaptabilité et, bien que le conteneur ait rétréci, la grille essaie de prendre en charge les cinq colonnes. Pour ce faire, elle continue d'essayer la même largeur de colonne et dépasse éventuellement les limites de son conteneur à droite. De là, un défilement horizontal apparaît.

Pourquoi ça arrive? Le problème est que le navigateur essaie de se conformer à nos directions exactes pour les lignes de la grille. À cette largeur, la auto-fitgrille ne devrait afficher que deux colonnes, mais notre système de numérotation des lignes de pile contredit cela, se référant spécifiquement à la cinquième ligne. Cette contradiction mène au désordre. Pour afficher correctement notre grille implicite à deux colonnes, seuls les nombres 1, 2, 3 et -3, -2, -1 peuvent être utilisés, comme ceci:


Mais si l'un des éléments de notre grille contient des instructions en grid-columndehors de ces limites, disons 4, 5 ou -6, le navigateur reçoit des instructions ambiguës. D'une part, nous vous demandons de créer automatiquement des colonnes flexibles (qui devraient - implicitement - rester deux à cette largeur d'écran). D'autre part, nous avons explicitement fait référence aux lignes de grille, qui ne peuvent pas exister dans un format à deux colonnes. En cas de contradiction entre les colonnes implicites (automatiques) et leur nombre explicitement défini, la grille préfère toujours une définition explicite . C'est ainsi que les colonnes indésirables et le débordement horizontal apparaissent (ce qu'ils appellent la perte de données CSS) Les étendues, comme les numéros de ligne de grille, peuvent créer des définitions de colonnes explicites. grid-column: span 3 (le huitième élément de la grille dans la démo) force la grille à avoir explicitement au moins trois colonnes, malgré le fait que nous voulons deux implicites.

Il peut sembler que la seule option consiste à utiliser des requêtes multimédias pour modifier les valeurs grid-columnà la largeur souhaitée ... mais ne vous précipitez pas! Je le pensais aussi au début. Mais, après un peu de réflexion et de jouer avec différents paramètres, j'ai trouvé qu'il existe quelques façons de contourner ce problème, grâce auxquelles nous n'avons toujours qu'une seule demande de support pour les appareils les plus étroits.

Solutions


Il s'est avéré que l'astuce consiste à déterminer les étendues en utilisant uniquement les numéros de ligne disponibles pour la grille la plus étroite de celles planifiées pour l'affichage. Dans ce cas, nous parlons d'une grille à deux colonnes (rappel, nous utilisons une requête média pour l'affichage sur une seule colonne). Ainsi, vous pouvez utiliser en toute sécurité les nombres 1, 2, 3 et leurs paires négatives sans casser la grille.

Au début, je pensais que je me limiterais à la largeur de la travée en deux colonnes en utilisant ces combinaisons de nombres:

  • grid column: span 2
  • grid-column: 1 /3
  • grid-column: -3 / -1


Qui restent parfaitement adaptables jusqu'à deux enceintes:


Bien que cela fonctionne , du point de vue de la conception, c'est une limitation sérieuse et pas trop brillante. Je voulais faire des portées de trois, quatre ou même cinq colonnes sur des écrans larges. Mais comment? Ma première pensée a été de revenir aux questions des médias (omg, il est difficile de se débarrasser des anciennes habitudes), mais j'ai quand même essayé d'éviter cette approche et de regarder le design réactif sous un angle différent.

En regardant à nouveau la liste des nombres disponibles, je me suis soudain rendu compte que les nombres positifs et négatifs dans les valeurs initiales et finales grid-columnpeuvent être combinés, par exemple 1/-3,2/-2. Cela ne semble rien d'intéressant. Mais cela ne semble plus comme ça lorsque vous comprenez la position des lignes après avoir changé la taille de la grille: les portées changent la largeur en fonction de la largeur de l'écran. Un tas de nouvelles opportunités pour les étendues adaptatives ouvrent, en particulier, des éléments qui s'étendent sur un nombre différent de colonnes avec un changement de la largeur de l'écran, sans aucune interrogation multimédia.

Le premier exemple que j'ai découvert est grid-column: 1/-1. Cette propriété transforme l'élément en bannière en pleine largeur, remplissant toutes les colonnes de la première à la dernière, même lorsqu'il n'y a qu'une seule colonne!

En utilisantgrid-column: 1/-2, vous pouvez créer une plage "presque pleine largeur", qui remplit toutes les colonnes de gauche à droite, sauf la dernière. Dans une disposition à deux colonnes, une telle étendue se transforme de manière adaptative en un élément ordinaire dans une colonne. Étonnamment, cela fonctionne même lors de la compression de la mise en page sur une colonne. (Il semble que la raison soit que la grille ne réduira pas l'élément à une largeur nulle et qu'il reste donc la largeur d'une colonne, comme dans le cas avec grid-column: 1/1.) J'ai supposé que cela grid-column: 2/-1devrait fonctionner de la même manière, ne laisser qu'une colonne intacte à gauche, et non à droite . Il s'est avéré être presque à droite, lors de la compression de la mise en page sur une colonne, un débordement se produit toujours.

J'ai ensuite essayé une combinaison1/-3. Cela a bien fonctionné sur des écrans larges - remplissant au moins trois colonnes - et sur des écrans étroits - n'en remplissant qu'une seule. Je pensais qu'avec une grille à deux colonnes, quelque chose d'étrange se produirait, car puisque la première ligne de la grille est la même que la ligne sous le numéro -3. À ma grande surprise, l'article s'affiche correctement dans une colonne.

Après de nombreuses expériences, j'ai découvert qu'il existe 11 valeurs appropriées grid-columnparmi celles disponibles dans une grille à deux colonnes. Trois d'entre eux fonctionnent même dans une disposition à une seule colonne. Sept autres fonctionnent correctement jusqu'à deux colonnes, et pour un affichage correct dans une colonne, ils n'auront besoin que d'une seule requête média.

Voici la liste complète:


Démonstration de valeurs adaptatives grille-colonne sur différentes tailles d'écran dans une grille à ajustement automatique. ( Démo )

En général, malgré un sous-ensemble plutôt limité de plages adaptatives, il existe de nombreuses opportunités.

  • 2/-2 - Une combinaison intéressante, crée une portée centrée qui fonctionne jusqu'à la grille à une seule colonne!
  • 3/-1 - moins utile car elle conduit à un débordement même sur deux colonnes.
  • 3/-3 - une agréable surprise.

En raison de la variété des valeurs grid-columnde cette liste, il est possible de créer une mise en page intéressante et entièrement réactive. En utilisant une seule requête multimédia pour l'affichage sur une seule colonne le plus étroit, nous pouvons gérer dix modèles différents grid-column.

Cette même requête média est assez simple, même disons simple. Dans notre exemple, il est responsable de la commutation de l'affichage de la grille sur flexbox:

@media (max-width: 680px) {
  .archive {
    display: flex;
    flex-direction: column;
  }

  .article {
    margin-bottom: 2em;
  }
}

Voici la grille finale qui, comme vous l'avez peut-être remarqué, est entièrement réactive - d'une à cinq colonnes (cliquable):


Utilisation: nth-child () pour répéter la largeur dynamique


Pour réduire mon code à deux douzaines de lignes, j'ai appliqué une autre astuce. Le sélecteur :nth-child(n)m'a permis de styliser un grand nombre d'éléments à la fois. Toute mon idée avec les étendues devait être appliquée à de nombreux messages dans le flux afin que les blocs sélectionnés apparaissent régulièrement sur la page. Tout d'abord, j'ai écrit une liste de sélecteurs séparés par des virgules avec un numéro d'élément clairement défini:

.article:nth-child(2),
.article:nth-child(18),
.article:nth-child(34),
.article:nth-child(50)  {
  background-color: rgba(128,0,64,0.8);
  grid-column: -3 / -1;
}

En vitesse, j'ai réalisé que c'est un processus très long, surtout quand vous devez copier toute cette liste de conditions pour chaque élément enfant qui doit être modifié dans l'article - en-tête, liens, etc. Pendant le prototypage, j'ai été forcé de changer manuellement les numéros dans chacune de ces listes chaque fois que je voulais jouer avec la position des travées. Un processus ennuyeux et sujet aux erreurs.

C'est alors que j'ai réalisé que vous pouvez profiter de la fonctionnalité intéressante du pseudo-sélecteur :nth-child: au lieu d'une valeur entière, entrez une expression, par exemple :nth-child(2n+ 2), ce qui signifie chaque deuxième élément enfant.

Voici comment j'ai utilisé :nth-child([])pour créer un bloc bleu pleine largeur dans ma grille qui apparaît en haut de la page, puis à mi-chemin dans la liste:

.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
  background: rgba(11, 111, 222, 0.5);
}

Un morceau de code entre parenthèses ( 31n + 1) est responsable du choix du 1er, 32e, 63e, etc. élément enfant. Le navigateur démarre la boucle en commençant par n = 0 ( 31 * 0 + 1 = 1), puis n=1( 31 * 1 + 1 = 32) et enfin n=2( 31 * 2 + 1 = 63). Dans ce dernier cas, le navigateur comprend qu'il n'y a pas de 63e élément enfant, ignore la règle, arrête le cycle et applique la règle aux 1er et 32e éléments.

Je fais quelque chose de similaire pour les blocs violets qui apparaissent à gauche ou à droite sur toute la page:

.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
  background: rgba(128, 0, 64, 0.8);
}

.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
  background: rgba(128, 0, 64, 0.8);
}

Le premier sélecteur est pour les blocs violets de gauche. L'expression 16n + 2est responsable de l'application de styles à chaque 16e élément de grille, en commençant par le second.

Le deuxième sélecteur est pour les blocs violets de droite. L'intervalle est le même ( 16n), mais le décalage est différent ( 10). En conséquence, ces blocs se retrouvent régulièrement sur le côté droit de la grille, dans les éléments numérotés 10, 26, 42, etc.

Pour les styles visuels de ces éléments, j'ai utilisé une autre astuce pour éviter la répétition du code. Pour les styles courants de blocs violets (pour évidents background-color, par exemple), vous pouvez utiliser un sélecteur:

.article:nth-child(8n + 2) {
  background: rgba(128, 0, 64, 0.8);
  /* Other shared syling */
}

Ce sélecteur sélectionne les éléments 2, 10, 18, 26, 34, 42, 50 et plus. En d'autres termes, il sélectionne les blocs gauche et droit.

Cela fonctionne parce que 8n- c'est exactement la moitié 16n, et la différence de décalage dans les deux sélecteurs est également de 8.

Le dernier mot


CSS Grid peut désormais être utilisé pour créer des maillages flexibles et réactifs avec un minimum de code. Cependant, si vous évitez d'utiliser des requêtes de médias rétrogrades, il existe des restrictions importantes sur le positionnement des éléments dans la grille.

Ce serait très cool de pouvoir créer des portées qui n'entraîneraient pas de défilement horizontal et de débordement sur de petits écrans. Maintenant, nous pouvons dire au navigateur: «Faites une grille adaptative, s'il vous plaît», et il le fait très bien. Mais il vous suffit d'ajouter: "Oh, et cet élément de la grille est étiré en quatre colonnes, s'il vous plaît", et il agite la poignée vers des écrans étroits, donnant la préférence à la demande d'une étendue de quatre colonnes, plutôt qu'à une grille adaptative. Il serait possible de faire faire le contraire à la grille, par exemple comme ceci:

.article {
  grid-column: span 3, autofit;
}

Un autre problème avec les grilles réactives est la dernière ligne. La modification de la largeur de l'écran peut souvent la rendre vierge. J'ai essayé pendant longtemps de trouver un moyen d'étirer le dernier élément de la grille aux colonnes restantes (respectivement, remplir la ligne), mais cela semble impossible. Au moins pour l'instant. Ce serait bien d'avoir la possibilité de définir la position initiale de l'élément avec un mot-clé, comme auto, pour ainsi dire, dire "Remplissez la ligne jusqu'à la fin, en commençant par le bord gauche". Quelque chose comme ça:

.article {
  grid-column: auto, -1;
}

... qui étirerait la portée sur le bord gauche de la grille jusqu'à la fin de la ligne.


All Articles