There are many ways to create what is known commonly as a grid system. Over the years some popular frameworks that also provided their own grid systems were:
More recently though there's:
The common things they share are: 12 columns, gutters, margins.
Why 12 columns?
Multiples of 4 work well as a general rule, and apply to more than grid systems. With 12 columns I can initially divide my grid like so: 12 / 1, 12 / 2, 12 / 3, 12 / 4. Then there are sub-divisions within, and many combinations possible, for example 4 + 8 will give me a typical web layout that has a main content area with an aside.
Web designers occasionally complain about 12 columns being over-used, but despite being a very common approach it works well, and covers the majority of cases for a typical website.
Sometimes a custom solution is the only thing that makes sense
Traditionally grid systems are meant to be designed to fit the type of content they hang together. I learned this fundamental by reading Grid Systems in Graphic Design by Josef Müller-Brockmann, his book is referenced many times. After studying, applying and experimenting with the principles; and applying them to real-life web projects, I know that's for good reason.
Grid systems from frameworks
Prior to the CSS grid spec that we all know and love (which was published in 2017) most grid systems were implemented using HTML markup, for instance take Bootstrap.
Tailwind also promote the use of markup to construct layouts using a predefined set of constraints, one of which (like Bootstrap) is 12 columns.
Tailwind advocate using utility classes, which means writing the same classes in HTML more than once, which, without putting measures in-place, over the lifetime of a codebase can be a maintenance headache.
Thoughts on how to keep things DRY
Can you imagine having to recreate the same grid, button, alert or form input more than once, with states? The possibilities for bugs and inconsistency are multiplied with every instance.
In order to create consistency, reduce repetition and avoid a utility class soup, you'll want to combine Tailwind with templates, components or mixins, otherwise you and other developers are redefining the same things. This never works out unless you aren't concerned with maintenance, consistency, repetition and keeping people who have an eye for detail happy. This isn’t something Tailwind appears to teach in their docs.
In a future article I will demonstrate how to deal with this specific problem, if you want to find out sooner rather than later, let me know here.
Fluid 12 column grid
Creating a fluid 12 column grid, with equal columns and gutters, using CSS Grid is as straight forward as:
Then I apply the
.grid class to HTML
And finally, span all the columns:
Note how simple that was, the result is also simple.
Bear in mind that due to the nature of CSS Grid being concerned about content first and foremost, and because the grid is divided with
fr units, by placing content in any number of columns that can't contain it, the overall alignment of the grid is distorted.
Centralising and containing the grid
With a few adjustments to the previous code I can create a container, by default it will be central to the viewport. In order to create it I need to decide the width it will be, including the gutters on the outside (I usually refer to these as margins), and then divide by 2 and subtract that amount using
calc() is a CSS superpower here as it can subtract one completely different type of unit from another, go
In order to see the content within the container I created I place it within the second and thirteenth columns. Due to the addition of the container my grid has an extra 2 columns, the starting column for content has to change, unless I want to add a full-bleed image (Fig. 8).
The example assumes that I only ever want to constrain the content within a
1024px width container, therefore will only ever be visible above a viewport width of
There's a working CodePen of this example right here.
Change the number of columns across breakpoints
12 columns work well, but may not always be necessary on smaller screens with less real-estate. To change the amount of columns I can make some changes:
calc() is my friend here, however, what becomes apparent is the code is becoming harder to understand! Not only that but it doesn't look that extensible.
A less cumbersome way to do it, one of my preferences is to use named grid lines:
To recap, I created named grid lines for fluid-margin (
fm) and content (
c), I used abbreviations for succinctness as that's sometimes how I like to roll, CSS Grid uses its superpowers here to do some heavy-lifting, the spec accepts named grid lines that start with
-start and end with
-end and does the rest.
Due to the combination of named grid lines, custom properties and
@media(), all I need to change across breakpoints is a single custom property, the rest is taken care of by the way I have set up the surrounding code.
The code is now extensible, I can keep building on it adding more containers and breakpoints as needed without having to faff around too much with the code I have already written.
There's a working CodePen of this example right here.
If you liked this, feel free to subscribe so I can let you know when I have new content available. Feel free to share on Twitter or wherever you want.