There is a sentiment that leaving math calculations in your CSS is a good idea that I agree with. This is for math that you could calculate at authoring time, but specifically chose not to. For instance, if you needed a 7-column float-based grid (don’t ask), it’s cleaner and more intuitive:
.col {
/* groan */
width: 14.2857142857%;
/* oh, I get it */
width: calc(100% / 7);
}
You could probably prove that the calc()
takes the computer 0.0000001% longer, so explicitly defining the width is technically faster for performance reason — but that is about the equivalent of not using punctuation in sentences because it saves HTML weight.
That math can be a little more complicated as you continue. For example, like in our use cases for calc() article, what about columns in that 7-column grid that span?
.column-1-7 {
width: calc(100% / 7);
}
.column-2-7 {
width: calc(100% / 7 * 2);
}
.column-3-7 {
width: calc(100% / 7 * 3);
}
I’d say that’s rather clean to read and manage.
The readability of the math can be enhanced by comments if it gets too complicated. Say you are trying to account for a margin-based gutter with padding inside of an element:
.parent {
width: 600px;
padding: 18px;
}
.left {
/* base width - 1/2 horizontal padding */
width: calc(400px - 18px);
margin-right: 1rem; /* gutter */
}
.right {
/* base width - 1/2 horizontal padding - gutter */
width: calc(200px - 1rem - 18px);
}
Again, I’d say that’s pretty readable, but it’s also a good amount of repetition. This might call for using variables. We’ll do it with CSS custom properties for fun. You have to pick what is worthy of a variable and what isn’t. You might need fewer comments as the code becomes somewhat self-documenting:
.parent {
--padding: 18px;
--gutter: 1rem;
width: 600px;
padding: var(--padding);
}
.left {
width: calc(400px - var(--padding));
margin-right: var(--gutter);
}
.right {
width: calc(200px - var(--gutter) - var(--padding));
}
That is a decent balance to me. Here’s a step further:
.parent {
--padding: 18px;
--gutter: 1rem;
--parentWidth: 600px;
--leftSize: 2/3;
--rightSize: 1/3;
width: var(--parentWidth);
padding: var(--padding);
}
.left {
width: calc(calc(var(--parentWidth) * var(--leftSize)) - var(--padding));
margin-right: var(--gutter);
}
.right {
width: calc(calc(var(--parentWidth) * var(--rightSize)) - var(--gutter) - var(--padding));
}
Every single number has been given a variable in there. Too far? Maybe. It certainly makes those width declarations pretty hard to wrap your head around quickly. Ana Tudor does some serious stuff with calc()
, as proof that everyone’s comfort level with this stuff is different.
One of the things that made me think of all this is a recent article from James Nash — “Hardcore CSS calc()” — where he builds this:
While the solution took a heavily math-y road to get there, it ends up being only sort of medium-level calculation on the ol’ complexity meter. And note that not everything gets a variable’ only the most re-used bits:
See the Pen Fluid 1 + 2 thumbnail block by James Nash (@cirrus) on CodePen.
Why the nested calc? Instead of
You could do:
I like your sense of humor. Personally, I think we all need to move on and use flex. It is time to leave grids and calculations behind…
I agree 100% here. With many languages it is often a trade off between optimum performance and the readability of your code.
This is the same with magic numbers in your code. I often tell students to create variables for their magic numbers so other people have an easier time understanding their code.
Of course variables take up memory and therefore hinder performance…but it’s a trade off.
This is one of the many reasons I still use Sass. You can have self-documenting variable names like this, with easier-to-read syntax (
$padding
vsvar(—padding)
), wider browser support due to less usage ofcalc,
fewer issues with transitions due to less usage ofcalc
, whatever minor performance benefits there may be by calculating less of it on the fly in the browser, and an overall smaller file size thanks to precompiling each of those variable names and calculations into a few characters.Alternatively you can use font-size as gutter and use relative unites to set all sizes. We use such approach in our library.
It would work in all the browsers and IE11 :).
calc doesn’t need to be used all the time, but I’ve had a couple instances where it really made sense. We should always do everything we can with HTML first, then everything we possibly can with CSS, then move on to things like JavaScript. Use the stack appropriately.
Can’t new css properties do all that? New box properties are already implemented in grid like ‘gap’ can easily do magic. Is there a different performance cost?
I couldn’t agreed more. I had before used fixed value with @media queries, but those were a mess. CSS’s calc function comes in handy and does the job.