$ sudo teach IT

Медиазапросы меняют макет ступенями: до порога — так, после — иначе. Но многое хочется менять плавно: чтобы заголовок подрастал вместе с шириной экрана, а контейнер не превышал разумной ширины на огромном мониторе — без единого breakpoint. Для этого в CSS есть гибкие единицы (%, vw, vh) и функции (clamp(), min(), max()), которые вычисляют размер на лету. Они делают вёрстку по-настоящему текучей и часто позволяют обойтись меньшим числом медиазапросов.

После этого урока вы сможете задавать размеры, которые плавно подстраиваются под экран, и ограничивать их разумными рамками — заголовки, отступы и контейнеры станут отзывчивыми сами по себе.

Что уже умеем / что добавим

Вы адаптируете макет медиазапросами и контейнерными запросами — ступенчато. Теперь добавим плавную адаптацию через гибкие единицы и функции. В сквозном проекте мы сделаем главный заголовок и контейнер страницы текучими: заголовок будет плавно расти, а контейнер — тянуться, но не шире разумного предела.

Что вы узнаете в рамках статьи

  • Как работают относительные единицы %, vw и vh.
  • Что делают функции min() и max().
  • Как задавать «размер в рамках» функцией clamp().
  • Как плавно масштабировать текст и контейнеры без медиазапросов.

Ключевые термины

  • % — размер относительно родителя.
  • vw / vh — 1% ширины / высоты окна (viewport width/height).
  • min(a, b) — выбирает меньшее из значений.
  • max(a, b) — выбирает большее из значений.
  • clamp(min, идеал, max) — значение в рамках: не меньше min, не больше max.

Относительные единицы: %, vw, vh

Проценты задают размер относительно родителя: width: 50% — половина ширины родительского блока. Это давно знакомо. Единицы vw и vh считают от окна: 1vw — это один процент ширины окна, 1vh — один процент его высоты.

.hero {
  width: 100vw;
  height: 50vh;
}

Такой блок займёт всю ширину окна и половину его высоты — и будет пересчитываться при изменении размера окна. vw/vh удобны для крупных блоков «в экран»: обложек, экранов-заставок. Проценты же чаще применяют внутри контейнеров. Обе единицы текучие: размер меняется плавно вместе с окном или родителем, без ступеней.

Функции min() и max()

Функции min() и max() выбирают из нескольких значений одно — и это мощный инструмент ограничения. Несмотря на названия, работают они чуть непривычно, поэтому разберём внимательно.

min(a, b) берёт меньшее из значений. Типичный приём — ограничить ширину сверху:

.container {
  width: min(90%, 1200px);
}

Здесь ширина будет равна 90%, пока эти 90% меньше 1200px (на небольших экранах), но как только 90% превысят 1200px (на большом мониторе), возьмётся 1200px. Итог: контейнер тянется вместе с экраном, но не шире 1200px. max(a, b) работает наоборот — берёт большее, что удобно для ограничения снизу, например max(300px, 50%) не даст блоку стать уже 300px.

Функция clamp()

clamp() объединяет обе идеи и задаёт значение в рамках. Она принимает три аргумента: минимум, идеальное (обычно текучее) значение и максимум:

.title {
  font-size: clamp(1.5rem, 4vw, 3rem);
}

Читается так: размер шрифта стремится к 4vw (плавно растёт с шириной окна), но никогда не станет меньше 1.5rem и больше 3rem. На узком экране заголовок не съёжится ниже полутора рем, на широком — не раздуется больше трёх, а между ними — плавно подстраивается. Это чрезвычайно удобно для «отзывчивой типографики»: один clamp() заменяет несколько медиазапросов для размера шрифта. Тот же приём применим к отступам, ширине, любым размерам, где нужна плавность в разумных границах.

Плавная адаптация без медиазапросов

Гибкие функции позволяют решать целые задачи адаптивности вообще без @media. Заголовок на clamp() масштабируется сам. Контейнер на min() сам держит максимальную ширину. Отступы можно сделать текучими:

.section {
  padding: clamp(1rem, 5vw, 4rem);
}

Отступы секции плавно растут от 1rem на телефоне до 4rem на большом экране, не требуя ни одной контрольной точки. Это не значит, что медиазапросы больше не нужны — для смены самого макета (число колонок, показ/скрытие блоков) они по-прежнему главные. Но для плавного масштабирования размеров гибкие функции чище и короче: меньше кода, естественнее поведение. Профессиональная вёрстка сочетает оба подхода: ступенчатые перестройки — медиазапросами, плавное масштабирование — гибкими функциями.

Применяем в проекте

Сделаем главный заголовок и контейнер страницы текучими. Заголовок будет плавно расти с шириной экрана в разумных пределах, а контейнер — тянуться, но не шире 1200px и с текучими отступами. Всё это без медиазапросов.

.container {
  width: min(90%, 1200px);
  margin: 0 auto;
  padding: clamp(1rem, 4vw, 3rem);
}

.hero__title {
  font-size: clamp(1.75rem, 5vw, 3.5rem);
}

Плавно меняйте ширину окна: заголовок подрастает и уменьшается сам, оставаясь читаемым и не гигантским; контейнер расширяется вместе с экраном, но на большом мониторе замирает на 1200px и остаётся по центру, а его внутренние отступы мягко меняются. Мы добавили ощутимую адаптивность, не написав ни одного медиазапроса. Текучесть — признак современной, аккуратной вёрстки.

Частые ошибки

  • Путаница min() и max().
    Для ограничения ширины сверху нужен min() (берёт меньшее), для нижней границы — max(); названия сбивают с толку, проверяйте логику.
  • 100vw и горизонтальная прокрутка.
    100vw не учитывает полосу прокрутки и может дать лишний горизонтальный скролл; для ширины по родителю чаще безопаснее 100%.
  • clamp() без разумных границ.
    Слишком широкий диапазон делает текст то крошечным, то огромным; подбирайте минимум и максимум осознанно.
  • Отказ от медиазапросов совсем.
    Гибкие функции масштабируют размеры, но не перестраивают макет; смену числа колонок оставляйте за @media.

Итоги

Прекрасная работа — ваша вёрстка стала текучей! Вы освоили относительные единицы %, vw и vh, разобрались с функциями min() и max() для ограничения размеров и научились задавать значение в рамках через clamp(). Теперь заголовок и контейнер вашего сайта плавно подстраиваются под экран без единого медиазапроса — это признак зрелой, современной вёрстки.

Отличный прогресс! Впереди — заключительный урок курса: мы займёмся адаптивными изображениями и авто-сетками, научимся вписывать картинки через object-fit и aspect-ratio и строить сетки, которые сами меняют число колонок с auto-fit и auto-fill.

На следующий урок