CSS: full calc () function guide

CSS has a special function calc()used to perform simple calculations. Here is an example of its use:

.main-content {
  /*  80px  100vh */
  height: calc(100vh - 80px);
}

Herecalc() you can experiment with the CSS code in which it is used . The author of the article, the translation of which we are publishing today, wants to talk about everything that is worth knowing about this very useful function.





Calc () function and CSS property values


The only place you can use the function calc()is with CSS property values. Take a look at the following examples in which, using this function, we set the values ​​of various properties.

.el {
  font-size: calc(3vw + 2px);
  width:     calc(100% - 20px);
  height:    calc(100vh - 20px);
  padding:   calc(1vw + 5px);
}

The function calc()can also be used to set any separate part of the property:

.el {
  margin: 10px calc(2vw + 5px);
  border-radius: 15px calc(15px / 3) 4px 2px;
  transition: transform calc(1s - 120ms);
}

This function may even be part of another function that is responsible for forming part of a certain property! For example, here it is calc()used to adjust the position of the gradient color change:

.el {
  background: #1E88E5 linear-gradient(
    to bottom,
    #1E88E5,
    #1E88E5 calc(50% - 10px),
    #3949AB calc(50% + 10px),
    #3949AB
  );
}

The calc () function is a tool for working with numeric properties.


Please note that in all the above examples we use the function calc()when working with numerical properties. We will also talk about some features of working with numerical properties (they are associated with the fact that sometimes the use of units of measurement is not required). Now we note that this function is designed to perform operations with numbers, and not with strings or with something else.

.el {
  /* ! */
  counter-reset: calc("My " + "counter");
}
.el::before {
  /* ! */
  content: calc("Candyman " * 3);
}

There are many units of measurement that can be used in CSS to specify the size of the elements and their parts: px, %, em, rem, in, mm, cm, pt, pc, ex, ch, vh, vw, vmin, vmax. All of these units can be used with the function calc().

This function can also work with numbers used without specifying units:

line-height: calc(1.2 * 1.2);

It can also be used to calculate angles:

transform: rotate(calc(10deg * 5));

This function can also be used in those cases when no calculations are performed in the expression passed to it:

.el {
  /*   ,    */
  width: calc(20px);
}

Calc () cannot be used in media queries


Although this function is designed to set CSS property values, it unfortunately does not work in media queries:

@media (max-width: 40rem) {
  /* 40rem   */
}

/*  ! */
@media (min-width: calc(40rem + 1px)) {
  /* ,  40rem */
}

If once such constructions prove to be workable, this will be very good, as this will allow creating mutually exclusive media queries that look very logical (for example, the ones shown above).

Using different units in one expression


The admissibility of using different units of measure in one expression is probably the most valuable opportunity calc(). Similar is used in almost all of the above examples. Here, just to draw your attention to it, another example that shows the use of different units of measurement:

/*      */
width: calc(100% - 20px);

This expression reads as follows: "The width is equal to the width of the element from which 20 pixels are subtracted."

In a situation where the width of the element can change, it is completely impossible to calculate the desired value in advance, using only indicators expressed in pixels. In other words, you cannot preprocess calc()using something like Sass and trying to get something like polyfill. Yes, this is not necessary, given the fact that browsers supportcalc() very well . The point here is that such calculations, in which values ​​expressed in different units are mixed, must be performed in the browser (during the "execution" of the page). Namely, the ability to perform such calculations is the main value .calc()

Here are a couple more examples of using values ​​expressed in different units:

transform: rotate(calc(1turn + 45deg));

animation-delay: calc(1s + 15ms);

You can probably preprocess these expressions, because they mix values ​​whose units are not related to anything that is determined while the page is running in the browser.

Comparison of calc () with preprocessor calculations


We just said that the most useful feature is calc()not amenable to preprocessing. But preprocessing gives the developer some features that match the capabilities calc(). For example, when using Sass, you can also calculate property values:

$padding: 1rem;

.el[data-padding="extra"] {
  padding: $padding + 2rem; //  3rem;
  margin-bottom: $padding * 2; //  2rem; 
}

Here, calculations can be made indicating the units of measure, here you can add the quantities expressed in the same units of measurement, you can multiply certain values ​​by values ​​whose units are not indicated. But it is not possible to perform calculations with values ​​expressed in different units of measurement. Restrictions resembling restrictions are imposed on such calculations calc()(for example, the numbers by which something is multiplied or divided must be values ​​without units).

Disclosing the meaning of numeric values ​​used in CSS


Even if you do not take advantage of opportunities that are achievable only with help calc(), this function can be used to reveal the meaning of the values ​​used in CSS. Suppose you want the value of a property to be exactly 1/7 of the width of the element:

.el {
  /*   , */
  width: calc(100% / 7);

  /*   */
  width: 14.2857142857%;
}

This approach may be useful in something like a kind of self-styled CSS-API:

[data-columns="7"] .col { width: calc(100% / 7); }
[data-columns="6"] .col { width: calc(100% / 6); }
[data-columns="5"] .col { width: calc(100% / 5); }
[data-columns="4"] .col { width: calc(100% / 4); }
[data-columns="3"] .col { width: calc(100% / 3); }
[data-columns="2"] .col { width: calc(100% / 2); }

Mathematical Operators of calc () Function


The expression to be evaluated with the help of calc()the operators you can use +, -, *and /. But in their application there are some features.

When adding ( +) and subtracting ( -), you must use the values ​​with the specified units

.el {
  /*  */
  margin: calc(10px + 10px);

  /*  */
  margin: calc(10px + 5);
}

Please note that the use of incorrect values ​​leads to the fact that a particular announcement also becomes incorrect.

When dividing ( /), it is necessary that the unit of measurement should not be indicated on the second number

.el {
  /*  */
  margin: calc(30px / 3);

  /*  */
  margin: calc(30px / 10px);

  /*  (   ) */
  margin: calc(30px / 0);
}

When multiplying ( *), one of the numbers should not have a unit of measure:

.el {
  /*  */
  margin: calc(10px * 3);

  /*  */
  margin: calc(3 * 10px);

  /*  */
  margin: calc(30px * 3px);
}

The importance of spaces


The spaces used in expressions are important in addition and subtraction operations:

.el {
  /*  */
  font-size: calc(3vw + 2px);

  /*  */
  font-size: calc(3vw+2px);

  /*  */
  font-size: calc(3vw - 2px);

  /*  */
  font-size: calc(3vw-2px);
}

Here it is quite possible to use negative numbers (for example, in a construct like calc(5vw — -5px)), but this is an example of a situation in which spaces are not only necessary, but also useful in terms of clarity of expressions.

Tab Atkins told me that the reason why the addition and subtraction operators should be separated by spaces is, in fact, the peculiarities of parsing expressions. I can’t say that I fully understood this, but, for example, the parser processes the expression 2px-3pxas a number 2with a unit of measurepx-3px. And nobody will need such a strange unit of measurement. Parsing expressions with the addition operator has its own problems. For example, a parser can take this operator as part of the syntax used to describe numbers. I thought that a space is needed to correctly handle the syntax of custom properties ( --), but that is not the case.

When multiplying and dividing spaces around operators are not required. But I believe that you can recommend using spaces with these operators in order to increase the readability of the code, and in order to not forget about spaces and when entering expressions that use addition and subtraction.

Spaces that separate brackets calc()from an expression do not play any role. The expression, if desired, can even be selected by moving to a new line:

.el {
  /*  */
  width: calc(
    100%     /   3
  );
}

True, you should be careful here. There calcshould be no spaces between the name of the function ( ) and the first opening bracket:

.el {
  /*  */
  width: calc (100% / 3);
}

Nested constructions: calc (calc ())


When working with a function calc(), you can use nested constructions, but this is not really necessary. This is similar to using brackets without calc:

.el {
  width: calc(
    calc(100% / 3)
    -
    calc(1rem * 2)
  );
}

It makes no sense to build nested constructions from functions calc(), since the same can be rewritten using only brackets:

.el {
  width: calc(
   (100% / 3)
    -
   (1rem * 2)
  );
}

In addition, parentheses are not needed in this example, since the rules for determining the priority of operators are applied when calculating the expressions presented here. Division and multiplication are performed before addition and subtraction. As a result, the code can be rewritten like this:

.el {
  width: calc(100% / 3 - 1rem * 2);
}

But in the event that it seems that the additional brackets make the code clearer, they can be used. In addition, if without parentheses, based only on the priority of the operators, the expression will be calculated incorrectly, then such an expression needs parentheses:

.el {
  /* ,   , */
  width: calc(100% + 2rem / 2);

  /*     ,    */
  width: calc((100% + 2rem) / 2);
}

Custom CSS Properties and calc ()


We already learned about one of the great features calc(), about calculations that use values ​​with different units. Another interesting feature of this function is how it can be applied with custom CSS properties. Custom properties can be assigned values ​​that can be used in calculations:

html {
  --spacing: 10px;
}

.module {
  padding: calc(var(--spacing) * 2);
}

I’m sure it’s easy to imagine a set of CSS styles in which many settings are done in one place by setting values ​​for custom CSS properties. These values ​​will then be used throughout the CSS code.

Custom properties, in addition, can refer to each other (note that it is calc()not used here). The obtained values ​​can be used to set the values ​​of other CSS properties (but here you can calc()not do without).

html {
  --spacing: 10px;
  --spacing-L: var(--spacing) * 2;
  --spacing-XL: var(--spacing) * 3;
}

.module[data-spacing="XL"] {
  padding: calc(var(--spacing-XL));
}

To some this may not seem very convenient, since when accessing a custom property, you need to remember about calc(). But I find this interesting in terms of code readability.

The source of custom properties can be HTML code. Sometimes this can be extremely useful. For example, in Splitting.js indexes are added to words and symbols.

<div style="--index: 1;"> ... </div>
<div style="--index: 2;"> ... </div>
<div style="--index: 3;"> ... </div>
div {
  /*     HTML (   ) */
  animation-delay: calc(var(--index, 1) * 0.2s);
}

Assigning Units to Custom Properties After Declaring These Properties


Suppose we are in a situation where it makes sense to write a number without units of measure in a custom property, or in a situation where such a number will be convenient, before actual use, somehow convert without using units of measure. In such cases, a custom property can be assigned a value without specifying units. When it will be necessary to convert this number into a new one, expressed in certain units of measure, it can be multiplied by 1indicating the desired units of measurement:

html {
  --importantNumber: 2;
}

.el {
  /*    ,  ,        */
  padding: calc(var(--importantNumber) * 1rem);
}

Work with flowers


When describing colors using formats such as RGB and HSL, numbers are used. These numbers can be worked on calc(). For example, you can set some basic HSL values, and then change them as needed ( here is an example):

html {
  --H: 100;
  --S: 100%;
  --L: 50%;
}

.el {
  background: hsl(
    calc(var(--H) + 20),
    calc(var(--S) - 10%),
    calc(var(--L) + 30%)
  )
}

You cannot combine calc () and attr ()


CSS function attr()can seem quite attractive. And the truth: you take the attribute value from HTML, and then use it. But…

<div data-color="red">...</div>
div {
  /*    */
  color: attr(data-color);
}

Unfortunately, this function does not understand the “types” of values. As a result, it attr()is suitable only for working with strings and for setting CSS properties using it content. That is, such a design turns out to be quite working:

div::before {
  content: attr(data-color);
}

I said this here because someone might want to try to extract a attr()certain number from the HTML code and use it in the calculations:

<div class="grid" data-columns="7" data-gap="2">...</div>
.grid {
  display: grid;

  /*         */
  grid-template-columns: repeat(attr(data-columns), 1fr);
  grid-gap: calc(1rem * attr(data-gap));
}

However, the good thing is that it does not really matter, since custom properties in HTML are great for solving such problems:

<div class="grid" style="--columns: 7; --gap: 2rem;">...</div>
.grid {
  display: grid;

  /* ! */
  grid-template-columns: repeat(var(--columns), 1fr);
  grid-gap: calc(var(--gap));
}

Browser tools


Browser developer tools typically display expressions with the calc()same as described in the CSS source code.


Firefox Developer Tools, Rules tab

If you need to find out what the function will calculatecalc(), you can refer to the tabComputed(this tab can be found in the developer tools of all browsers I know). There, the value calculated will be showncalc().


Chrome Developer Tools Computed Tab

Browser support


Here you can find out about calc()browser support for the feature . If we talk about modern browsers, the level of support calc()is more than 97%. If you need to support fairly old browsers (like IE 8 or Firefox 3.6), then they usually do this: add calc()the same property before the property to calculate the value of which is set in a format that old browsers understand:

.el {
  width: 92%; /*   */
  width: calc(100% - 2rem);
}

The function calc()has many known problems, but only old browsers suffer from them. On Caniuse can find a description of 13 such problems. Here are some of them:

  • The Firefox browser below version 59 does not support calc()the functions used to set the color. For example: color: hsl(calc(60 * 2), 100%, 50%).
  • IE 9-11 will not render the shadow specified by the property box-shadowif it is used to determine any of the values calc().
  • Neither IE 9-11 nor Edge support view design width: calc()when applied to table cells.

Life examples


I asked several CSS developers about when they last used the function calc(). Their answers were a small selection that will help everyone to learn about how other programmers use calc()in their daily work.

  • I used calc()to create the helper class for the fields of management: .full-bleed { width: 100vw; margin-left: calc(50% — 50vw); }. I can say that it calc()is in my top 3 most useful CSS features.
  • I used this function to allocate space for a fixed "footer" of the page.
  • calc() . , font-size, . font-sizecalc() line-height. ( , calc() , , , rem em. , ).
  • , . . — : .margin { width: calc( (100vw — var(--content-width)) / 2); }.
  • calc() - , . : .drop-cap { --drop-cap-lines: 3; font-size: calc(1em * var(--drop-cap-lines) * var(--body-line-height)); }.
  • , .
  • , padding vw/vh.
  • I apply calc()to circumvent restrictions background-position. This is especially true for restrictions on the adjustment of color change positions in gradients. For example, this can be described as follows: "position the position of the color change, not reaching 0.75emthe bottom."



Dear readers! Do you use CSS calc()?

All Articles