The wonderful calc() function

13 June 2011, in CSS | 31 comments »

Sitting right at the top of my CSS wishlist was always the implementation of the calc() function. With it now being supported by not only Firefox 4 but Internet Explorer 9, I think it’s time for a quick overview on how useful calc() can be and why it would be great to see more usage of it.

What is calc()?

The calc() function can be used to specify lengths. For example, you can use it for values of borders, margins, paddings, fonts, etc. Calculating widths can be extremely complicated, especially in fluid layouts; calc() is unique because it lets you make these calculations within the CSS itself.

It accepts addition (“+”), subtraction (“-“), multiplication (“*”) and division (“/“).

Let’s look at a simple example:

div {
	width: calc(100% - 20px); }

Easy, right? You can also have more than one calculation within a single function, for example:

div {
	width: calc(100% - 20px + 2px*2); }

In the current specification, it is stated that multiplication and division expressions have precedence over addition and subtraction. This means that, in the previous example, “2px*2” will be calculated before the other two expressions. Also, note that divisions by zero will (or should) result in an error.

The spec is not yet finalised (there is an Editor’s Draft version that expands on the CSS3 Values and Units Working Draft version), but these are the basics.

A simple use case

To show calc’s usefulness further, I’ve created a basic layout where I’ve applied it generously.

The layout consists of a main content container on the left, a sidebar on the right, and a footer, below the previous two. Here is the basic markup with only part of the text being shown, for brevity:

<div class="wrapper">

	<div id="main">
		<h1>Far far away…</h1>
		<p>Far far away, behind the word mountains…</p>
	</div>

	<div id="accessory">
		<ul>
			<li><a href="#">Far far away…</a></li>
			<li><a href="#">Separated they live…</a></li>
			<li><a href="#">A small river named…</a></li>
		</ul>
	</div>

	<div id="footer">
		Visit the article…
	</div>

</div>

After the (hopefully) self-explanatory simple reset (please check the full CSS in the example document), font properties and defining the links, I will define the look of my body element:

body {
	background: #E8EADD;
	color: #3C323A;
	padding: 20px; }

After that, I will say that the main container div should be full width (100%) minus 40px and aligned horizontally in the middle of the viewport:

.wrapper {
	width: 1024px; /* Fallback for browsers that don't support the calc() function */
	width: -moz-calc(100% - 40px);
	width: calc(100% - 40px);
	margin: auto; }

In the CSS above, I have also added a fallback width for browsers that don’t support the calc() function, and the vendor prefix for Firefox 4.

Following this, I will set the borders, width and margins of the main container div, and I will float it left:

#main {
	border: 8px solid #B8C172;
	float: left;
	margin-bottom: 20px;
	margin-right: 20px;
	padding: 20px;
	width: 704px; /* Fallback for browsers that don't support the calc() function */
	width: -moz-calc(75% - 20px*2 - 8px*2);
	width: calc(75% - 20px*2 - 8px*2); }

So what is happening in this calc() function? I am stating that I want my container div to be 75% wide (75% of its parent container, remember), but from these 75% I need to subtract “20px*2” to account for the padding on each side, and “8px*2” to account for the border on each side.

Moving onto the sidebar, I want it to occupy the remaining 25% inside the parent container, but this value will have to allow for padding, borders and the margin of the sidebar div (20px).

#accessory {
	border: 8px solid #B8C172;
	float: right;
	padding: 10px;
	width: 208px; /* Fallback for browsers that don't support the calc() function */
	width: -moz-calc(25% - 10px*2 - 8px*2 - 20px);
	width: calc(25% - 10px*2 - 8px*2 - 20px); }

Deconstructing “calc(25% - 10px*2 - 8px*2 - 20px)”, we have the original 25%, minus “10px*2” to account for the padding on each side, minus “8px*2” to account for the border on each side, minus “20px” to account for the main container’s right margin.

The footer is simply a full width div, I shan’t bore everyone by explaining that.

Because the sidebar becomes too small after a certain point, I’ve also included a simple media query that unfloats the main and sidebar containers, and recalculates their widths so they are 100% wide minus their respective padding and border values.

@media screen and (max-width: 768px) {
	#main, #accessory {
		float: none; }
	#main {
		margin-right: 0;
		width: -moz-calc(100% - 20px*2 - 8px*2);
		width: calc(100% - 20px*2 - 8px*2); }
	#accessory {
		width: -moz-calc(100% - 10px*2 - 8px*2);
		width: calc(100% - 10px*2 - 8px*2); }
}

This is a very simplistic use case, but hopefully it’s enough to spike your interest and prompt you to find out more about wonderful calc().

Browser support

Calc is supported by IE 9 and Firefox 4 (which requires the -moz-calc vendor property). I can understand, however, how this can be a bigger problem if compared to the relatively deficient browser support of things such as animations. While animations are accessory, incorrect measurements can break a site.

This does not mean though that we shouldn’t be trying these out. If Firefox and IE have made the effort to implement this, and these are the browsers with the biggest market share, I would suppose the others will follow suit (I’m not sure whether WebKit or Opera are working on this or not, but do let me know if they are).

