Flexing the Flexbox muscle

In order to better understand CSS Flexbox let us build our own
basic Grid system. It’s also a good chance to practice separation of CSS code for layout and from styling. When the layout is separated from style, the code becomes less repetitive, easier to compose and reuse. But by no means what we’re doing here is production ready! This is just a small learning exercise. You’re invited to come learn with me.

All grid frameworks out there have their own rules, some kind of API, that is different from each other. Each library has
its own flavor which is natural since everyone has different ways to solve the same problem. You should experiment with a few and see which one you like the most. Our approach is very basic but will be enough for our goals. Let’s define our rules:

  1. No JavaScript
  2. The grid is delimited by a root element. All our classes and markup only work inside of it.
  3. There are only Rows and Columns.
  4. The grid can have Rows. Rows can have Columns. Columns can have Rows.
  5. Rows always take the full available width of its parent container.
  6. Columns share the whole available width of the Row if no size is specified. They shrink and grow as necessary. They only break/wrap if there’s no available space.

We start off the first use case: 2 rows that take the full-width, each with 3 columns that share equally the width available. Let’s write the markup first:

[pastacode lang=”markup” manual=”%3Cdiv%20class%3D%22flex-grid%22%3E%0A%20%20%3Cdiv%20class%3D%22row%22%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%201%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%202%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%201%20Column%203%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%20%20%3Cdiv%20class%3D%22row%22%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%201%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%202%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%20%20%3Ch1%3ERow%202%20Column%203%3C%2Fh1%3E%0A%20%20%20%20%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E” message=”” highlight=”” provider=”manual”/]

Working with invisible elements is not that easy so let’s add some visual cues so we can track our progress. A few borders and background color will do the trick:

