Using The CSS Function calc() Inside The LESS CSS Preprocessor
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 insidecalc()
(among some other places), which greatly improves the utility ofcalc()
. I think the only real work we did was:~"..."
math: 'parens-division'
to the Less config, which prevents processing/
unless it's inside parentheses. This makes it safer to use complexborder-radius
andfont
properties without escaping.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.)
@Phil,
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.
@Ben,
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.
@Phil,
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 inselect[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.
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 ignoreCalc(100% - 50px)
, leaving it to be handled by the browser.