calc() позволяет делать простые вычисления прямо в значениях CSS. Это удобно, когда нужно «смешать» разные единицы: например, «90% ширины плюс 20 пикселей» или «высота в 3 строки минус шапка».
Синтаксис: как писать, чтобы браузер понял
Базовый вид:
свойство: calc(выражение);
Важные правила:
- Операторы
+,-,*,/обязательно окружаются пробелами:calc(100% - 20px)— правильно,calc(100%-20px)— неправильно. - Единицы измерения нужны у чисел, которые их подразумевают.
10без единицы — это просто число (работает в умножении/делении), а10px— длина. - Вложенные
calc()работают, но почти всегда можно обойтись без них.
Примеры:
width: calc(100% - 40px); height: calc(50vh - 100px); padding: calc(2rem * 1.5); margin-top: calc(100px / 4);
Самые частые кейсы: где это реально полезно
1. «Почти на всю ширину, но с отступом»
Частая задача: блок должен занимать почти всю ширину контейнера, но не касаться краёв.
.card { width: calc(100% - 40px); /* минус по 20px слева и справа */ margin: 0 auto; }
2. Фиксированная колонка + «всё остальное»
Когда нужно сделать макет из фиксированной и резиновой части:
.layout { display: grid; grid-template-columns: 250px calc(100% - 250px); }
Или Flexbox:
.flex-container { display: flex; } .fixed { flex-basis: 250px; } .fluid { flex-basis: calc(100% - 250px); }
3. Высота с вычетом шапки
Если шапка имеет фиксированную высоту, а контент должен занять всё оставшееся место:
body { margin: 0; height: 100vh; display: flex; flex-direction: column; } header { height: 80px; } main { flex: 1; min-height: calc(100vh - 80px); /* страховка, если flex не сработает */ }
4. Смешивание разных единиц
Например, «высота строки в em минус небольшой отступ»:
line-height: calc(1.5em - 2px);
Это бывает полезно для тонкой подгонки типографики.
Частые ошибки: что ломается и почему
Ошибка 1: нет пробелов вокруг операторов
/* Неправильно */ width: calc(100%-20px); /* Правильно */ width: calc(100% - 20px);
Браузер не распознаёт выражение и просто игнорирует это свойство.
Ошибка 2: путаница с процентами и абсолютными единицами
calc(100% - 20px) работает только если у родителя задана ширина (или высота), от которой считаются проценты. Если родитель «не имеет размера», процент становится неопределённым, и результат может быть непредсказуемым.
Пример проблемы:
.parent { /* нет ширины */ } .child { width: calc(100% - 20px); /* может не сработать как ожидается */ }
Решение: убедиться, что у родителя есть размер (ширина, min-width, max-width) или он находится в потоке с понятным размером.
Ошибка 3: деление на ноль
height: calc(100vh / 0); /* недопустимое выражение */
Такое свойство будет проигнорировано.
Ошибка 4: попытка использовать переменные без fallback
CSS-переменные сами по себе допустимы, но если значение переменной невалидно, calc() тоже сломается.
:root { --gutter: 20px; } .box { padding: calc(var(--gutter) * 2); }
Если --gutter не задана или содержит текст, выражение станет невалидным. Лучше сразу давать запасное значение:
padding: calc(var(--gutter, 20px) * 2);
Ошибка 5: умножение/деление без единиц
/* Допустимо (число без единиц можно умножать) */ width: calc(2 * 50px); /* Но нельзя так: */ width: calc(2px * 5px); /* получится px², а нужна длина */
Умножение длины на длину не даст допустимого значения длины.
Ошибка 6: попытка «сложить яблоки и апельсины»
Нельзя складывать разные типы единиц, которые браузер не может привести к одному. Например, проценты и пиксели — можно, потому что браузер знает, как их свести к длине. А вот, скажем, px и deg вместе складывать нельзя.
Практические примеры с пояснением
Пример 1: адаптивная сетка с отступами
Задача: три колонки, между ними отступы, но суммарно на всю ширину.
.grid { display: grid; /* 3 колонки одинаковой ширины, минус отступы */ grid-template-columns: repeat(3, calc((100% - 2 * 20px) / 3)); gap: 20px; }
Пояснение: мы вычитаем два отступа (между тремя колонками их два), делим остаток на три и получаем ширину одной колонки.
Пример 2: «резиновая» карточка с минимальной шириной
.card { width: min(100%, calc(600px - 40px)); /* максимум 600px минус отступы */ padding: 20px; }
Здесь calc() участвует в ограничении ширины. Обратите внимание: min() — это отдельная функция, но она отлично дружит с calc().
Пример 3: динамические отступы через переменные
: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 — это отступ между колонками |
