calc(): математика прямо в CSS — без магии, зато с примерами (CSS)

calc() позволяет делать простые вычисления прямо в значениях CSS. Это удобно, когда нужно «смешать» разные единицы: например, «90% ширины плюс 20 пикселей» или «высота в 3 строки минус шапка».


Синтаксис: как писать, чтобы браузер понял

Базовый вид:

css
свойство: calc(выражение);

Важные правила:

  • Операторы +, -, *, / обязательно окружаются пробелами: calc(100% - 20px) — правильно, calc(100%-20px) — неправильно.
  • Единицы измерения нужны у чисел, которые их подразумевают. 10 без единицы — это просто число (работает в умножении/делении), а 10px — длина.
  • Вложенные calc() работают, но почти всегда можно обойтись без них.

Примеры:

css
width: calc(100% - 40px);
height: calc(50vh - 100px);
padding: calc(2rem * 1.5);
margin-top: calc(100px / 4);

Самые частые кейсы: где это реально полезно

1. «Почти на всю ширину, но с отступом»

Частая задача: блок должен занимать почти всю ширину контейнера, но не касаться краёв.

css
.card {
  width: calc(100% - 40px); /* минус по 20px слева и справа */
  margin: 0 auto;
}

2. Фиксированная колонка + «всё остальное»

Когда нужно сделать макет из фиксированной и резиновой части:

css
.layout {
  display: grid;
  grid-template-columns: 250px calc(100% - 250px);
}

Или Flexbox:

css
.flex-container {
  display: flex;
}
.fixed {
  flex-basis: 250px;
}
.fluid {
  flex-basis: calc(100% - 250px);
}

3. Высота с вычетом шапки

Если шапка имеет фиксированную высоту, а контент должен занять всё оставшееся место:

css
body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
}
header {
  height: 80px;
}
main {
  flex: 1;
  min-height: calc(100vh - 80px); /* страховка, если flex не сработает */
}

4. Смешивание разных единиц

Например, «высота строки в em минус небольшой отступ»:

css
line-height: calc(1.5em - 2px);

Это бывает полезно для тонкой подгонки типографики.


Частые ошибки: что ломается и почему

Ошибка 1: нет пробелов вокруг операторов

css
/* Неправильно */
width: calc(100%-20px);
 
/* Правильно */
width: calc(100% - 20px);

Браузер не распознаёт выражение и просто игнорирует это свойство.

Ошибка 2: путаница с процентами и абсолютными единицами

calc(100% - 20px) работает только если у родителя задана ширина (или высота), от которой считаются проценты. Если родитель «не имеет размера», процент становится неопределённым, и результат может быть непредсказуемым.

Пример проблемы:

css
.parent { /* нет ширины */ }
.child {
  width: calc(100% - 20px); /* может не сработать как ожидается */
}

Решение: убедиться, что у родителя есть размер (ширина, min-width, max-width) или он находится в потоке с понятным размером.

Ошибка 3: деление на ноль

css
height: calc(100vh / 0); /* недопустимое выражение */

Такое свойство будет проигнорировано.

Ошибка 4: попытка использовать переменные без fallback

CSS-переменные сами по себе допустимы, но если значение переменной невалидно, calc() тоже сломается.

css
:root {
  --gutter: 20px;
}
 
.box {
  padding: calc(var(--gutter) * 2);
}

Если --gutter не задана или содержит текст, выражение станет невалидным. Лучше сразу давать запасное значение:

css
padding: calc(var(--gutter, 20px) * 2);

Ошибка 5: умножение/деление без единиц

css
/* Допустимо (число без единиц можно умножать) */
width: calc(2 * 50px);
 
/* Но нельзя так: */
width: calc(2px * 5px); /* получится px², а нужна длина */

Умножение длины на длину не даст допустимого значения длины.

Ошибка 6: попытка «сложить яблоки и апельсины»

Нельзя складывать разные типы единиц, которые браузер не может привести к одному. Например, проценты и пиксели — можно, потому что браузер знает, как их свести к длине. А вот, скажем, px и deg вместе складывать нельзя.


Практические примеры с пояснением

Пример 1: адаптивная сетка с отступами

Задача: три колонки, между ними отступы, но суммарно на всю ширину.

css
.grid {
  display: grid;
  /* 3 колонки одинаковой ширины, минус отступы */
  grid-template-columns: repeat(3, calc((100% - 2 * 20px) / 3));
  gap: 20px;
}

Пояснение: мы вычитаем два отступа (между тремя колонками их два), делим остаток на три и получаем ширину одной колонки.

Пример 2: «резиновая» карточка с минимальной шириной

css
.card {
  width: min(100%, calc(600px - 40px)); /* максимум 600px минус отступы */
  padding: 20px;
}

Здесь calc() участвует в ограничении ширины. Обратите внимание: min() — это отдельная функция, но она отлично дружит с calc().

Пример 3: динамические отступы через переменные

css
:root {
  --base: 8px;
  --level: 2;
}
 
.item {
  margin-bottom: calc(var(--base) * var(--level)); /* 16px */
}

Такой подход делает отступы масштабируемыми и понятными.


Нюансы совместимости и производительности

  • Поддержка calc() в современных браузерах отличная. Старые версии IE не поддерживают, но для актуальных проектов это обычно не проблема.
  • Вычисления в calc() происходят на этапе отрисовки и не считаются «тяжёлыми» для браузера.
  • Не стоит использовать calc() там, где можно обойтись простыми значениями: это усложняет чтение кода.

Мини-шпаргалка

Что хотите сделать Как записать Важное условие
Почти на всю ширину минус отступ width: calc(100% - 40px); У родителя должна быть ширина
Фиксированная и резиновая часть grid-template-columns: 200px calc(100% - 200px); Следить за порядком и пробелами
Высота минус шапка min-height: calc(100vh - 80px); Часто используют как страховку
Масштабируемые отступы margin: calc(var(--unit) * 3); Переменная должна быть валидной длиной
Сетка с равными колонками и отступами repeat(4, calc((100% - 3 * gap) / 4)) gap — это отступ между колонками

 

Добавить комментарий