Don't build a Bootstrap style grid-system with Flexbox.
Firstly I'd like to point out that I'm not here to bash Bootstrap, in fact the opposite. This post details some lessons I've learnt from using Bootstrap, Flexbox and building Grid-systems.
Not so long ago in Manchester, England
Early in 2015 I got involved with a large scale commercial project for which the popular framework Bootstrap (v.3.3.4), became an integral component.
I soon became familiar with grid helper classes such as .col-xs-12
, which in this case allows a container to span the full width of a 12 column grid, from an "extra-small" screen width of 480px, upwards. There are more helper classes which span a variety of column widths for different screen sizes, e.g.
/* 480px */
.col-xs-12 {
...
}
/* 768px */
.col-sm-12 {
...
}
/* 992px */
.col-md-12 {
...
}
/* 1200px */
.col-lg-12 {
...
}
So if you want a <div>
to span 12 columns for the smallest screen size, then 6, then 4, and then 3 columns at the largest screen size it would look like so:
<div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
...
</div>
I had to extend Bootstrap's grid-system to suit the project, this included extending the amount of grid helper classes which span columns at specific screen widths e.g.
/* 480px */
.col-xs-12 {
...
}
/* 590px */
.col-phablet-p-12 {
...
}
/* 667px */
.col-phablet-l-12 {
...
}
/* 768px */
.col-sm-12 {
...
}
/* 992px */
.col-md-12 {
...
}
/* 1200px */
.col-lg-12 {
...
}
This was not easy
In fact it took more time than I would have liked to work out a decent way of doing this without over-writing core files. I don't understand why there are were a set number of breakpoints in Bootstrap, there will be a good reason for it, but it just created a headache for me.
Anyway... the rest is history, away we went coding with lots and LOTS of helper classes.
I eventually rolled my own
The more I got used to Bootstrap's grid-system the more I thought I can do this. I therefore decided to create a CSS grid-system using Sass (Scss flavour) to use in my own projects. The main motivation was to understand how grid-systems are built & also how they work from the inside out. Another was to learn about automating re-usable CSS.
A learning experience
Apart from getting into the whole Github thing, i.e. sharing a project publicly, going through the process of documenting how to use it, and creating a nice microsite I learned so much about why a grid system should or shouldn't exist. And yes I totally understand that old chestnut "We don't need another grid-system" (whoever said that).
Here's my old grid-system
Enter Flexbox
I may have understood floated columns using Bootstrap's grid, however I had to start right back at the beginning again to succeed with Flexbox. I first heard about it via CSS Tricks over 4 years ago, there was always promise but a lack of browser support previously blocked me from picking it up.
Motivation
There was no immediate financial benefit to learning Flexbox, and it wasn't a quick win due to the downtime in learning it. However it was challenging to understand building a grid-system with, and I like a challenge.
Goal
The goal was to convert my grid-system (Interior) which was based on Bootstrap, into a Flexbox enabled version. I set out to 'plug-in' Flexbox, and understand it in the process. I went through the sections in my documentation, and converted the methods for things like nested columns which was simple.
Offset columnns
Converting the float-based system, which uses margins to offset columns, was pretty much as-is. You can use margins with Flexbox. But was it right to do that; said the voice in the back of my mind?
Re-ordering columns
I moved on to the next section which focuses on flipping the visual appearance of columns from one side to the other; this allows the source order to be different, yet allow the presentation to appear the same.
Flexbox has a property order
which would make this seem trivial, and like less of a hack. No more fudging the order - Huzzah!
But wait...
I started to notice that what I was doing didn't allow Flexbox to do its thing. In-fact if anything it restricted it.
What was the point?
It felt like I was just replicating what Bootstrap did with floats, with Flexbox. I soon came to the conclusion that something didn't feel right.
I became at odds with Bootstrap
Bootstrap provides ways to get around the lack of layout capability with pre-flexbox CSS. It sets it's own standards and makes use of what has been available for years. Everything's bundled together to make the whole process of creating a layout (pre-flexbox) easier.
Flexbox solves many issues with layout, for example you don't have to offset or re-order content in the same way as with Bootstrap. You can simply refer to the spec, and come up with a solution based on the examples provided, these features are baked-in.
As I progressed, the need to demonstrate examples of what you can do with columns using Flexbox, yet in a Bootstrap grid framework style (i.e. push, pull, reorder), became redundant. You may think well that's bloody obvious anyway, however after adopting a certain way of working, you can become so engrossed that you forget to step back from your work; and think about what you are doing.
Questioning myself
I started to question why I needed to create a system that worked in a similar fashion to Bootstrap in the first place. I didn't ask myself this when I began creating Interior.
Commercial project requirements
We didn't have the luxury of deciding upon a suitable framework to suit the needs of the project, the choice was made for us. The fact that the team I was working with were using this Framework meant that we had some well crafted, practical, and thoroughly tested code. Not just that but a wealth of knowledge and guidance in the form of documentation - this brought many benefits in itself.
Personal project requirements
My own personal requirements are totally different:
- I don't work as part of a large team when I work on personal projects.
- I have ways of working which I prefer.
- I don't have to follow conventions from any given framework as I usually work on my own.
So taking this into consideration why would I then go and create a grid-system which does pretty much what Bootstrap does?
I experimented
I decided to use Codepen to create some working examples as proof of concept. I went through several iterations, all with varying degrees of progression.
What I was aiming for with the above example was to reduce the amount of markup needed to create a gutter. I thought THAT'S IT, no more of this:
<div class="col">
<div class="inner">
...
</div>
</div>
And more of this
<div class="col">
...
</div>
But why?
The gutter is VERY important when working with columns. For example if you want a gutter that separates 2 x panels which are orange, on a purple background, and you want the content within each panel to be inset by 10 pixels, you would need to wrap 2 <div>
or other tags/elements to achieve this using padding alone.
I liked the way that using calc
to slice off the gutters with justify-content: space-between
to distribute the columns within the remaining space felt. There was less markup and I didn't need a single float, or the clearfix hack.
Below is another variation of that approach, this time using only flex-grow
instead of width, and not using calc()
to subtract the width of the gutter:
What I really like about that one is that the browser is doing all the calculations for me, not Scss and CSS calc()
.
display: flex
accounts for the margin as part of the overall width of an element, and you can mix units such as have a percentage width and a pixel gutter width. The same can't be said for other display properties such as display: block
, due to the box-model.
I found new influences
Rachel Andrew's article makes a comparison between Bootstrap's HTML markup & the markup of HTML tables:
<div class="row">
<div class="col-md-8">
.col-md-8
<div class="row">
<div class="col-md-6">
.col-md-6
</div>
<div class="col-md-6">
.col-md-6
</div>
</div>
</div>
<div class="col-md-4">
.col-md-4
</div>
</div>
Rachel states that Bootstrap's markup was:
Not a million miles away from something I might have written in 1999.
I found this comparison enlightening, it made me step back and think about what I was bringing to the table (no pun intended) with my own offering.
There was more...
Zell Liew wrote an article featured in Smashing Mag which compared the Sass framework Suzy to Flexbox - the comparison itself caused a bit of a stir on the interwebs due to it being made between a Spec and a Framework.
I understand why it ruffled a few feathers, however I believe Zell was right to draw a comparison. I'm doing one right here, Flexbox makes many features of existing CSS frameworks look as though they are things of the past.
I digress
Both people I just mentioned seem to go against the whole methodology of Bootstrap in one form or another. For example Zell mentioned:
I’m personally not a fan of writing grid classes on the HTML
He explained his point in great detail here.
I took a step back
I questioned myself ...again, how could I provide all the tools needed to create a grid without describing it in markup, yet allow the same level of flexibility as Bootstrap? There seemed to be one real answer to this and that was to allow for every possible layout, in advance. This made me feel like things were becoming more complicated than they needed to be.
I decided against that idea for one reason: how am I supposed to know every possibility in advance?
I still knew that I had to start from scratch in order to get to where I felt I was heading. There was no way I was going to just flick a switch to 'enable' Flexbox on Interior - where's the fun in that? Yet there was equally no way that I wanted to spend more free time working out a way to provide for an unknown quantity of possibilities.
New approach
I realised that creating for every possibility in advance is impossible, I'm not wimping out of doing it, for example Suzy does a good job of pre-empting layout possibilities - so it can be done, but I'm also trying my damnedest right now to come up with the simplest way of doing ANYTHING.
This is the principle I'm advocating by comparing what I did in version 1 (bloated) and version 2 (lean). It will turn out later that things can be simpler.
I just need to create a grid and gutters, flexbox does the rest.
I will be providing some solid working examples of what can be achieved using Interior V.2, a set of standards which if applied WILL enable more web layout possibilities. And Interior V.2, which hasn't released yet, but you can see the working proof of concept via codepen below.
Benefits
Less code
The main Interior mixin was reduced from 149 to 15 lines:
@mixin col(
$col,
$sibling-offset: $grid-gutter + $grid-gutter,
$width: 100% / $cols * $col,
$sibling: false,
$grid-gutter: $grid-gutter,
$fg: 1,
$fs: 1
){
@if $sibling == true {
flex: $fg $fs calc(#{$width} - #{$sibling-offset});
} @else {
flex: $fg $fs calc(#{$width} - #{$grid-gutter});
}
}
Implementation
The approach to implementation has changed so that focus is on using a custom CSS approach, as opposed to outputting markup to describe the layout using .col-[this]-[that]
type classes. The following example creates a 2 column grid (sidebar and main content area) out of a total of 12 columns:
$cols: 12;
.col {
&:first-of-type {
@include col(4);
}
&:last-of-type {
@include col(8);
}
}
The next code example (layout 1d from the previous pen) goes further and creates an entire page layout with header, footer and some columns for laying-out content.
.example {
> .col {
&:nth-of-type(1) {
@include col(12);
}
&:nth-of-type(2),
&:nth-of-type(3),
&:nth-of-type(4),
&:nth-of-type(5){
@include col(8/4, $sibling: true);
}
&:nth-of-type(6) {
@include col(4);
}
&:nth-of-type(7){
@include col(12);
}
}
}
Flexible
This mixin is flexible so you can iterate through a predetermined number of columns if that is the approach you need. Like so:
$cols: 3;
@for $i from 1 through $cols {
.col-#{$i} {
@include col($i);
}
}
.col-1 {
flex: 1 1 calc(33.33333% - 1rem);
}
.col-2 {
flex: 1 1 calc(66.66667% - 1rem);
}
.col-3 {
flex: 1 1 calc(100% - 1rem);
}
I wouldn't recommend it, but there may be reasons why this approach suits you better therefore I'll also document how it can be done.
No more rows in your markup
The previous pen demonstrates how I removed <div class="row">
tags from the markup whilst achieving the appearance of gutters.
I'm annoyed we've not got a standard for gutters, as the majority of development time has been taken up by creating a way to add them, with less markup than it currently takes with floats, padding and box-sizing.
Creativity
Let's get creative with layout and not be confined to regurgutating what has been done before, there are new ways to create layouts for the web. It's early days, just think about how long paper layouts have been around and then look at the age of the web. I'll be demonstrating how creativity can be achieved in your layout with Interior V.2 and Flexbox.
Cross browser issues make me sad (Safari this ones for you)
The previous example demonstrates how I would like things to work, but nothing is that straight forward on the web, EVER due to browser implementation. Flexbox has had quite a ride, it's come a long way or it's been a long time coming, not sure really.
The implementation of Flexbox in Safari, makes me want to cry, this bug: found via Philip Walton's Flexbugs takes inconsistency to a new height. Some of the awesome features that flexbox brings are just completely screwed up in the Safari implementation.
Workaround
Yes that word can only mean one thing... I created a workaround. You can see an example of it below:
In a nutshell the grid columns have a min-width
and require media queries, whereas before you wouldn't need media queries for content to reflow it would just be based upon the content, a bit like an element query of sorts.
Wrapping up
At the time of writing I was designing & developing a new site, on the way I got caught up in grid-systems, I did't mind that, it didn't really matter because the work was for me, not a client. I started this so I can use my own tools, and not frameworks.
It's so important that we don't become confined to prescribed ways of working, and not be held back by 'the norm'. Bootstrap is useful, but the success of it means the web is looking like it has it's own styleguide - it doesn't need one!
As mentioned earlier, I will be creating some created some working real-world examples which demonstrate what I'm talking about with regard to layout, so feel free to come back later or keep an eye out on Github. What I'm developing is actually what I will be using for wireframing my new website, as I progress I'll add improvements and examples where I can, think of this post as proof of concept.
Update
Don't use Bootstrap, or any framework that include's what has become commonly known as a "Grid System", use CSS Grid!
For more info on why, and to see what I eventually came up with after this article was written, visit: Interior