The world of web development has always had a gap between the design-to-development handoff. Ambitious designers want the final result of their effort to look unique and beautiful (and true to their initial vision), whereas many developers find more value in an outcome that is consistent, dependable, and rock solid (and easy to code). This dynamic can result in sustained tension between the two sides with both parties looking to steer things their own way.
While this situation is unavoidable to some extent, new front-end technology can play a role in bringing the two sides closer together. One such technology is CSS grid. This post explores how it can be used to write CSS styles that match design layouts to a high degree of fidelity (without the headache!).
A common way that designers give instructions to front-end developers is with design mockups (by mockups, we’re talking about deliverables that are built in Sketch, XD, Illustrator, Photoshop etc). All designers work differently to some degree (as do developers), but many like to base the structure of their layouts on some kind of grid system. A consistent grid system is invaluable for communicating how a webpage should be coded and how it should respond when the size of the user’s screen differs from the mockup. As a developer, I really appreciate designers who take the trouble to adopt a well thought-out grid system.
A 12-column layout is particularly popular, but other patterns are common as well. Software like Sketch and XD makes creating pages that follow a preset column layout pretty easy — you can toggle an overlay on and off with the click of a button.
Once a grid system is implemented, most design elements should be positioned squarely within it. This approach ensures that shapes line up evenly and makes for a more appealing appearance. In addition to being visually attractive, a predictable grid gives developers a distinct target to shoot for when writing styles.
Unfortunately, this basic pattern can be deceptively difficult to code accurately. Frameworks like Bootstrap are often used to create grid layouts, but they come with downsides like added page weight and a lack of fine-grained control. CSS grid offers a better solution for the front-end perfectionist. Let’s look at an example.
The design above is a good application for grid. There is a 14-column pattern with multiple elements positioned within it. While the boxes all have different widths and offsets, they all adhere to the same grid. This layout can be made with flexbox — and even floats — but that would likely involve some very specific math to get a pixel-perfect result across all breakpoints. And let’s face it: many front-end developers don’t have the patience for that. Let’s look at three CSS grid layout strategies for doing this kind of work more easily.
Strategy 1: A basic grid
See the Pen
Basic Grid Placement by chris geel (@RadDog25)
on CodePen.
The most intuitive way to write an evenly spaced 12-column layout would probably be some variation of this. Here, an outer container is used to control the outside gutter spacing with left and right padding, and an inner row element is used to restrain content to a maximum width. The row receives some grid-specific styling:
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-gap: 20px;
This rule defines the grid to consist of 12 columns, each having a width of one fractional unit (fr
). A gap of 20px between columns is also specified. With the column template set, the start and end of any child column can be set quite easily using the grid-column
property. For example, setting grid-column: 3/8
positions that element to begin at column three and span five columns across to column eight.
We can already see a lot of value in what CSS grid provides in this one example, but this approach has some limitations. One problem is Internet Explorer, which doesn’t have support for the grid-gap
property. Another problem is that this 12-column approach does not provide the ability to start columns at the end of gaps or end columns at the start of gaps. For that, another system is needed.
Strategy 2: A more flexible grid
See the Pen
More Flexible Grid Placement by chris geel (@RadDog25)
on CodePen.
Although grid-gap
may be a no go for IE, the appearance of gaps can be recreated by including the spaces as part of the grid template itself. The repeat
function available to grid-template-columns
accepts not just a single column width as an argument, but repeating patterns of arbitrary length. To this end, a pattern of column-then-gap can be repeated 11 times, and then the final column can be inserted to complete the 12-column / 11 interior gap layout desired:
grid-template-columns: repeat(11, 1fr 20px) 1fr;
This gets around the IE issue and also allows for columns to be started and ended on both columns or gaps. While being a nice improvement over the previous method, it still has some room to grow. For example, what if a column was to be positioned with one side spanning to the outer edge of the screen, and the other fit within the grid system? Here’s an example:
In this layout, the card (our left column) begins and ends within the grid. The main image (our right column) begins within the grid as well, but extends beyond the grid to the edge of the screen. Writing CSS for this can be a challenge. One approach might be to position the image absolutely and pin it to the right edge, but this comes with the downside of taking it out of the document flow (which might be a problem if the image is taller than the card). Another idea would be to use floats or flexbox to maintain document flow, but this would entail some tricky one-off calculation to get the widths and spacing just right. Let’s look at a better way.
Strategy 3: An even more flexible grid
See the Pen
Right Edge Aligned image with grid by chris geel (@RadDog25)
on CodePen.
This technique builds on the idea introduced in the last revision. Now, instead of having the grid exist within other elements that define the gutter sizes and row widths, we’re integrating those spaces with the grid’s pattern. Since the gutters, columns, and gaps are all incorporated into the template, child elements can be positioned easily and precisely on the grid by using the grid-column
property.
$row-width: 1140px;
$gutter: 30px;
$gap: 20px;
$break: $row-width + 2 * $gutter;
$col-width-post-break: ($row-width - 11 * $gap) / 12;
.container {
display: grid;
grid-template-columns: $gutter repeat(11, calc((100% - 2 * #{$gutter} - 11 * #{$gap})/12) #{$gap}) calc((100% - 2 * #{$gutter} - 11 * #{$gap})/12) $gutter;
@media screen and (min-width: #{$break}) {
grid-template-columns: calc(0.5 * (100% - #{$row-width})) repeat(11, #{$col-width- post-break} #{$gap}) #{$col-width-post-break} calc(0.5 * (100% - #{$row-width}));
}
}
Yes, some math is required to get this just right. It’s important to have the template set differently before and after the maximum width of the row has been realized. I elected to use SCSS for this because defining variables can make the calculation a lot more manageable (not to mention more readable for other developers). What started as a 12-part pattern grew to a 23-part pattern with the integration of the 11 interior gaps, and is now 25 pieces accounting for the left and right gutters.
One cool thing about this approach is that it can be used as the basis for any layout that adheres to the grid once the pattern is set, including traditionally awkward layouts that involve columns spanning to outside edges. Moreover, it serves as a straightforward way to precisely implement designs that are likely to be handed down in quality mockups. That is something that should make both developers and designers happy!
There are a couple of caveats…
While these techniques can be used to crack traditionally awkward styling problems, they are not silver bullets. Instead, they should be thought of as alternative tools to be used for the right application.
One situation in which the second and third layout patterns are not appropriate are layouts that require auto placement. Another would be production environments that need to support browsers that don’t play nice with CSS grid.
Some of the setup on these grids seems overly complex. I’ve been doing something similar (matching design columns to css columns) for the past year and documented it a while back for colleagues here: https://davec-til.netlify.com/grid/max-width-grid.html
That’s served pretty well as the base of my production Grid code.
I also strongly recommend using autoprefixer to save having to manually code the grid gaps.
Hey Dave!
That example is pretty slick! Yeah, this approach does require some setup, but the beauty of it is that it only needs to be setup once if you use a consistent grid.
Does your suggestion have the ability to position columns at the end of gaps or the edges of the screen (like strategy #3)?
Sure does! I usually try to go with general
grid-template-area
s andgrid-area: [AREANAME]
and then augment them when needed withgrid-area: 1/1/1/span 7
(or other) when I need to go to the edge of a screen.grid-area
is the Swiss Army knife of Grid.You are right about the single set-up. Looking forward to trying and seeing how I could skip grid area names and go right to grid lines in my current grids.
I really enjoyed this article, thanks for taking the time to write it up.
I forked your last example and added in the (ick) version that IE would need to do the same thing, in case you’re interested. https://codepen.io/grace-snow/pen/RwwvpMz
Generally, I probably wouldn’t do this approach in production, but it was quite fun to play with. If this was a layout I had to do for real, IE would probably get a simplified fallback version of the layout, rather than trying to match it (OR, I’d use flexbox). That works fine for most projects.
I did once work on an app that needed a very specific layout AND needed to support IE, so built up a complex IE-specific version of it’s grid. It wasn’t too bad as it was only one layout on the whole app, and I kept it in a separate IE-specific scss file. So once IE support is dropped, they can always rip that scss file out and rely wholly on the modern grid. Not an approach I’d reccommend for most sites though, there’s almost always a fallback/alternative that would work!