Функции CSS: calc, clamp, min, max
Урок про математику в CSS: складываем единицы через calc, делаем адаптивную типографику на clamp и ставим границы значениям через min/max.
Функции вычислений CSS (
calc(),clamp(),min(),max()) считают значения прямо в браузере, позволяя смешивать несовместимые на первый взгляд единицы — проценты, пиксели,remиvw— в одном выражении.
Зачем это на практике
Вёрстка постоянно упирается в вопрос «сколько именно». Ширина блока — это «всё доступное место минус 32 пикселя отступа». Размер шрифта должен расти на широких экранах, но не бесконтрольно. Кнопка не должна становиться уже 120 пикселей и шире 320. Раньше такие задачи решались каскадом медиа-запросов и магическими числами. Функции вычислений делают условия частью самого значения — меньше брейкпоинтов, меньше дублирования, плавная адаптация вместо ступенек.
calc(): смешиваем единицы
Главный талант calc() — складывать величины разных систем, которые иначе несовместимы. Классика — «ширина 100% минус фиксированный отступ».
.content {
/* вся ширина родителя минус сайдбар 240px */
width: calc(100% - 240px);
}
.full-bleed {
/* на всю ширину окна, даже из узкого контейнера */
width: 100vw;
margin-left: calc(50% - 50vw);
}
Внутри calc() работают +, -, *, /. Важная тонкость: вокруг + и - обязательны пробелы — calc(100%-240px) не сработает, потому что -240px читается как отрицательное число. Умножать и делить можно только на безразмерное число: calc(100% / 3) — да, calc(100% / 20px) — нет.
Особенно мощно calc() сочетается с переменными: получается «формула токенов».
:root { --space: 8px; }
.card { padding: calc(var(--space) * 2); } /* 16px */
.card + .card { margin-top: calc(var(--space) * 3); } /* 24px */
Так из одного базового модуля строится вся ритмика отступов: меняете --space — и пропорции сетки пересчитываются разом. Ещё calc() удобен для смещений в позиционировании (top: calc(50% - 20px), чтобы центрировать элемент известной высоты) и для сеток, где из доступной ширины нужно вычесть суммарные промежутки между колонками.
clamp(): адаптивная типографика без медиа-запросов
clamp(MIN, PREFERRED, MAX) возвращает предпочтительное значение, но зажимает его между минимумом и максимумом. Это идеальный инструмент для «текучего» (fluid) размера шрифта: он плавно растёт вместе с шириной окна и останавливается на границах.
h1 {
/* не меньше 28px, не больше 56px,
а между ними растёт вместе с шириной экрана */
font-size: clamp(28px, 5vw, 56px);
}
.container {
width: clamp(320px, 90%, 1200px);
margin-inline: auto;
}
Здесь 5vw — это 5% ширины окна. На телефоне шириной 360px это 18px, поэтому включается нижняя граница 28px. На мониторе 1920px это 96px — срабатывает верхняя граница 56px. Между ними заголовок плавно масштабируется. Один clamp() заменяет три-четыре медиа-запроса.
Чтобы шрифт реагировал ещё и на масштаб страницы (доступность), среднее значение обычно комбинируют: clamp(1rem, 0.5rem + 2vw, 2rem) — добавка в rem гарантирует рост при увеличении базового шрифта пользователем.
min() и max(): границы значениям
min() возвращает наименьший из аргументов, max() — наибольший. Звучит наоборот, чем кажется, поэтому держите в голове правило: min() ставит верхний потолок, max() — нижний пол.
.card {
/* ширина 100%, но не больше 600px */
width: min(100%, 600px);
}
.gutter {
/* отступ 5% ширины, но не меньше 16px */
padding-inline: max(16px, 5%);
}
min(100%, 600px) — это компактная замена связке width: 100%; max-width: 600px;. Аргументов может быть сколько угодно, и их можно вкладывать друг в друга и в calc().
Реальные примеры вместе
Функции комбинируются — именно так пишут современные адаптивные компоненты.
:root { --gap: clamp(12px, 2vw, 32px); }
.grid {
display: grid;
gap: var(--gap);
/* колонки минимум 220px, заполняют ряд */
grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
}
.hero-title {
font-size: clamp(2rem, 1rem + 4vw, 4rem);
line-height: 1.1;
max-width: min(90%, 60ch);
}
Вложенный min(220px, 100%) внутри minmax — известный приём: он не даёт колонке стать шире самого контейнера на очень узких экранах, где иначе появилась бы горизонтальная прокрутка.
Как это работает под капотом
Все четыре функции — это math-функции: браузер строит дерево выражения и вычисляет его на этапе вычисления стилей, уже зная контекст (ширину родителя, размер окна, базовый шрифт). Проценты и vw резолвятся в пиксели, затем выполняется арифметика. clamp(a, b, c) по спецификации эквивалентен max(a, min(b, c)) — то есть сначала ограничивается сверху, потом снизу. Поскольку всё считается во время работы страницы, при изменении размера окна или зуме значения пересчитываются автоматически — отсюда плавность без JS.
Частые ошибки
- Нет пробелов вокруг + и -.
calc(100%-10px)невалидно. Пишитеcalc(100% - 10px). - Путать min() и max(). Верхний предел задаёт
min(), нижний —max(). Если блок «не растягивается до нужного», часто перепутаны именно они. - MIN больше MAX в clamp. Если в
clamp(MIN, P, MAX)минимум окажется больше максимума, побеждает минимум — получается неожиданно крупный текст. - Делить на величину с единицей.
calc(100% / 16px)не сработает: делитель и множитель обязаны быть безразмерными. - Только vw в шрифте.
font-size: 5vwбезrem-добавки игнорирует пользовательский зум — это проблема доступности.
Итоги
calc()складывает разные единицы; вокруг+и-нужны пробелы, делитель — безразмерный.clamp(MIN, PREFERRED, MAX)даёт текучую типографику и размеры без медиа-запросов.min()— верхний потолок,max()— нижний пол значения.min(100%, 600px)заменяет паруwidth+max-width.- Функции свободно вкладываются друг в друга и в переменные — основа адаптивных компонентов.