[pastacode lang=”css” manual=”.flex-grid%20%7B%0A%20%20background-color%3A%20%23ddd%3B%0A%7D%0A%0A.flex-grid%20.row%20%7B%0A%20%20border%3A%201px%20dotted%20red%3B%0A%20%20box-sizing%3A%20border-box%3B%0A%7D%0A%0A.flex-grid%20.row%20.col%20%7B%0A%20%20border%3A%201px%20dashed%20blue%3B%0A%20%20box-sizing%3A%20border-box%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

Time to turn on the Flexbox! Just add display: flex; to .flex-grid and Boom! The layout is totally wrecked now but we’ll adjust it step-by-step.

Since our grid can only have Rows as direct children, and every row must always be on top of each other, we have to add flex-direction: column; to .flex-grid. Wait, what!? Rows as columns?! Yep. Not very intuitive at first glance. That’s why this exercise is important: it will put us out of our comfort zone and force us to really grasp the theory and put it into practice.

So what’s going on here?

When we set the flex-direction to “column“, we’re telling that we want every direct child element of the flex container to be displayed on top of each other to form a column/stack. Which is what we want: rows stacked on top of each other occupying the whole width.

Unfortunately, the columns are behaving just like Rows too. We want that all columns of a row share the available width by default. This also means that the columns must appear next to each other, in a line. We could achieve this by setting display: inline; or float: left; to the columns … but that’s not Flexbox. Since the layout of the Columns is ruled by the parent container, setting display: flex; to .flex-grid .row does one of the tricks: Columns in a row. But now the Columns only occupy the space required by its contents. To fix this we must tell the Columns to grow as much as they can by setting flex: 1; to .flex-grid .row .col. Uh la la!

Things are starting to look good but let’s first try some nesting. Replace the contents of the first Column of the first Row with the following snippet.

[pastacode lang=”markup” manual=”%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cspan%3EInner%20Row%201%20Col%201%3C%2Fspan%3E%0A%20%20%3C%2Fdiv%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Ch4%3EInner%20Row%201%20Col%202%3C%2Fh4%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cp%3EInner%20Row%202%3C%2Fp%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3Cdiv%20class%3D%22row%22%3E%0A%20%20%3Cdiv%20class%3D%22col%22%3E%0A%20%20%20%20%3Cspan%3EInner%20Row%203%3C%2Fspan%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E” message=”” highlight=”” provider=”manual”/]

Beautiful, it works wonders out of the box! This truly shows the potential of Flexbox. Let’s try to add some width modifier to our columns so we can have so more control over our layouts. We’ll skip responsive breakpoints for now and apply the widths for all sizes:

  • .all-20: 1/5 (20%)
  • .all-25: 1/4 (25%)
  • .all-33: 1/3 (33%)
  • .all-50: 1/2 (50%)
  • .all-60: 3/5 (60%)
  • .all-66: 2/3 (66%)
  • .all-75: 3/4 (75%)

Another important detail: when we specify the column width, that’s the min and max width it should have -a fixed proportion. So we add the following selectors and rules:

[pastacode lang=”css” manual=”.flex-grid%20.row%20.col.all-20%20%7B%0A%20%20max-width%3A%2020%25%3B%0A%20%20width%3A%2020%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-25%20%7B%0A%20%20max-width%3A%2025%25%3B%0A%20%20width%3A%2025%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-33%20%7B%0A%20%20max-width%3A%2033%25%3B%0A%20%20width%3A%2033%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-50%20%7B%0A%20%20max-width%3A%2050%25%3B%0A%20%20width%3A%2050%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-60%20%7B%0A%20%20max-width%3A%2060%25%3B%0A%20%20width%3A%2060%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-66%20%7B%0A%20%20max-width%3A%2066%25%3B%0A%20%20width%3A%2066%25%3B%0A%7D%0A%0A.flex-grid%20.row%20.col.all-75%20%7B%0A%20%20max-width%3A%2075%25%3B%0A%20%20width%3A%2075%25%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

So if we add .all-25 to Column 2 of Row 1, it shrinks to 1/4 of the Row width as expected and the other two Columns grow. But if instead of .all-25 we apply .all-75 it doesn’t work, they all occupy 1/3 of the width. This is a bit trickier.

When we set flex: 1; it’s actually a short-hand for setting flex-grow: 1; flex-shrink: 1; flex-basis: 0%; The most important detail being flex-basis: 0%;. This means that the element will not enforce its min size. And since all other Columns have the same rule, the result is that they only grow to the max width which is equally divided. To make sure the Column has the width it was supposed to have, we got to set flex-basis: 100%; to all width modifiers. And now it’s working!

But what happens when we apply .all-25 to Column 1 of Row 1? The first two Columns (1/4 + 3/4 = 1) should occupy 100% of the Row’s width but why the hell Column 3 is still in the same Row?! Why didn’t it break to the second line? Well, that’s because we didn’t tell the Row to allow it. And to do it we must set flex-wrap: wrap; to .flex-grid .row.

Another interesting feature we can add to our Flexbox grid system is the ability to align Columns. And who controls the alignment? The parent container: Row! We can create the following modifier with the Flexbox property justify-content to put Columns at the horizontal center of the Row:

[pastacode lang=”css” manual=”.flex-grid%20.row.hor-center%20%7B%0A%20%20justify-content%3A%20center%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

It’s important to note that this only works if the Columns have a defined width. How can you know what’s the center of the Row if the Column is occupying the whole width? Once the Column has a width, say 50%, the Row can calculate how much space is left and position the Column at its horizontal center.

But you want to put the Columns at the vertical center of the Row, right? For that, we need a different Flexbox prop: align-items. If justify-content works by placing elements on the X/horizontal axis, align-items works on the Y/vertical axis. So we add a new Row modifier to center columns vertically:

[pastacode lang=”css” manual=”.flex-grid%20.row.vert-center%20%7B%0A%20%20align-items%3A%20center%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

There you go! A super basic base for a grid system implemented with Flexbox. Just enough features so we can practice some core concepts and solidify them in our CSS knowledge base.

The whole CSS for our basic Flexbox grid system:

[pastacode lang=”css” manual=”.flex-grid%20%7B%0A%20%20%20background-color%3A%20%23ddd%3B%0A%20%20%20display%3A%20flex%3B%0A%20%20%20flex-direction%3A%20column%3B%0A%20%7D%0A%0A.flex-grid%20.row%20%7B%0A%20%20%20border%3A%201px%20dotted%20red%3B%0A%20%20%20box-sizing%3A%20border-box%3B%0A%20%20%20display%3A%20flex%3B%0A%20%20%20flex-wrap%3A%20wrap%3B%0A%20%7D%0A%0A.flex-grid%20.row.hor-center%20%7B%0A%20%20justify-content%3A%20center%3B%0A%7D%0A%0A.flex-grid%20.row.vert-center%20%7B%0A%20%20align-items%3A%20center%3B%0A%7D%0A%0A.flex-grid%20.row%20.col%20%7B%0A%20%20%20border%3A%201px%20dashed%20blue%3B%0A%20%20%20box-sizing%3A%20border-box%3B%0A%20%20%20flex%3A%201%3B%0A%20%20%20padding-right%3A%20.5em%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col%3Alast-child%20%7B%0A%20%20%20padding-right%3A%200%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-20%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2020%25%3B%0A%20%20%20width%3A%2020%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-25%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2025%25%3B%0A%20%20%20width%3A%2025%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-33%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2033%25%3B%0A%20%20%20width%3A%2033%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-50%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2050%25%3B%0A%20%20%20width%3A%2050%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-60%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2060%25%3B%0A%20%20%20width%3A%2060%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-66%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2066%25%3B%0A%20%20%20width%3A%2066%25%3B%0A%20%7D%0A%0A.flex-grid%20.row%20.col.all-75%20%7B%0A%20%20%20flex-basis%3A%20100%25%3B%0A%20%20%20max-width%3A%2075%25%3B%0A%20%20%20width%3A%2075%25%3B%0A%20%7D%0A” message=”” highlight=”” provider=”manual”/]

This is a very limited implementation and we can still achieve some interesting results:

Keep on honing your skills while having some fun.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.