Кратко
СкопированоКлассная функция, позволяющая производить математические вычисления прямо в CSS. Раньше, до появления calc, приходилось страдать и вычислять размеры примерно.
Пример
СкопированоЧастая ситуация: в вёрстке три колонки, ширина каждой из которых должна быть ровно третью от 100%. 100% / 3 = 33.3333333333...%. Раньше мы допускали неточность и указывали ширину как 33%. Теперь можно использовать calc и пусть браузер сам считает.
При отрисовке страницы браузер сам высчитает и подставит значение:
.selector { width: calc(100% / 3);}
.selector {
width: calc(100% / 3);
}
Как пишется
СкопированоВ круглых скобках мы можем писать любые математические операции с любыми единицами измерения, доступными в вебе (%, px, rem, em, vw, vh, vmin и т.д.). Доступны четыре стандартных операнда:
+— сложение;-— вычитание;/— деление;*— умножение.
Операторы сложения и вычитания обязательно с двух сторон должны отбиваться пробелом. Иначе браузер воспримет их как часть числа. Хоть операторы деления и умножения не требуют такой строгости к себе, но принято и их тоже отбивать пробелами для удобства чтения.
Хорошо 👍
.selector { width: calc(100% - 2rem);}
.selector {
width: calc(100% - 2rem);
}
Плохо 👎
.selector { width: calc(100% -2rem);}
.selector {
width: calc(100% -2rem);
}
Внутри скобок может быть больше одного вычисления, можно группировать операции при помощи скобок. Всё как в настоящем языке программирования 😺 Но не стоит увлекаться: чем короче вычисление, тем проще потом его прочитать и понять что там вообще происходит.
Для каких свойств можно указать calc в качестве значения? Для любых, значением которых должна быть цифра! Причём если свойство предполагает составное значение, то можно указать функцию как часть этого значения:
.selector { margin: calc(5vh / 4) 20px; transition: transform calc(0.5s + 120ms);}
.selector {
margin: calc(5vh / 4) 20px;
transition: transform calc(0.5s + 120ms);
}
Внутри круглых скобок можно складывать только числовые значения. Нельзя сложить число со строкой, хотя в полноценных языках программирования, типа JavaScript, такой трюк с лёгкостью бы удался.
Ещё одно неудачное место для этой функции — медиавыражения. Вот такая запись считается не валидной:
@media (min-width: calc(465px + 1vw)) { ...;}
@media (min-width: calc(465px + 1vw)) {
...;
}
Хотя бы у одной из цифр внутри скобок нужно указать единицу измерения, иначе браузер не сможет понять, от чего же вести вычисление.
Математические константы
СкопированоВнутри calc можно использовать встроенные математические константы:
pi— число π (≈ 3.14159);e— основание натурального логарифма (≈ 2.71828);infinityи-infinity— бесконечность;NaN— не число (Not a Number).
Константы удобны при работе с тригонометрическими функциями CSS — sin, cos, tan:
.element { transform: rotate(calc(pi / 4 * 1rad)); /* 45 градусов */}
.element {
transform: rotate(calc(pi / 4 * 1rad)); /* 45 градусов */
}
CSS-переменные в calc()
Скопированоcalc отлично работает с кастомными свойствами. Можно вынести базовые значения в переменные и считать производные прямо в CSS:
:root { --gap: 20px; --columns: 3;}.item { width: calc((100% - var(--gap) * (var(--columns) - 1)) / var(--columns));}
:root {
--gap: 20px;
--columns: 3;
}
.item {
width: calc((100% - var(--gap) * (var(--columns) - 1)) / var(--columns));
}
Вложенные calc внутри calc работают как обычные скобки — браузер сворачивает их в одно выражение.
Работа с цветовыми каналами
Скопированоcalc можно использовать внутри цветовых функций для динамической корректировки цветовых каналов:
.muted { /* уменьшаем яркость на 10 единиц относительно исходного цвета */ color: lch(from rebeccapurple calc(l - 10) c h);}
.muted {
/* уменьшаем яркость на 10 единиц относительно исходного цвета */
color: lch(from rebeccapurple calc(l - 10) c h);
}
Ограничение: intrinsic-размеры
Скопированоcalc не умеет работать с ключевыми словами вроде auto или fit. Для анимации или вычислений с такими значениями есть специальная функция calc.
/* Не сработает */.element { width: calc(auto - 20px);}
/* Не сработает */
.element {
width: calc(auto - 20px);
}
Как понять
СкопированоВо время отрисовки (рендеринга) страницы браузер заглядывает в CSS и производит все вычисления из функций calc, подставляя на их место итоговое значение. Исходя из этих значений и отрисовываются стили элементов.
Подсказки
Скопировано💡 Очень удобно (и часто приходится) использовать, когда из одной величины в относительных единицах надо вычесть другую величину в абсолютных единицах. Самостоятельно это никак не посчитать, а браузеру раз плюнуть. Например, каким будет результат такого вычисления?
.selector { height: calc(100vh - 34px);}
.selector {
height: calc(100vh - 34px);
}
💡 С помощью calc можно превратить кастомное свойство без единицы измерения в значение с единицей. Это удобно, когда число хранится отдельно, а используется в разных свойствах с разными единицами:
:root { --value: 100;}.element { /* Значение будет 100px */ width: calc(var(--value) * 1px);}
:root {
--value: 100;
}
.element {
/* Значение будет 100px */
width: calc(var(--value) * 1px);
}
- Chrome 26, поддерживается
- Edge 12, поддерживается
- Firefox 16, поддерживается
- Safari 7, поддерживается
На практике
Скопированосоветует
Скопировано🛠 Возьмём тот же пример с тремя колонками из начала статьи. Пусть у каждой из колонок будет внешний отступ по 40 px с каждой из сторон. Если зададим свойства в лоб, то ничего не выйдет, последний блок падает на новую строку.
<div class="parent"> <div class="child"> <h2>Газеты</h2> </div> <div class="child"> <h2>Заводы</h2> </div> <div class="child"> <h2>Пароходы</h2> </div></div>
<div class="parent">
<div class="child">
<h2>Газеты</h2>
</div>
<div class="child">
<h2>Заводы</h2>
</div>
<div class="child">
<h2>Пароходы</h2>
</div>
</div>
.child { display: inline-block; width: 33.3%; margin-left: 40px; margin-right: 40px;}
.child {
display: inline-block;
width: 33.3%;
margin-left: 40px;
margin-right: 40px;
}
Можно использовать флексбокс. Избежим падения, но получим проблему сужения блоков, чтобы уместиться в ряд. Как всего этого избежать? Высчитывать размер блоков с учётом этих боковых отступов!
.child { display: inline-block; width: calc(33.3% - 80px); margin-left: 40px; margin-right: 40px;}
.child {
display: inline-block;
width: calc(33.3% - 80px);
margin-left: 40px;
margin-right: 40px;
}