Home > css3 > A Creative Grid System With Sass and calc()

A Creative Grid System With Sass and calc()

March 27Hits:1

Before we even get started talking about Sass and the code for this project, let me remind you this is no more than a quick experiment which by no means should be used in a live context. So what follows is only a proof of concept of what we could do with a couple of Sass variables and the calc() function from CSS.

There are probably still quite a couple of flaws and edge cases that are not handled in this example. If you have any recommendations for improving this, be sure to share. Otherwise keep in mind it’s just an experiment.

What Are We Trying to Do?

In the last couple of days I have been working with the calc() function in CSS. calc() can be a blessing when it comes to cross-unit calculations, and gets even better when you have a couple of Sass variables to make everything easily customizable.

Then someone on Twitter asked me how I would build a grid system if I had to. Firstly, I wouldn’t. There are too many CSS grid systems and frameworks already, and building another one would be reinventing the wheel. In fact, I once wrote an article called We don’t need your CSS grid, even if my opinion is now slightly more peaceful than when I first wrote the article. Long story short: Some smart people already built grid-systems that are more powerful than any grid system I could ever come up with (like Breakpoint and Susy).

Anyway, I didn’t want to do a grid system like the others so I thought “hey, why not have some fun with calc? I wanted to keep things as simple as they could be — three variables, one breakpoint, one mixin. That’s all. The three customizable variables are:

  • The number of columns in the grid (default $grid-columns: 12)
  • The width of a gutter between columns (default $grid-gutter: 10px)
  • The screen width, under which we move to a single column (default $grid-breakpoint: 48em)

Another peculiarity I wanted to have (which is actually getting less and less special) is to avoid using a class name in the DOM. More importantly, I wanted to do everything from within the stylesheet. This also means I had to use advanced CSS selectors like :nth-of-type.

Mixin Core

I always try to keep my function and mixin signatures as lean as possible. For this one, I ended up needing no more than a single argument:

