Use the Tailwind CSS Grid System
If you want to learn how to use Tailwind's CSS Grid utilities to create real-world layouts, look no further. By real-world, what I mean is actual content that flows, text & images, the core of the internet, not coloured boxes.
This is what I’ll cover:
Tailwind's grid system
Responsive layout container
Responsive layout, within a container on a 12-column grid
Tailwind's grid system
Tailwind deconstructs a very popular 12 column grid system into a series of utility classes that are categorised by the various properties of the CSS Grid spec, here's a small excerpt:
Responsive layout container
In this exercise there are 2 parts of the overall system, one that constrains the layout in a container (this one) and the content itself, which is placed in the container:
I’ll refer to the header, main, and footer as regions.
The width of the container is 1280px which is the 2nd largest breakpoint that comes with Tailwind, below 1280px it’s 100% of the viewport width.
There are margins either side, so the content is inset, the total width of the container also comprises the margins.
Building a responsive layout container with Tailwind
There’s more than 1 way this can be done with Tailwind, I prefer using arbitrary values, but will demonstrate 2 ways this can be done way for the sake of comparison:
What’s common in both is that the layout container can be applied to each region of content, a header, a main and a footer. This system allows the flexibility to add background colours to the fluid space within regions and also the container.
In both examples the width of the container is constrained at a maximum width of 1280px.
If, like me, you want to make use of
grid-gap to add space between the different regions the 1st example requires more elements, without them grid will force the container to be the intrinsic width of it's content, not 1280px. That is why, in my opinion, it’s also an out-of-date date way of doing things, therefore I prefer the 2nd example.
Content within the container
My layout is based on a mid-point (tablet) and then adjusted for mobile and larger screens. The design allows the content to be laid out without changing it too much at each breakpoint, that kind of decision can save on repetitive work, i.e. too many things changing at each breakpoint.
The content can be categorised as such:
You can see the different layouts across breakpoints will be like so:
Building a responsive layout on a 12-column grid with Tailwind
Now we have somewhere to place the content, let’s add it. The HTML created in the previous step will house the content. You might notice I added a header and footer that are grey, they exist to demonstrate where a header and footer might go, I won’t be utilising them in this exercise.
For context take a look at this codepen which is a fully working example, and then I'll walk you through how each element relates to another.
Or if you just want to look at code, here it is:
Let’s start with the
<main/> attraction as that’s where the content will be added, I don’t want to interfere with the container so, to keep them separate I add a
I’ll explain what’s going on with the section; because the layout container is a grid consisting of 3 columns, I must specify where I want to place my container, I do this with:
col-start-2, grid will by default span 1 column, so I now have placed the container where I want it to be, in the second column, remember there are 3:
I apply a 12-column grid with
grid grid-cols-12, gaps, and specify that content should be placed at the start. Now we’re ready to go through each element within the section one by one:
col-span-12 the title spans... you guessed it, all 12 columns. It’s good practice to not let lines of text run on forever-and-ever as they are harder to read, so I might have considered capping the width of the title by applying
lg:col-span-8 on larger screens as it will fill the full width of the container, but as it’s a short title I won’t.
The main image spans 12 columns until the
lg breakpoint (1024px) where its orientation changes to portrait and it spans 5 columns, I’ve also given it a height and applied
object-fit. I would normally combine this treatment with a
<picture/> element, however as this post about layout I’ll skip that.
This is super important, I used the change of orientation to control the placement of other content, I’ll explain in more detail. Grid is smart, it knows that unless I specify explicitly how many columns to span, to just go ahead and fill the remaining space with content. So, because I only specified how many columns to span and not where to span from, the simple calculation of 5 + 7 = 12 and having 12 columns available means that grid takes over and does the layout for me. There’s one more thing, I specified that I want the image to span all the available rows, without that the layout would look like a Picasso, try removing
lg:row-span-2, and see for yourself.
The font-size won’t change across breakpoints, it spans 12 columns until the
lg breakpoint (1024px) where it then spans 7 columns.
It has 3 breakpoints due to being adjacent to the image grid, the font-size changes once.
I used a
<figure/> element for this as using
<div/> for everything is boring, live a little, try all the elements.
Image Grid retains its layout across breakpoints, corresponds to its parent grid on what is essentially a sub-grid (but it doesn’t use the sub-grid spec as support is not good) dig into the code and you’ll see what I mean. Image Grid starts out like the rest of the content, it has the same number of columns applied at this point (12) and the content within is also laid out on a 12 column grid so it corresponds to everything else, then, at the
sm breakpoint (640px) it spans 5 columns and likewise the number of columns applied to it is 5, so the content corresponds to... you get the picture.
If things don’t align properly what’s the point? We have the tools.
lg breakpoint (1024px) Image Grid spans 3 columns, and if we go off what I said about things lining up you would be right in thinking that we’ll be changing the columns to match, but nope, I just broke my own rule. It doesn’t make sense to do that here due to the parent’s grid gaps being bigger than I want for this, and the layout would look weird, so at this point the 5-column grid works so I keep it. There’s no point following a rule when it doesn’t make sense to do so.
Tailwind makes using CSS Grid for simple, repeatable, layouts easy and familiar. I’ve used it with several projects and the more I used it, the more I got used to the system, but let’s face it you could say that about most frameworks.
Feel free to reach out to me on Twitter, bye for now!
Does it always make sense to use CSS Grid?
I'm a huge fan of CSS Grid, I always favour it for any kind of layout, the main reasons for this are:
CSS Grid is 2 dimensional, which means unlike flex items, grid items can be placed both horizontally and vertically (x & y).
display: gridthe amount of boilerplate HTML needed to create a layout is usually always less than it would be with
grid-gapproperty has been supported for longer,
grid-gapwas revolutionary, gaps were a huge problem in the world of CSS grid systems, you can see how obsessed over them I was by looking at this codepen collection of mine.
display: gridhas become second nature to me, pretty much anything that can be done with flex can be done with grid and more!
When doesn’t it make sense to use CSS Grid?
There are times when I’ve had to ask myself "why am I writing more code than I would otherwise need with flex?". These are the times when I have to let CSS Grid down gently—as it’s making life more complicated—and use flex. Paradoxically, with Tailwind this is not an issue due to it being utility based, maybe Tailwind is a gateway to more CSS Grid uptake.
Does it make sense to wrap content in markup for layouts like this?
Yes, but it depends, I've found with complex layouts grouping regions and sub-regions helps me keep my sanity and a sense of being organised, but go with what works. For the purposes of this demonstration adding more markup would have made it less useful.