Grow your CSS skills. Land your dream job.

#132: A Quick Useful Case for Sass Math and Mixins

I had a little design situation come up where I was making a fluid grid of boxes with floats. I wanted to specify how many boxes across a row was very easily, and have them flush against both edges of the container. Not too difficult, as we know from not overthinking grids and using the right box-sizing, you can get four floated boxes in a row width 25% width - easy.

But what if you want to use margin between the boxes? Still totally possible, just requires some thinking. Say you want four in a row, you'll need to figure out how much space you have left after all the margin is used. Because you don't want margin on the last one in the row, that's 3 margins:

100% - (3 * MARGIN)

3 is really "rows you want minus one", so:

100% - ((ROWS - 1) * MARGIN)

Then you divide the space you have left by how many boxes you want on that like, so:

(100% - ((ROWS - 1) * MARGIN)) / ROWS

You could use Sass for that:

$numPerRow: 4;
$margin: 2%;

width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow);

Even better, we make that into a @mixin, so we can just call it whenever we need it:

@mixin rowMachine($numPerRow, $margin) {
  width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow);
  &:nth-child(n) {
    margin-bottom: $margin;
    margin-right: $margin;
  }
  &:nth-child(#{$numPerRow}n) {
    margin-right: 0;
    margin-bottom: 0;
  }
}

Watch the video to learn about that tricky bit with :nth-child

In the video, the bit in the beginning with the Jade loop you can learn more about here.

And here's the Pen:

See the Pen Simple Technique for using Sass for Rows by Chris Coyier (@chriscoyier) on CodePen.

Comments

  1. Marco Biedermann
    Permalink to comment#

    I used something similar with a image gallery but without some comlpicated math. Works pretty well!
    Use negative margins to fix the padding of the elements.

    img {
      display: block;
      max-width: 100%;
    }
    
    .gallery {
      ul {
        @extend .cfx;
        list-style: none;
        margin: -.25em;
        padding: 0;
    }
    
    li {
        @include box-sizing(border-box);
        float: left;
        padding: .25em;
        width: 25%;
    
        @media (min-width: $media-tablet + 1) and (max-width: $media-desktop) {
            width: 33.33%;
        }
    
        @media (max-width: $media-tablet) {
            width: 50%;
        }
    
      }
    }
  2. Bruno Almeida
    Permalink to comment#

    Amazing that.

    I was just making a rule that lists items and stopped to see the twitter when I came across this.

    Thanks

  3. Permalink to comment#

    Nice screen-cast :)

    I used something similar for a grid system http://www.get-maze.co.uk/

    /* Variables */ 
    
    $grid-columns: 12;
    $unit: (100%  / $grid-columns) ;
    $gutter: 2%;
    $push: 0 !default;
    /* Mixin */ 
    @mixin grid($col, $push) {
    @include box-size;
    @include transition;
    width: (($unit * $col) - $gutter ) + ( $gutter / ( $grid-columns / $col) );
    float: left;
    margin-right: $gutter;
      @if $push > 1 {
      margin-left:(($unit * $push) ) + ( $gutter / ( $grid-columns / $push) );
      }
        &:last-child {
        margin-right:0;
        }
    }
  4. Permalink to comment#

    Sometimes it’s more appropriate to use Susy. Version 2.0 was released earlier this month and it is indeed simple meta-framework with Sass 3.3 dependency. The point is, it’s really simple, but you can make it really complicated with all powerful features and nerd the heck out of it! =)

    Definitely check it out: Susy 2.0

  5. Permalink to comment#

    Sometimes it’s more appropriate to use Susy. Version 2.0 was released earlier this month and it is indeed simple meta-framework with Sass 3.3 dependency. The point is, it’s really simple, but you can make it really complicated with all powerful features and nerd the heck out of it! =)

    Definitely check it out: Susy 2.0

    update: wrong e-mail address on previous comment

  6. Permalink to comment#

    Hey Chris,

    Thanks for the video, quick and relatively simple to implement.. but I got a bit lost with the :nth-child(n) bit. Can you explain a bit about how you can use :nth-child on ‘n’ instead of literally specifying something, e.g 4 or 5. Your link to your other article on nth-child didn’t really help me.

    • Permalink to comment#

      oh wait. I think I get it now!

      with nth-child(n) you give the margin-right to each ‘card’ to ensure it’s got some margin-right in case it moves around and then take it away on the last one with the other use nth-child

      aha!

  7. echo
    Permalink to comment#

    Ya Chris, and you were thinkin’ out loud on this, it seems we don’t have to specify margin-bottom: 0; on the last in the row, although it doesn’t break anything since the bottom margin is held open by the others in the row.

    Thanks a lot for this video, I love watching and learning from them!

  8. Richard Dale
    Permalink to comment#

    No support in ie for nth child though so using this you’d need ie9, not much of an issue these days but worth considering.

  9. Mark Spain
    Permalink to comment#

    Hey Chris, great example! I will definitely be incorporating this into my future projects. I think I may have spotted a problem though… you probably shouldn’t be setting margin-bottom: 0; on nth-child(#{numPerRow}n) because it can cause problems when you set display: block; on an <img />. I made a pen to play around with animating border-width on a grid of image links that illustrates the issue: pen

    Check out what happens when you comment/un-comment the margin-bottom: 0; in the rowMachine mixin.

  10. Woah! Awesome! I needed this a looong, long time ago, to display a grid of logos to a client’s site and failed. Now this is how you do it properly! Thanks!

  11. I believe (from the tests I made on codepen) there is no need for including the margin-bottom: $margin; margin-right: $margin; inside the nth-child(n). You also don’t need to reset the margin-bottom to 0px on the last element of the row.

    Please tell me if I’m wrong.

  12. Jordie
    Permalink to comment#

    Hmmm, this will be handy, I have a client that requires the homepage to be a metro style, was using flex-box. although wasn’t what I was looking for. Will definitely give this a shot

  13. Gustavo Caso
    Permalink to comment#

    Thanks Chris.
    Really useful mixins and simple way of handling grid layout.

  14. There is no point in setting margin-bottom: 0; at the last element in a row, it is just obsolete.

  15. Abhishek
    Permalink to comment#

    There is a mixun called omega in neat which does that.

  16. BrutusMaximus

    Great video! It’s now included in my collection of helpfull mixins. I added an extra feature to the mixin, that clears the “row” . Just in case there is an item that has an extra long title. And I also removed the margin-bottom.

    To make it work, I also changed the mediaqueries that called the mixin. Check my codepen and edit one of the titles from “Darth” to ie. “Darth Vader is the fictional character in the Star Wars”.

  17. nishi

    Hello,

    I need your help for writing below syntax in Less.
    &:nth-child(#{$numPerRow}n)
    {

    }
    how could I do it?

    Thanks…

  18. Here’s something similar I’ve done to achieve a similar responsive layout:

    // Generate responsive rules
    $max-resolution: 1920px;
    $cell-width: 250px; 
    // actual cell width should = $cell-width ( 1 - ( 1 / ceil( [the-browser-resolution] / $cell-width ) )
    
    @for $i from 0 to ceil($max-resolution/$cell-width) {
        $min: $i * $cell-width;
        $max: ($i + 1) * $cell-width;
        @include respond-between($min,$max) {
            $width: 100% / ($i + 1);
            .container .cell {
                width: $width;
                &amp;:nth-child(#{$i + 1}n + 1){
                    clear: both; 
                }
            }
        }
    }
    

    Simple responsive mixin:

    @mixin respond-between($min: 0px, $max: 1920px) {   
        @media only screen and (min-width: $min) and (max-width: $max) { @content; } 
    }
    

    -Mike McCormick

  19. Bullz

    You don’t need flexbox… if you use equal height pictures ;-)

  20. Jerry

    That’s very very cool -thanks

  21. Robert Brodziak

    Mr. Coyier, you’re the best!

  22. Permalink to comment#

    SUPER AWESOME! Where were you like 3 weeks ago when I was pulling my hair out trying to make this work!? Thanks for a great screencast!

  23. Joaquin
    Permalink to comment#

    Wow, this is amazing!

Leave a Comment

Current ye@r *

*May or may not contain any actual "CSS" or "Tricks".