Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Using The CSS Function calc() Inside The LESS CSS Preprocessor

By Ben Nadel on
Tags: HTML / CSS

This is a note-to-self. I've several times wanted to use the CSS function, calc(), inside of my LESS CSS preprocessor. However, when I went to use it, I didn't get the expected value. calc() appears to conflict with a built-in function inside of LESS. As such, if you want to use the CSS function, calc(), you need to escape the function call so that the LESS CSS preprocessor defers the interpretation to the browser runtime.

To see what I mean, If I try to run this code through the LESS CSS preprocessor:

p { width: calc( 100% - 50px ) ; }

The generated CSS files contains:

p { width: calc(50%); }

As you can see, the LESS CSS preprocessor ran some sort of calculation during the compilation step that munged the % and px together. Of course, we don't want that - we want the % and px values to stay in place and be evaluated in the browser at runtime. In order to defer the calc() maths to the runtime, we have to escape the invocation:

p { width: ~"calc( 100% - 50px )" ; }

In this case, the quotes treat the calc() expression like an opaque value. And then, the tilde (~) "unwraps" that quoted value, stripping off the quotes and leaving the original calc() in place. Now, if we run this file through the LESS CSS preprocessor, we get the following output:

p { width: calc( 100% - 50px ); }

Perfect! The calc() call has been kept as-is and will now behave as expected in the browser.

Reader Comments

I don't know if this helps, but this is a limitation of the older Less precompiler.

We upgraded to 13.3.x, and this version no longer processes math operators inside calc() (among some other places), which greatly improves the utility of calc(). I think the only real work we did was:

  1. Remove explicit escaping ~"..."
  2. Added math: 'parens-division' to the Less config, which prevents processing / unless it's inside parentheses. This makes it safer to use complex border-radius and font properties without escaping.
  3. Wrap any existing division in parentheses, but this was a fairly quick search to find uses.

With these changes, Less is much cleaner, and we don't use explicit escaping anywhere now. Totally worth the refactor, and no need to remember weird rules-plus it's easier to incorporate variables into calc() functions.

There's a bunch of other small improvements to Less, too. The biggest caveat is it runs a tiny bit slower. (There was actually a huge bug where it ran 2-4 times slower, but that's been resolved, thankfully.)

Reply to this Comment


This is great to know. It's funny, I never think of upgrading LESS for some reason. It's part of our build-system which is a bit of a black-box to me. But, yeah, the version we are using is probably hella-old. I'll have to look at the change-log to see if there are breaking changes to worry about - we have a fairly huge app and I'd be concerned about "smoke testing" the UIs. But, could be worthwhile.

Reply to this Comment


We have a similar situation, with an older AngularJS application (upgraded to 1.8 😅) and a custom-built Gulp 3.9 build system. I've modernized the codebase as best I can, but once you hit so many thousands of lines of code, it becomes a bit overwhelming to upgrade even simple libraries!

I still, personally, much prefer Less's syntax and workflow over Sass/SCSS, and really prefer it to Stylus, which made me actively angry when trying to use it. I was honestly surprised to see it is still in development (3.x is actually still old---4.x required something that I didn't want to fight with, but I can't remember the details off the top of my head).

I found the Less 3.x upgrade to be fairly painless. I actually went through my commit before posting the comment, and those were the only changes I could find, which were both easily searchable.

Of course, it might just be we weren't using any of the other breaking features!

Good luck, I hope you can upgrade, it's been nice having one less weird thing to work on.

Reply to this Comment


Oh man, I would love to get to AngularJS 1.8 🙏 I was able to take one SPA (Single-Page Application) and upgrade it from 1.2 to 1.7, which was a decent-sized effort. But, the main SPA is so much larger. One of the big breaking changes that I've found in my exploration is the change in the way the null option works in select[ngModel]. I think it changed in 1.6 or something and would force me to change a surprising amount of code in our app.

I really need to take inventory of the libraries we use and see what needs to be upgraded (in general). Keeping things up-to-date is not something I have baked into my team's workflow.

Reply to this Comment

You can also take advantage of the fact that LESS/SASS functions are case-sensitive, and CSS functions are not: LESS will process calc(100% - 50px) but ignore Calc(100% - 50px), leaving it to be handled by the browser.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.