Matt
DeveloperAfter reading Paul’s great blog article about using calc() more (http://bit.ly/2o5Vpxg) I found myself finding more and more great uses for it, from basic layout and sizing to more complicated transforms and animations.
One great use of calc() is for margins in a left aligned grid. Using flex, we could set “justify-content: space-between;”, but that will only work for a full row of items. Instead, assuming we know how many items we want to display on a row, we can use calc() to work out our margins for us, so that our grid looks right no matter how many items are in a row. Our margin equation is as follows:
container-width – ((item-width * num-items) / (num-items – 1))
Now, using some real values, let’s set a width of 250px for each item in our grid, and assume our grid design has 4 items in a row, our equation looks like this:
100% – ((250px * 4) / 3)
Which can easily be wrapped in calc(), like so:
calc(100% – ((250px * 4) / 3));
Now, because this is 2017, we’re using a preprocessor for our CSS, so we’d store all the important information in variables. We can do the same for our calculated margin, and then use it in place, as demonstrated below. We’re using SCSS for this example, so you’ll notice that, inside the calc() function, variables are interpolated by wrapping them in #{}.
$grid-item-width: 250px; $grid-item-rows: 4; $grid-item-margin: calc((100% - (#{$grid-item-width} * #{$grid-item-rows})) / (#{$grid-item-rows} – 1)); .grid { display: flex; flex-wrap: wrap; } .grid-item { width: $grid-item-width; margin-right: $grid-item-margin; &:last-child { margin-right: 0; } }
We may want to change the layout and margins when the viewport can no longer fit our 4 item row, but that’s something this article assumes you’ve got covered. The above code is fine as long as grid items never change, but what about if we have a hero box that needs to be double width? Well we can work out the width of our big box, using the equation below:
($item-width * 2) + $margin-width
So this should be as simple as wrapping the above, with the variables interpolated, in calc(), right? Unfortunately not, as that will give us the following line of CSS:
width: calc((250px * 1.5) + (calc((100% - (250px * 4)) / 3)));
The problem with the above CSS is that a calc() function cannot contain another calc(), as anything inside the opening calc() is already subject to calc. A simple solution to this would be to store the margin equation as one variable, and then reference it elsewhere, like so:
$grid-item-margin-eq: (100% - (#{$grid-item-width} * #{$grid-item-rows})) / (#{$grid-item-rows} – 1); $grid-item-margin: calc(#{$grid-item-margin-eq}); $grid-item-width-big: calc((#{$grid-item-width} * 2) + #{$grid-item-margin });
This should, in theory, allow us to use our equation variable in other places when we need to perform calculations on the value. In practice, unfortunately, you’ll notice that all of the parentheses have been stripped from the original equation. This is where unquote() comes in; it allows us to escape a string to be used elsewhere. In our case, it allows us to do the following:
$grid-item-margin-eq: unquote(“100% - (#{$grid-item-width} * #{$grid-item-rows})) / (#{$grid-item-rows} – 1)”); $grid-item-margin: calc(#{$grid-item-margin-eq}); $grid-item-width-big: calc((#{$grid-item-width} * 2) + #{$grid-item-margin-eq});
Thanks to unquote, we can store our original calculation, and reference it again when we need to play around with our grid, and it means we can avoid any messy duplicate calculations.