In my example, I’ve opted for including fallback, absolute lengths for non-comforming browsers, which gives them a non-fluid layout. This might not be acceptable in your particular case though, but it is for my use case.

Reference

If you’d like to know more about calc(), head on to the following resources:

Final note

Along with few other CSS3 properties, calc() is one of those new pieces of CSS that can truly make our lives easier. I haven’t tested it in terms of performance, so I don’t know how big an impact all these calculations might have on the browser, but I’d guess it won’t be more consequential than all those transitions and transforms that everyone seems to be so happy to indiscriminately add to their sites.

So, have you used calc() on a project? Is this something you feel is a welcome addition to CSS? I’d love to hear your thoughts.

There are 31 comments:

  1. Hoboy, this is gonna make flexible layouts so much less painful! Great news.

    I do wonder though: with the introduction of a calculation function isn’t that edging CSS closer to being a programming language: something it’s not meant to be? And if that’s the direction it’s going, surely it’s time that CSS supported variables…

  2. Tai says:

    CSS is headed towards being a proper language. However, it is not yet. I wouldn’t be surprised if .calc() crippled performance.

    Have you tested it?

    • inayaili says:

      Hey Tai,

      You’re probably right. I haven’t tested it though, as I mentioned in the final note.

      • Tai says:

        Thanks for the reply. I see now that you did mention it is untested.

        Another solution would be to use something like SASS or LESS. Basically it lets you use variables. The nice thing there is that is it all happening server side. Apparently there is a Firefox plugin that lets you view it before it is rendered into plain css.
        Can’t vouch for either though because I haven’t used them yet.

        • Ricky Duckworth says:

          @Tai – I’ve used LESS many times on sites and it’s improved my efficiency ten fold. This and nested classes, mixins, variables etc makes CSS more of a programming language, and really should be how css works. Great thing about LESS is you can either do it server side or client side. If you do it client side, I would advise using it only in dev and then copying the output to your css file. Also note, LESS allows you to still use CSS extension on files, and works in IE as well. I have no experience with SASS.

  3. Hem says:

    My question is why can’t we define width directly? or why don’t you use JS? why calc function in css if u are making everything hard coded? and yes it is not browsers compatible… and last question is why use hacks to manupulate browsers. FF might remove -moz property in future.

    I won’t use it till all browser support or at least IE7 and 8 disappears from market

    • Jack says:

      How incredibly progressive of you! ‘IE doesn’t do it so neither will I.’

      You have fun with that. Also, where do you see a hack in this code? The -moz prefix is most certainly not a hack my friend, it’s a vendor specific prefix and she has included the native option of calc underneath it.

      • Hem says:

        It is about universal acceptance, almost 25% user of the world use IE (http://www.w3schools.com/browsers/browsers_stats.asp) yes, vendor specific prefix are CSS3 hack..http://www.alistapart.com/articles/stop-forking-with-css3/

      • Tai says:

        Progressive enhancement is so old school! If computer years are 15/1, the concept originated in 2003, so that like… 120 years old!
        I encourage you to started thinking in terms of graceful degradation.
        At this moderate internet company we don’t support anything older than IE8. Our audience is pretty savvy though.

        Okay so Javascript is not really a proper fallback. However, detecting whether calc() is supported, and warning the user to upgrade to a modern browser (not IE (even IE9!)) would be a good way to go.

        There are ways to make CSS3 work in IE but, the question remains, why? Aren’t we just enabling Microsoft to keep making C(@p?

        • Wow, how tolerant of your users you are!

          Normal people don’t often care about upgrading their browser. A lot don’t even understand what a browser is, they just want to use the internet. Chrome updates automatically and that’s the perfect solution: always on the cutting edge. People still using browsers that require manual updating will probably get an upgrade when they upgrade their computer. Are you happy excluding all those users that don’t care about running the latest software, or can’t because of IT restrictions at work?

          Interesting you should mention the age of the term progressive enhancement… graceful degradation is a term that has been around for much, much longer.

          • Tai says:

            Graceful degradation has been around forever in the software world. However, in reference to supporting old browsers with just the extreme basics, it is relatively recent.
            The best example I can think of is the Universal IE6 stylesheet:
            http://code.google.com/p/universal-ie6-css/

            If enough sites are throwing up warnings to update, then maybe old school IT departments will get pressure to do their job.

            My bank uses IE 5.5! Talk about a gaping security hole. Chase should know better (or not).

            We are doing users a favor by encouraging them to update. When creating all your conditional stylesheets, you are effectively enabling your users.

            Just like giving a junkie money when what they really need is help.

            No rounded corners, no box shadow, nothing extra for IE. This is what some of the folks at YayQuery are advocating and I couldn’t agree more.

        • Magnus says:

          Have you ever worked with customers in regulated industries? Upgrading browsers in companies in regulated industries, e.g. Banks or pharmaceutical companies is not an easy due to strict testing and validation policies. So developing web based business applications for such customers, you may very well be faced with requirements for supporting a specific browser version,and typically not the latest.

    • Scott says:

      > why can’t we define width directly?

      You can, if you know the width. Obviously in the real world you would always simplify “20px + 2px*2″ to “24px”. But you don’t know in advance what size “100% – 24px” is actually going to be.

      > why don’t you use JS?

      JS isn’t designed for layout issues, especially vital ones like getting column widths correct. Plus you’re gonna get the “flash of un-javascripted content” problem.

      > FF might remove -moz property in future

      Hence the fallback to the “calc” property for browsers that support it. Assuming that calc stays in the spec, then all browsers will eventually support it.

      > why calc function in css if u are making everything hard coded?

      As above, it is used so that you can mix units like % and px. However, the problem I see is that this is its one and only use. I can’t think of a single other use for the property.

  4. Henri256 says:

    My opinion is that calc misses completely the main point. We want calculation between brothers divs. I want calc to be able to give for example div1 the same height than div2, or half its height, or one third of its height, or half of its width, etc.. Having two or three or for divs with the same height is one of the main problem of css 2. Calc could have been an elegant solution to that problem. It seems it won’t.

  5. =) I think that this new CSS specifications are starting to make the browsers implement their own Lesser compiler.
    In a way, it’s cool… But, why make the client process extra ALU data when that can be done in the development time (with the lesser compiler, for example) ?
    It shouldn’t be just the ‘designers’ view to count, but also the clients’/users’ view too..

  6. Pablinho says:

    What a neat property, I wonder how we will have to wait until it is fully implemented.

    Wonderful things could be done when coding liquid layouts…

  7. André Luís says:

    First, a statement:

    I’ve always thought of this dire need of having a calc() function would lead to sloppy, inflexible layouts. I think you need an example with ems or a unit other than px in there to make it clear you can use other units as well.

    Now, I’ve slowly grown to accept the fact that this way we’ll probably need less and less elements on the DOM and that’s a good thing. But I still feel dirty using it… you see, back in the day, I spent two days debugging each and every line of a full javascript webapp only to realize the incessant javascript error was in fact a CSS expression on IEs gone bad.

    The lesson there was the sheer amount of times the browsers needed to calculate/execute that expression. It’s not like transitions, wherein you specify exactly the moment the browser need to execute them (more or less). Now, for layout purposes, we know the browser re-evaluates the layout over and over again… more often than we imagine.

    Although, using calc() is slightly better since it’s limited to calculations, which shouldn’t impact performance as much. So that shouldn’t be a show-stopper.

    One last thing… if you update the padding on that element later, the calc won’t update itself. You can’t refer an element and use it’s property like so:
    calc( element(“ul li a:first-child”).width –
    ( element(this).padding-left + element(this).padding-right ) )
    (boy, that felt like writing javascript! but perhaps we should discuss this option on the www-style mailing list)

    So, IMHO, most of the times you’ll be better off just specifying the width you want on a container element and adding other box model properties on a child. I care a lot about maintainability… :-\ But for stuff that is absolutely impossible to achieve without calc(), I’ll go for it. Eg: calc( 80% – 200px ) :)

    Thanks for the write up, Yaili.

  8. Gport says:

    This only works for height and width I presume?

    I just tried it out on Firefox 4, with a div of 200px high, and tried to give it a margin-top of 50%-200px to align it vertically (at least that was what I tried to achieve), but my results are so much off from the centre.

  9. Mark Stickley says:

    I was trying this out just now but couldn’t figure out why it wasn’t working. Turns out you NEED to have spaces either side of a minus operator. How finicky is that? Very un-CSS-like. Is this behaviour in the spec, do you know?

  10. Luiz says:

    Está sendo implementado no webkit desde 2007: Veja em:
    https://bugs.webkit.org/show_bug.cgi?id=16662

  11. Radu says:

    Please put a Like button on this site! :)

  12. Ryan B. says:

    I haven’t used the calc function yet in any projects but I’d love to try it out. Thanks for the walkthrough.

  13. Matt M says:

    Does this work with browser resizing as well? One of the benefits of doing this with JS is that you can make it say, “re-calculate X value whenever the browser is resized.” I am not sure if doing it with CSS does this automatically, but if it did that would be a huge win!

  14. And why not use:
    display: box;
    box-sizing: content-box;

    That way width is automatically calculated.

    • Bart says:

      I totally agree. Although in this case, box-sizing: border-box would make more sense.

      Using calc() on width to take padding & borders into account seems bad because you’re duplicating information.

      If you have to change the padding of an element, you would have to change it in both the padding declaration *and* the calculated width declaration.

      Perhaps calc() can be useful in some cases, but I think using the box-sizing declaration is a more elegant solution to these problems.

    • x says:

      you meant box-sizing: border-box?

  15. Russ says:

    I’ve written a polyfill for the CSS min() and max() functions. Someone with a bit more patience than me might be able to expand the code to support calc() as well.

    https://github.com/indyarmy/jquery.cssMinMax.plugin

  16. Daniel Roch says:

    As Moises Garcia and Bart just said, the box-sizing property is more efficient, small and faster to do the same job.

    Calc should only be used in responsive design when a section should calculted according to both flexible and fixed elements.

  17. RR says:

    calc() is great – but it does not work with IE 9 (e.g. your example)…

    it falls back to the fixed width of 1024px – any ideas?

Leave a comment: