In this article, we’ll look at 4 of the CSS math functions that are already massively supported by browsers:

  • calc()
  • min()
  • max()
  • clamp()

Why is it massively supported by browsers? Because in addition to these 4 math functions that are already massively supported,already defines math functions such as sin(),cos(),tan() for trigonometric functions, pow(), sqrt () for exponential functions, etc., but they are all in the lab stage and no browsers support them yet. There are no browsers that support them yet, so we need to give it some time.


calc() This CSS function allows to perform some calculations when declaring the value of a CSS property.

The syntax is similar to

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

Some points to note:

  • The + and - operators must have whitespace characters on both sides. For example, calc(50% -8px) will be parsed as an invalid expression and must be written as calc(8px + -50%)
  • The operators * and / do not require whitespace before and after the operator, but whitespace is still recommended for uniformity reasons.
  • Using 0 as a divisor causes the HTML parser to throw an exception.
  • Mathematical expressions involving percentages of width and height for table columns, table column groups, table rows, table row groups, and table cells in auto- and fixed-layout tables, auto can be considered as specified.
  • The calc() function supports nesting, but supports it in such a way that the nested calc() function is all treated as ordinary parentheses. (So, just use parentheses inside the function.)
  • calc() supports mixing with CSS variables.

Looking at one of the most common examples, the page structure is as follows:

<div class="g-container"> <div class="g-content">Content</div> <div class="g-footer">Footer</div> </div> </div<div class="g-footer">Footer</div

The g-footer of the page is 80px high, and we want the g-content section to fill the remaining space no matter how long the page is, like this:

This layout can be easily achieved using flex’s elastic layout or, of course, calc():

.g-container { height: 100vh;}.g-content { height: calc ( 100vh - 80px );}.g-footer { height: 80px;}

Here’s a list of advanced Calc() techniques.

Differences between addition, subtraction and multiplication in Calc

Note the difference between addition and subtraction in calc() and multiplication and division:

{ font-size: calc ( 1rem + 10px ); width: calc ( 100px + 10% );}

As you can see, addition and subtraction require units on both sides of the operand, while multiplication and division, which require a unitless number, simply represent a multiplier:

{ width: calc ( 100% / 7 ); animation-delay: calc ( 1s * 3 );}

Nesting of Calc

The calc() function can be nested, like this:

{ width: calc ( 100vw - calc ( 100% - 64px ));}

At this point, the internal calc() function can be written degenerately as just a parenthesis (), so the above code is equivalent:

{ width: calc ( 100vw - ( 100% - 64px ));}

That is, calc() within the nest, calc several function characters can be o mitted.

Mixed operations with different units in Calc

calc() supports mixing of different units, for length, as long as the units belonging to the length-related units can be mixed, including these:

  • px
  • %
  • em
  • rem
  • in
  • mm
  • cm
  • pt
  • pc
  • ex
  • ch
  • vh
  • vw
  • vmin
  • vmax

An interesting point here is that arithmetic is definitely a performance hog, and in the early days, there was this piece of CSS code that could just crash Chrome:


The CSS styles are as follows:

div { --initial-level-0: calc ( 1vh + 1% + 1px + 1em + 1vw + 1cm ); --level-1: calc ( var ( --initial-level-0 ) + var ( --initial-level-0 )); --level-2: calc (var ( --level-1 ) + var ( --level-1 )); --level-3: calc ( var ( --level-2 ) + var ( --level-2 )); --level-4: calc ( var ( --level-3 ) + var ( --level-3 )); --level --level-5: calc ( var ( --level-4 ) + var ( --level-4 ) );  --level-6: calc ( var ( --level-5 ) + var ( --level-5 )); --level-7: calc ( var ( --level-6 ) + var ( --level-6 )); --level-8: calc(  var( --level-6 ) + var ( --level-6 ));
  --level-8: calc ( var ( --level-7 ) + var ( --level-7 ));
  --level-9: calc ( var ( --level-8 ) + var ( --level-8 ));
  --level-10: calc ( var ( --level-9 ) + var (--level-9 ));
  --level-11: calc ( var ( --level-10 ) + var ( --level-10 ));
  --level-12: calc ( var ( --level-11 ) + var ( --level-11 ));
  --level-13: calc ( var (--level-12 ) + var ( --level-12 ));
  --level-14: calc ( var ( --level-13 ) + var ( --level-13 ));
  --level-15: calc ( var ( --level-14 ) + var ( --level-14 ));
  --  level-16: calc ( var ( --level-15 ) + var ( --level-15 )); --level-17: calc ( var ( --level-16 ) + var ( --level-16 )); --level-18: calc ( var ( --level-17 )  + var ( --level-17 )) ; --level-19: calc ( var ( --level-18 ) + var ( --level-18 )); --level-20: calc ( var ( --level-19 ) + var ( --level-19 )); --level-21: calc ( var ( --level-20 ) + var ( --level-20 ) ) ; --level-22: calc ( var ( --level-21 ) + var ( --level-21 )); --level-23: calc ( var ( --level-22 ) + var ( --level-22 )); --level-24: calc( var ( --level-22 ) + var ( --level-22 ));  --level-23: calc( var ( --level-22 ) + var ( --level-22 )) ); --level-24: calc ( var ( --level-23 ) + var ( --level-23 )) ; --level-25: calc ( var ( --level-24 ) + var ( --level-24 )); --level-26: calc ( var ( --level-25 ) + var ( --level-25 )); --level-27: calc ( var ( --level-26 ) + var ( --level-26 )); --level-28: calc ( var ( --level-27 ) + var ( --level-27 )); --level-29. calc ( var ( --level-28 ) + var ( --level-28 )); --level-30: calc ( var ( --level-29 ) + var ( --level-29 )); --level-final: calc ( var ( --level-30 ) + 1px ); border-width: var (--level-final);  border-style: solid;}

As you can see, from --level-1 to --level-30, the amount of arithmetic grows exponentially each time, culminating in the --level-final variable, which unfolds will have 2^30 = 1073741824 contents of the --initial-level-0 expression.

And, the content of each --initial-level-0 expression — calc(1vh + 1% + 1px + 1em + 1vw + 1cm) — is also complex enough for the browser to parse.

Mixed together, this results in a BOOM in the browser (Chrome versions prior to 70). To see the effect, we assign the above styles to an element when it is hovered, and we get the following effect:

Of course, this bug has now been fixed, and we can also learn from this little DEMO that calc can be used to mix different units, and that it can lead to a big performance hit if it’s used with a huge amount of calculations.

Of course, don’t mix length units with non-length units, like this:

{ animation-delay: calc ( 1s + 1px );}

Calc with CSS Custom Variables

An important feature of the calc() function is that it can be used with CSS customizations and CSS @property variables.

One of the simplest DEMOs:

:root { --width: 10px;}div { width: calc ( var (--width));}

Of course, it doesn’t seem to make much sense to write it this way. In practice, the application scenario will be slightly more complex than the above DEMO.

Let’s say we want to implement a loading animation that starts with only 3 balls:

A possible way to write this would be to add the same rotation animation to all 3 balls, and then control their animation-delay separately:

<div class="g-container"> <div class="g-item" ></div> <div class="g-item" ></div> <div class="g-item">& lt;/div> </div>
.item:nth-child ( 1 ) { animation: rotate 3s infinite linear;}.item:nth-child ( 2 ) { animation: rotate 3s infinite -1s linear;}.item:nth-child ( 3 ) { animation: rotate  3s infinite -2s linear;}

If the animation ever needs to be expanded to 5 balls, it looks like this:

We then have to add HTML and modify CSS at the same time, a scenario that can be simplified a bit with the help of Calc and CSS variables.

Suppose there are only 3 balls:

<div class="g-container"> <div class="g-item" style="--delay: 0" ></div> <div class="g-item" style="--delay: 1"><</div> <div class="g-item" style="--delay: 2"></div> </div>

We pass in the --delay variables via the Style tag in HTML and use them directly in CSS:

.g-item { animation: rotate 3s infinite linear; animation-delay: calc ( var (--delay) * -1s );}@keyframes rotate { to { transform: rotate ( 360deg ); }}

And when the animation is changed to 5 balls, we don’t need to change the CSS, we just change the HTML, like this:

<div class="g-container"><div class="g-item" style=" --delay: 0"></div> <div class="g-item " style=" --delay: 0.6">& lt;/div> <div class="g-item " style=" --delay: 1.2"></div> <div class="g-item " style=" --delay: 1.8"></div> <div class="g-item " style=" --delay: 2.4"></div> </div> </div>

The core CSS is still the same, and doesn’t need to be modified in any way:

{ animation-delay: calc ( var (--delay) * -1s );}

For the full DEMO, you can poke here:

calc Default value when using custom variables

Still with the Loading animation above, what happens if one of my HTML tags forgets to fill in the --delay value?

Like this:

<div class="g-container"> <div class="g-item" style="--delay: 0" ></div> <div class="g-item" style="--delay: 0.6">& lt; /div> <div class="g-item" ></div> <div class="g-item" style="--delay: 1.8" ></div> <div class="g-item" style="--delay: 2  .8" ></div> <div class="g-item" style = "--delay: 2.4"></div></ div> </div>
{ animation-delay: calc ( var (--delay) * -1s );}

Since the HTML tag doesn’t pass in a value for --delay, and the CSS lookup didn’t find a value for it, the line animation-delay: calc(var(--delay) * -1s) is invalid, and is equivalent to animation-delay: 0, which has the effect of missing the ball:

So, in this case, the fallback mechanism of the CSS custom variable var() can be utilized:

{ // The 1 in ( --delay, 1 ) is a fault tolerance mechanism animation-delay: calc ( var (--delay, 1 ) * -1s );}

At this point, if no --delay value is read, the default 1 with -1s is used for arithmetic.

Calc String Splicing

Many people try string splicing when using CSS, like this:

<div style="--url: 'bsBD1I.png'" ></div>
:root { --urlA: 'url('; --urlB: ')';}div { width: 400px; height: 400px; background-image: calc ( var (--urlA) + var ( --url) + var (--urlB));}

Here you want to use calc(var(--urlA) + var(--url) + var(--urlB)) to spell out the complete URL url ( available in the background-image.

However, this is not allowed (and cannot be implemented). calc has no string splicing capability.

The only possible way to do string splicing is in the content attribute of an element’s pseudo-element. But it’s not like you’re utilizing calc.

It’s wrong to look at an example like this:

:root { --stringA: '123'; --stringB: '456'; --stringC: '789';}div::before { content: calc ( var (--stringA) + var (--stringB) + var (--stringC));}

In this case, you don’t need calc, you can just use the custom variable to add up.

Hence, the correct way to write it:

:root { --stringA: '123'; --stringB: '456'; --stringC: '789';}div::before { content: var (--stringA) + var (--stringB) + var (--stringC);}

At this point, the content can be displayed normally:

Again, calc has no string splicing capability, and the following usage is unrecognizable as incorrect syntax:

.el::before { // String splicing is not supported content: calc ( "My " + "counter" );}.el::before { // String multiplication is even less supported content: calc ( "String Repeat 3 times" * 3 );}

min(), max(), clamp()

min(), max(), and clamp() fit together. Their roles are related to each other.

  • max(): selects the largest (positive direction) value from a comma-separated list of expressions as the value of the attribute
  • min(): selects the smallest value from a comma-separated list of expressions as the value of the attribute
  • clamp(): limits a value to an upper and lower limit, and selects a value to use between the minimum and maximum values when the value exceeds the range of the minimum and maximum values

Since in reality, there are very many elements whose properties are not static, but change according to the context and environment.

Like this layout:

<div class="container" ></div>
.container { height: 100px; background: #000;}

The effect is as follows, the .container block it will grow as the screen grows, always occupying the entire screen:

For a responsive project, we certainly do not want its width will always get bigger, but when a certain threshold is reached, the width from a relative unit to an absolute unit, this situation applies to min(), simple transformation of the code:

.container { width: min ( 100%, 500px ); height: 100px; background: #000;}

The container’s width value will be chosen between width: 100% and width: 500px, with the relatively small one selected.

If the screen width is less than 500px, it is also shown as width: 100%, and vice versa, as width: 500px:

Similarly, in a similar scenario, we can use max() to select a relatively large value from a number of values.

min(), max() support for lists of multiple values

min(), max() supports lists of multiple values, such as width: max(1px, 2px, 3px, 50px).

Of course, for the above expression:

width: max(1px, 2px, 3px, 50px) is actually equal to width: 50px. Therefore, for the specific use of min(), max(), it should contain at most one specific absolute unit. Otherwise, such code like the above, although syntactically supported, but in any case, the calculated value is determined, in fact, does not make sense.

Match calc

min(), max(), and clamp() can all be used with calc.

For example:

div { width: max ( 50vw, calc ( 300px + 10% ));}

In this case, the calc and the corresponding wrapped parentheses can be omitted, so the above code can again be written as:

div { width: max ( 50vw, 300px + 10% );}

Simulate clamp based on max, min

Now, there is this scenario, what if, again, we need to limit the maximum value and also the minimum value?

Like this scenario, ** the size of the font, the minimum is 12px, as the screen gets bigger, gradually larger, but in order to avoid the old man phenomenon (as the screen gets bigger, unlimited size), we also need to limit the maximum value of 20px.

We can use vw to realize the dynamic value assigned to the font, assuming that in the mobile terminal, the width of the device width of the CSS pixels for 320px, the page font width of the minimum 12px, converted into vw is 320 / 100 = 3.2, that is, 1vw in the screen width of 320px when the performance of the 3.2px, 12px is equal to about 3.75 vw.

At the same time, we need to limit the maximum font value to 20px, the corresponding CSS is as follows:

p { font-size: max ( 12px, min ( 3.75vw, 20px ));}

Look at the effect:

By using max(),min() and a relative unit vw, we have managed to set an upper and lower limit for the font, and a dynamic change between this upper and lower limit.

Of course, the core paragraph max(12px, min(3.75vw, 20px)) looks a bit convoluted, so CSS introduced clamp() to simplify this syntax, and the following two ways to write it are equivalent:

p { font-size: max ( 12px, min ( 3.75vw, 20px )); // equivalent font-size: clamp ( 12px, 3.75vw, 20px );}


The clamp() function works by limiting a value to an upper and lower limit, and choosing a value to use between the minimum and maximum values when that value exceeds the range of the minimum and maximum values. It receives three arguments: the minimum value, the preferred value, and the maximum value.

Interestingly, clamp(MIN, VAL, MAX) actually means max(MIN, min(VAL, MAX)).

Using vw with clamp for responsive layout

Let’s continue from above.

In the near future, more rem adaptation schemes will be used for mobile, probably with the help of existing libraries like flexible.js, hotcss.js, etc. One of the big problems with rem schemes is that they require a piece of JavaScript to reset the font-size of the root element in response to changes in the viewport, and using rem is more or less like a hack. is a bit of a hack.

Nowadays, in mobile adapting, we prefer vw pure CSS scheme, similar to rem scheme, it is also the essence of the page is proportional scaling. One of the problems with vw is that as the screen gets bigger and smaller, the content elements will keep getting bigger and smaller, which results in many elements looking too big on a large screen!

Therefore, we need a way to be able to control the maximum and minimum thresholds, like this:

At this point, clamp can be very good use, or our example above, this code font-size: clamp(12px, 3.75vw, 20px), you can limit the font in the range of 12px - 20px.

Therefore, for mobile pages, we can use vw to set all units related to length. For things like fonts, inner and outer margins, widths, etc., which should not be fully scaled, clamp() is used to control the maximum and minimum thresholds.

To summarize, for mobile pages, we can use vw with clamp() to complete the adaptation of the entire mobile layout. The advantage is:

  • No additional JavaScript code is introduced, pure CSS solution.
  • Able to control the boundary threshold well, reasonable scaling display

inverse response change

Another trick, using clamp() with negative values, we can also reverse the operation to get a kind of reverse responsive effect where the larger the screen, the smaller the font:

p { font-size: clamp ( 20px, -5vw + 96px, 60px );}

Look at the effect:

This trick is quite interesting, as the calculated value of -5vw + 96px increases as the screen gets smaller, implementing a sort of reverse font responsive change.


To summarize, the reasonable use of min(), max(), clamp(), is the focus of building a modern responsive layout, we can say goodbye to the traditional need for JavaScript to assist some of the program, based on the CSS of these mathematical functions can be accomplished to meet all the requirements.


By lzz

Leave a Reply

Your email address will not be published. Required fields are marked *