@mixin grid($cols...) {    // Sass magic  }

… which is actually a variable number of arguments (AKA an argList), as indicated by the ellipses after the $cols variable. The main idea is to loop through those arguments and handle columns based on this thanks to the :nth-of-type CSS selector. For instance, calling grid(6, 6) on a 12-column based grid will create 2 columns separated by a 10px gutter.

But before looping, let’s add a couple of declarations to build the layout:

@mixin grid($cols...) {    overflow: hidden;        > * {        float: left;        margin-right: $gutter;      }      // Loop  }

To target all children from the container calling the mixin, we use the * selector with the child combinator >. I bet some of you are gasping already. Well… yes. It’s 2014, which means CSS performance is not a problem anymore. Also, since we’re using calc(), we won’t be supporting anything below Internet Explorer 9, so we’re using > *, alright!

In our declaration block we float all immediate child elements and add a right margin. The wrapper has overflow: hidden to clear inner floats. If you’re more of a micro-clearfix person, be sure to change this to suit your needs. In case it’s a list, don’t forget to add list-style: none and padding-left: 0, if your CSS reset is not already doing so.

Now, the loop!

@for $i from 1 through length($cols) {    > :nth-of-type(#{$i}n) {          $multiplier: $i / $grid-columns;          width: calc(100% * #{$multiplier} - #{$grid-gutter} * (1 - #{$multiplier}));    }  }

Ouch, this looks complicated as hell! Let’s deal with this one line at a time. For the whole explanation, let’s assume we are calling grid(3, 7, 2), which would be a pretty standard layout with a central container circled with 2 sidebars. And don’t forget we have a 12-column layout with 10px gutters, as we’ve defined in our variables earlier.

First, the selector. Don’t pay attention to the n yet, we’ll see why it’s there in the next section. For now all you have to understand is we select children one by one to apply a width to them. By the way, the empty space before :nth-of-type is an implicit *.

Now the calc() function. The calculation looks pretty intense, doesn’t it? Actually it’s not that hard to understand if you picture it. Let’s deal with our first column (3). If we go through our equation step by step, here is what we get:

  1. 100% * 3 / 12 – 10px * (1 – 3 / 12)
  2. 100% * 0.25 – 10px * 0.75
  3. 25% – 7.5px

Our column will spread over 25% of the total width minus 7.5 pixels (hopefully targeted browsers will deal with subpixel rendering). Still not clear? Let’s see another example with our main container (7):

  1. 100% * 7 / 12 – 10px * (1 – 7 / 12)
  2. 100% * 0.5833333333333334 – 10px * 0.41666666666666663
  3. 58.33333333333334% – 4.1666666666666663px

And last but not least, our right sidebar (2):

  1. 100% * 2 / 12 – 10px * (1 – 2 / 12)
  2. 100% * 0.16666666666666666 – 10px * 0.8333333333333334
  3. 16.666666666666666% – 8.333333333333334px

Now if we add the three results to make sure everything’s right:

  1. (25% – 7.5%) + (58.33333333333334% – 4.1666666666666663px) + (16.666666666666666% – 8.333333333333334px)
  2. (25% + 58.33333333333334% + 16.666666666666666%) – (4.1666666666666663px + 8.333333333333334px + 7.5px)
  3. 100% – 20px

Which makes sense since we have 2 gutters of 10px. That’s all for the calculations folks. It wasn’t that difficult, was it?

Last important thing: We remove the right margin from the last child outside of the loop with another advanced CSS selector:

> :nth-of-type(#{length($cols)}n) {    margin-right: 0;  }

In case you’re wondering, applying margin to all children, then removing margin from last child, is better than just applying margin on every child except the last. I tried both.

Note: when using Sass variables in the calc() function, don’t forget to interpolate them if you want it to work. Remember calc() is not compiled by Sass but by the browser itself so it needs to have all values properly written in the function.

Unknown Number of Items

I suppose it is needless to say that the grid system handles nested grids quite well. One thing I wanted was the ability to have nested grids with an unknown number of children. There are numerous reasons for this, whether it be Ajax loading, lazyload, or whatever.

Because we don’t know the number of children, we can’t include the “ratio” for all children in the mixin inclusion. So I came up with a solution only requiring the pattern of a single row (e.g. grid(3, 3, 3, 3)). Then if there are more than 4 children, they should still behave like it’s a 4-column grid (new row and so on).

Also you may have noticed we are not using any sub-wrappers for each row since we don’t make any change to the DOM. So we need to make sure the last child of the container and the last child of each row each have no margin.

Hence the :nth-of-type() selectors we’ve seen previously. This means for instance that children 4, 8, 12, and so on won’t have a right margin.

Dealing with Small Screens

Now that we have everything working like a charm, we should make sure the grid degrades gracefully on small screens. I’m keeping it simple: Below the given breakpoint everything stacks as a single column.

@mixin grid($cols...) {      // Mixin core        @media (max-width: $breakpoint) {        float: none;        width: 100%;        margin-right: 0;      }  }

Below this screen width, elements behave as they would without the grid system. That is, block-level elements stretch to fit the width of their parent and get positioned one under in source order. A simple, yet efficient, approach.

Improving CSS Output with Placeholders

So far, it does the job very well. Everything works great and we are pretty happy, aren’t we? However if we happen to have multiple grids on the same page, there are a lot of duplicate CSS rules that could be merged to make output lighter.

We could make our mixin extend placeholders instead of directly dumping CSS rules. First, let’s create our placeholders.

%grid-parent {      overflow: hidden;  }    %grid-child {      float: left;      margin-right: $grid-gutter;  }    %grid-last-child {      margin-right: 0;  }    @for $i from 1 through $grid-columns {      %grid-col-#{$i} {            $multiplier: $i / $grid-columns;            width: calc(100% * #{$multiplier} - #{$grid-gutter} * (1 - #{$multiplier}));      }  }    @media (max-width: $grid-breakpoint) {      %grid-fallback {        float: none;        width: 100%;        margin-right: 0;      }  }

The first three placeholders speak for themselves. For the 4th placeholder, to avoid computing the width directly inside the mixin, we create as many placeholders as we need for our grid (e.g. 12 for 12-columns) with a @for loop.

Regarding the %grid-fallback placeholder, we have to instantiate it inside a media query in order to be able to extend it from within an equivalent media query elsewhere in the stylesheet. Indeed, Sass has some restrictions regarding cross-media @extend (i.e. it doesn’t work).

And here is what the mixin looks like now — doing no mare than extending placeholders:

@mixin grid($cols...) {       @extend %grid-parent;        > * {         @extend %grid-child;          @for $i from 1 through length($cols) {          &:nth-of-type(#{$i}n) {                @extend %grid-col-#{nth($cols, $i)};          }        }          &:nth-of-type(#{length($cols)}n) {          @extend %grid-last-child;        }      }        @media (max-width: $grid-breakpoint) {        @extend %grid-fallback;      }  }

Final thoughts

Hey, it was pretty intense in the end, wasn’t it? To be honest, at first I thought it would be easy, but it occurred to me I had to do some advanced Sass to keep the CSS output as clean as if it was written by hand. A good rule of thumb is that the output from your Sass files should be sensibly similar to the one you would have written by yourself.

See the Pen euKgi by SitePoint (@SitePoint) on CodePen.

Related Articles

  • A Creative Grid System With Sass and calc()March 27

    Before we even get started talking about Sass and the code for this project, let me remind you this is no more than a quick experiment which by no means should be used in a live context. So what follows is only a proof of concept of what we could do

  • SASS grid system improvementsJune 3

    I've got this own made grid system made in SASS: @import "../mixins/cross-browser-elements/box"; $columns: 12 !default; $gutter-width-px: 20px !default; @function calculate-column-width($index) { @return percentage($index / $columns); } .row { o

  • Bootstrap's Grid System vs. Susy: A Comparison

    Bootstrap's Grid System vs. Susy: A ComparisonAugust 29

    There are a lot of grid frameworks for us the to choose from while developing websites nowadays. Of the many frameworks, one of most loved and yet most hated framework is definitely Bootstrap. Today, I'll like to introduce you to a slightly lesser kn

  • Designing for the Web: Templates and Grid Systems

    Designing for the Web: Templates and Grid SystemsDecember 12

    Continuing on from the previous article in this series, Designing for the Web: Resolution and Size, we cover some helpful and important factors while creating a template for designing a website. In this article, we will cover some important issues an

  • Creating a Responsive Grid System with Susy and BreakpointDecember 9

    The big appeal behind CSS frameworks (like Bootstrap and Foundation) is that they come with a responsive grid system all set and ready to go. The downside is that you have to be content with their column count, the width of their gutters, and all the

  • Introducing sGrid: A Stylus-based Flexbox Grid SystemDecember 2

    After reading the title of this post, you might ask: Why another grid system? This is a good question. Basically there are plenty of grids out there and many of them are flexbox grids too. So why have I built another tool that's similar? The short an

  • A grid system (Photoshop/Pixelmator) for designing for the iPadSeptember 24

    I am designing an iPad app, so I could really use a grid template. I could probably just work with the 960 grid system; however, I'd love to find a solution that is tailor-made for the purpose (that is, takes into account such things as the iPad's re

  • My 960 grid system has wider columns that standard. 55px instead 40px for 16 columnsOctober 27

    I was surprised to discover my website is wider than is supposed to be. I am using Drupal + Omega Theme that uses 960 grid system. The total width of the site is 1180 instead 960. The columns are wider: On 16 columns options: they are supposed to be

  • Mixing the classic type size scale, grid systems and modular scalesNovember 25

    I'm coming from a programmers background and just trying to understand how sizes should relate. I read in the book "designing for the web" that these are the classic type sizes and text set in these size should look better if typeset correctly.

  • DesignFestival: Designing for the Web: Templates and Grid SystemsDecember 13

    Continuing on from the previous article in this series, Designing for the Web: Resolution and Size , we cover some helpful and important factors while creating a template for designing a website. In this article, we will cover some important issues a

  • Anyone using 960 Grid System or YUI 2 Grids CSSApril 13

    Anyone having experience with 960 Grid system and getting it to play nicely with SharePoint? I am intrested in experience from other CSS Framework and how successfull was your team in implementing this? --------------Solutions------------- I'm using

  • most efficient way to create a grid system in unityMay 19

    I'm creating a little tower defence game to get myself acquainted with unity. At first I was going to emulate a grid system by capturing touches / mouse clicks and rounding the coordinates to the nearest whole numbers. This is very low cost and works

  • How to choose a CSS grid system for new theme?August 4

    I am going to make a new responsive theme based on an existing base theme for drupal 6. The theme need to be minimal but flexible. As there are many CSS grid systems to choose from, I've read that 960.gs has fallen out of fashion as it is too small f

  • Is there a comparison of Frameworks, Grid Systems and Boilerplates? September 1

    There are so much of them, how can I know what to use for a project? --------------Solutions------------- Well, there is a comparison of web application frameworks in a Wikipedia page. Also you may find this Stackoverflow post on Grid system comparis

  • What are "rows" in a responsive grid system?

    What are "rows" in a responsive grid system?January 22

    I'm looking at various grid systems to make a responsive design, but I keep coming across the same thing that puzzles me : What's a row in a design where everything can move based on the screen size ? Say I want the following design on a large screen

  • Getting: org.opengis.referencing.NoSuchIdentifierException: No transform for classification "Transverse Mercator Zoned Grid System" with geotoolsApril 22

    Using geotools I've been trying to create a referenced envelope after converting/reprojecting from WGS 84 to UTM with specific zones and it seems that either I need to do something with authority factories or figure out some kind of a workaround for

  • Recommended grid systems or layouts for large display and responsive designs

    Recommended grid systems or layouts for large display and responsive designsJune 16

    I've been working with 960 layout for years – but was wondering if there are other grid systems that are strongly supported by the design and development community and also have blank or starter pages to work with. --------------Solutions------------

  • How does the grid system work for a full width page?August 25

    How does the grid system work for those websites that expand to the full size of the browser (no matter how you resize) while working with a base 12 grid system (such as 960.gs)? For example, take a look on these websites: http://vevo.com (Note: this

  • Adding custom layout to Omega child theme breaks grid systemSeptember 10

    I'm trying to create an new responsive layout in an Omega child theme. To do this I copied the grids variable from alpha.info into MYTHEME.info and added my layout (called 'narrower') based on the existing pattern: grids[alpha_default][name] = Defaul

  • Blocks and CSS Grid Systems

    Blocks and CSS Grid SystemsSeptember 12

    How do I achieve the following in Drupal: <div class="row"> <div class="col-xs-4 col-md-4">[MY BLOCK HERE]</div> <div class="col-xs-4 col-md-4">[MY BLOCK HERE]</div> <div class="col-xs-4

Copyright (C) 2017 ceus-now.com, All Rights Reserved. webmaster#ceus-now.com 14 q. 0.629 s.