In this article by the author, Ricardo Zea, of the book, Mastering Responsive Web Design, we're going to learn how to create a custom CSS grid. Responsive Web Design (RWD) has introduced a new layer of work for everyone building responsive websites and apps. When we have to test our work on different devices and in different dimensions, wherever the content breaks, we need to add a breakpoint and test again.
(For more resources related to this topic, see here.)
This can happen many, many times. So, building a website or app will take a bit longer than it used to.
To make things a little more interesting, as web designers and developers, we need to be mindful of how the content is laid out at different dimensions and how a grid can help us structure the content to different layouts.
Now that we have mentioned grids, have you ever asked yourself, "what do we use a grid for anyway?"
To borrow a few terms from the design industry and answer that question, we use a grid to allow the content to have rhythm, proportion, and balance. The objective is that those who use our websites/apps will have a more pleasant experience with our content, since it will be easier to scan (rhythm), easier to read (proportion) and organized (balance).
In order to speed up the design and build processes while keeping all the content properly formatted in different dimensions, many authors and companies have created CSS frameworks and CSS grids that contain not only a grid but also many other features and styles than can be leveraged by using a simple class name.
As time goes by and browsers start supporting more and more CSS3 properties, such as Flexbox, it'll become easier to work with layouts. This will render the grids inside CSS frameworks almost unnecessary.
Let's see what CSS grids are all about and how they can help us with RWD.
In this article, we're going to learn how to create a custom CSS grid.
Since we're mastering RWD, we have the luxury of creating our own CSS grid. However, we need to work smart, not hard. Let's lay out our CSS grid requirements:
Here's what our 1200 pixel wide and 12-column width 20px grid looks like:
The left and right padding in black are 10px each. We'll convert those 10px into percentages at the end of this process.
We're going to use the RWD magic formula:
(target ÷ context) x 100 = result %.
Our context is going to be 1200px. So let's convert one column:
80 ÷ 1200 x 100 = 6.67%.
For two columns, we have to account for the gutter that is 20px. In other words, we can't say that two columns are exactly 160px. That's not entirely correct.
Two columns are:
80px + 20px + 80px = 180px.
Let's now convert two columns:
180 ÷ 1200 x 100 = 15%.
For three columns, we now have to account for two gutters:
80px + 20px + 80px + 20px + 80px = 280px.
Let's now convert three columns:
280 ÷ 1200 x 100 = 23.33%.
Can you see the pattern now? Every time we add a column, all that we need to do is add 100 to the value. This value accounts for the gutters too!
Check the screenshot of the grid we saw moments ago, you can see the values of the columns increment by 100.
So, all the equations are as follows:
1 column: 80 ÷ 1200 x 100 = 6.67%
2 columns: 180 ÷ 1200 x 100 = 15%
3 columns: 280 ÷ 1200 x 100 = 23.33%
4 columns: 380 ÷ 1200 x 100 = 31.67%
5 columns: 480 ÷ 1200 x 100 = 40%
6 columns: 580 ÷ 1200 x 100 = 48.33%
7 columns: 680 ÷ 1200 x 100 = 56.67%
8 columns: 780 ÷ 1200 x 100 = 65%
9 columns: 880 ÷ 1200 x 100 = 73.33%
10 columns: 980 ÷ 1200 x 100 = 81.67%
11 columns:1080 ÷ 1200 x 100 = 90%
12 columns:1180 ÷ 1200 x 100 = 98.33%
Let's create the SCSS for the 12-column grid:
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
Using hyphens (-) to separate words allows for easier selection of the terms when editing the code.
Don't forget to include the UTF-8 encoding directive at the top of the file to let browsers know the character set we're using. Let's spruce up our code by adding a Credits section at the top. The code is as follows:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
Notice the Credits are commented with CSS style comments: /* */. These types of comments, depending on the way we compile our SCSS files, don't get stripped out. This way, the Credits are always visible so that others know who authored the file. This may or may not work for teams. Also, the impact on file size of having the Credits display is imperceptible, if any.
Including the box-sizing property allows the browser's box model to account for the padding inside the containers; this means the padding gets subtracted rather than added, thus maintaining the defined width(s).
Since the structure of our custom CSS grid is going to be mobile-first, we need to include the mixin that will handle this aspect:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
Since we're using the mobile-first approach, our main container is going to be 100% wide by default; but we're also going to give it a maximum width of 1200px since the requirement is to create a grid of that size.
We're also going to convert 10px into a percentage value, so using the RWD magic formula:
10 ÷ 1200 x 100 = 0.83%.
However, as we've seen before, 10px, or in this case 0.83%, is not enough padding and makes the content appear too close to the edge of the main container. So we're going to increase the padding to 20px:
20 ÷ 1200 x 100 = 1.67%.
We're also going to horizontally center the main container with margin:auto;.
There's no need to declare zero values to the top and bottom margins to center horizontally. In other words, margin: 0 auto; isn't necessary. Just declaring margin: auto; is enough.
Let's include these values now:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want,
no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
In the padding property, it's the same if we type 0.83% or .83%. We can omit the zero. It's always a good practice to keep our code as streamlined as possible. This is the same principle as when we use hexadecimal shorthand values: #3336699 is the same as #369.
On small screens, all the columns are going to be 100% wide. Since we're working with a single column layout, we don't use gutters; this means we don't have to declare margins, at least yet.
At 640px, the grid will kick in and assign corresponding percentages to each column, so we're going to include the columns in a 40em (640px) media query and float them to the left. At this point, we need gutters. Thus, we declare the margin with .83% to the left and right padding.
I chose 40em (640px) arbitrarily and only as a starting point. Remember to create content-based breakpoints rather than device-based ones.
The code is as follows:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want,
no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid
.grid {
//Global Properties - Mobile-first
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
width: 100%;
}
@include forLargeScreens(640) {
//Totally arbitrary width, it's only a starting point.
//Global Properties - Large screens
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 .83%;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
}
If we use rows in our HTML structure or add the class .clear to a tag, we can declare all the float clearing values in a single nested rule with the :before and :after pseudo-elements.
It's the same thing to use single or double colons when declaring pseudo-elements. The double colon is a CSS3 syntax and the single colon is a CSS2.1 syntax. The idea was to be able to differentiate them at a glance so a developer could tell which CSS version they were written on. However, IE8 and below do not support the double-colon syntax.
The float clearing technique is an adaptation of David Walsh's CSS snippet (http://davidwalsh.name/css-clear-fix).
We're also adding a rule for the rows with a bottom margin of 10px to separate them from each other, while removing that margin from the last row to avoid creating unwanted extra spacing at the bottom. Finally, we add the clearing rule for legacy IEs.
Let's include these rules now:
@charset "UTF-8";
/*
Custom Fluid & Responsive Grid System
Structure: Mobile-first (min-width)
Syntax: SCSS
Grid: Float-based
Created by: Your Name
Date: MM/DD/YY
*/
*, *:before, *:after {
box-sizing: border-box;
}
//Moble-first Media Queries Mixin
@mixin forLargeScreens($width) {
@media (min-width: $width/16+em) { @content }
}
//Main Container
.container-12 {
width: 100%;
//Change this value to ANYTHING you want,
no need to edit anything else.
max-width: 1200px;
padding: 0 1.67%;
margin: auto;
}
//Grid
.grid {
//Global Properties - Mobile-first
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
width: 100%;
}
@include forLargeScreens(640) {
//Totally arbitrary width, it's only a starting point.
//Global Properties - Large screens
&-1, &-2, &-3, &-4, &-5, &-6, &-7, &-8, &-9, &-10, &-11, &-12 {
float: left;
margin: 0 .83%;
}
//Grid 12 Columns
.grid {
&-1 { width:6.67%; }
&-2 { width:15%; }
&-3 { width:23.33%; }
&-4 { width:31.67%; }
&-5 { width:40%; }
&-6 { width:48.33%; }
&-7 { width:56.67%; }
&-8 { width:65%; }
&-9 { width:73.33%; }
&-10 { width:81.67%; }
&-11 { width:90%; }
&-12 { width:98.33%; }
}
}
//Clear Floated Elements - http://davidwalsh.name/css-clear-fix
.clear, .row {
&:before,
&:after { content: ''; display: table; }
&:after { clear: both; }
}
//Use rows to nest containers
.row { margin-bottom: 10px;
&:last-of-type { margin-bottom: 0; }
}
//Legacy IE
.clear { zoom: 1; }
Let's recap our CSS grid requirements:
A lot to digest here, eh?
Creating our custom CSS with the traditional floats technique was a matter of identifying the pattern where the addition of a new column was a matter of increasing the value by 100. Now, we can create a 12-column grid at any width we want.
Further resources on this subject: