Question

I would like to use CSS Flexbox properties to make some sort of grid, where the elements may span across multiple lines.

Here's the essential HTML source code:

<section class="group">
    <h1>...</h1>
    <section class="item">...</section>
    <section class="item">...</section>
    <section class="item">...</section>
    <section class="item">...</section>
    <section class="item">...</section>
    <section class="item">...</section>
</section>

Here's what I want to achieve:

enter image description here

Within the section.group, I want the h1 to be 100% wide with each section.item being 50% wide, arranged in a n × 2 grid, where n is dependent on the number of inner sections, which may change.

I cannot change the HTML, which means I cannot wrap section.items with unsemantic divs. I can only use CSS. Here's what I have so far:

.group {
   display: flex;
}
.group h1 {}
.group .item {}

(not much of a start)

How can I use the Flexbox Module to achieve this?

Was it helpful?

Solution

First, make sure flex-direction is set to row or row-reverse, because we want to lay out the items horizontally (left-to-right or right-to-left). But really, flex-direction: row; is default so we don’t explicitly need it. It’s just here for beginners.

Then, to ensure the boxes break across multiple tracks, add flex-wrap: wrap;.

.group {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}

Now we have to size the items. TLDR:

.group h1    { flex: 100%; } /* flex: 1 1 100%; */
.group .item { flex:  50%; } /* flex: 1 1  50%; */

If you want to know why, read on. Otherwise, that’s it!


By default, the main size (in this case, horizontal size, or width) of each flex item defers to the specified value of its width property. At first glance, we may want to just declare the widths:

/* NOPE!
.group h1    { width: 100%; }
.group .item { width:  50%; }
*/

However this is not very future-proof, as we may want to later change the flex-direction if for example we are writing in a vertical writing mode, or even if we merely want to swap the layout. If flex-direction is column or column-reverse, then setting the widths won’t get us what we want. We are really looking to size the main-size, which is what flex is for.

(This is an extremely basic usage of flex, but it is in fact a lot more …ahem… flexible …. Read more about the flex shorthand property on MDN.)

And some more info about flexbox in the CSS3 specs and in the MDN guide.

OTHER TIPS

My first answer was about using Flexbox to achieve the layout.

In this answer I talk about Grid.

To jump straight to the point, CSS Grid should be used in this situation. Flexbox is intended for one-dimensional layouts, or when wrapping rows (or columns), wrapping can happen arbitrarily, at any point beyond our control.

When we want complete control over both rows and columns. CSS Grid is our solution. CSS Grid is also a lot easier, because we only need to style the grid container, not any of its children. There are exceptions of course, like our heading.

.group {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
.group h1 {
  grid-column: 1 / -1;
}

Let’s break it down.

display: grid;

This is self-explanatory. It sets up the grid container.

grid-template-columns: repeat(2, 1fr);

We want 2 columns, with 1fr each, where a fr unit is “the remaining space”. In other words, the two columns share an equal amount of the remaining space. There are many ways to write this.

  • grid-template-columns: repeat(2, 1fr); is what we used, but we could have written
  • grid-template-columns: repeat(2, 50%);, or
  • grid-template-columns: 1fr 1fr;, or even
  • grid-template-columns: 50% 50%;

It just depends on how often you like to change your code. Later down the road if we want a 3-column layout, it could either be

  • grid-template-columns: repeat(3, 1fr);,
  • grid-template-columns: repeat(3, 33.33%);, or
  • grid-template-columns: 1fr 1fr 1fr, or
  • grid-template-columns: 33.33% 33.33% 33.33%.

Or let’s say we still want 2 columns, but we want the left to be half as big as the right—a one-to-two ratio:

  • grid-template-columns: 1fr 2fr
  • grid-template-columns: 33.33% 66.67%

The grid-template-columns and grid-template-rows are very complex properties that I still don’t understand fully. Don’t even get me started on automatic sizing, flexible breadths, and size ranges.

Let it be stated that although grid-template-columns: repeat(2, auto); is also a valid style, it’s not what we want here since auto sizes the columns based on their content, and we aren’t guaranteed that each will be 50% wide.

grid-column: 1 / -1;

This one looks weird, but it’s easy to learn. grid-column is shorthand for grid-column-start / grid-column-end, where the integers number the lines that define the columns. You can also use names, but that would be overkill for a layout as simple as ours.

  1. The first line, at the start of the grid, is 1 (not zero).
  2. In our grid, 2 is the line between the columns, and
  3. 3 is at the end.

In general though, negative numbers count backwards from the end. So in our 2-column grid, -1 is the same as 3, but in a 3-column layout, -1 would be equivalent to 4.

We want our header to be the full-width no matter how many columns we have, so it starts at 1 and ends at -1.

(Note: be aware that your preprocessor might interpret the slash as a division symbol, compiling to grid-column: -1;. Not cool. So in Less at least, I would escape a string literal. I can’t speak for Sass.)

grid-column: ~'1 / -1'; // compiles to `grid-column: 1 / -1;`
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top