Единицы измерения. Часть 2: единицы окна и вычисления

Чтение — около 7 минут· без учёта практики

В первой части мы разобрали размеры относительно шрифта и настроили текст сайта Тони. Теперь свяжем размеры с окном браузера: например, сделаем заголовок крупнее на широком экране и меньше на узком, сохранив удобные границы. Практику выполним на отдельной странице; файлы my-site в этом уроке менять не нужно.

Единицы относительно окна браузера

Отдельная группа единиц задаёт размеры относительно области просмотра (англ. viewport) — части окна браузера, в которой показывается страница, без панелей самого браузера.

  • 1vw (англ. viewport width — ширина области просмотра) = 1% ширины области просмотра. Если ширина области просмотра 1000px, то 1vw = 10px. При изменении ширины окна значение пересчитывается.

  • 1vh (англ. viewport height — высота области просмотра) = 1% высоты области просмотра. Если высота области просмотра 500px, то 1vh = 5px.

Единицы vw и vh: попробуйте сами

Размер шрифта и высота блоков заданы в vw и vh. Измените ширину окна браузера или перетащите разделитель между кодом и результатом: размер текста в vw изменится вместе с шириной области результата. Значения в vh зависят от её высоты. Область просмотра здесь — окно результата, а не всё окно браузера.

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <title>Единицы vw и vh</title>
    <style>
      body {
        margin: 0; /* без внешнего отступа блок шириной 100% занимает всё окно */
      }
      .banner {
        font-size: 5vw; /* 5% ширины окна */
        height: 30vh; /* 30% высоты окна */
        background-color: orange;
      }
      .half {
        font-size: 3vw;
        width: 50vw; /* половина ширины окна */
        height: 20vh;
        background-color: gold;
      }
    </style>
  </head>
  <body>
    <div class="banner">Гуляем с Тони! 5vw, высота 30vh</div>
    <div class="half">3vw, ширина 50vw, высота 20vh</div>
  </body>
</html>

Первому блоку заданы размер шрифта 5vw (5% ширины области просмотра) и высота 30vh (30% её высоты). Второму — размер шрифта 3vw, ширина 50vw и высота 20vh. При изменении размеров окна браузер пересчитывает значения «на лету».

На телефоне высота области просмотра меняется: при прокрутке адресная строка браузера то появляется, то прячется. Единица vh считается от высоты с убранной панелью, поэтому блок высотой 100vh при видимой панели частично уходит за край экрана. Для таких случаев есть единицы svh (малая область просмотра, панель видна), lvh (большая, панель убрана) и dvh (текущая высота). Для блока, который должен помещаться при видимых панелях, подходит min-height: 100svh. Значение min-height: 100dvh следует за доступной высотой, но может менять размер блока при прокрутке. Минимальная высота позволяет блоку расти, если текста больше, чем помещается на экране.

Единицы vmin и vmax

Единицы vmin и vmax берут за основу ширину или высоту области просмотра — в зависимости от того, какая из них меньше или больше:

  • 1vmin = 1vw или 1vh — меньшее из двух значений.
  • 1vmax = 1vw или 1vh — большее из двух значений.

Единицы vmin и vmax: попробуйте сами

Чёрный квадрат — 40% меньшей стороны области просмотра, синий — 40% большей. Сузьте окно браузера или перетащите разделитель между кодом и результатом так, чтобы область результата стала выше, чем шире: размер чёрного квадрата будет рассчитываться от её ширины, а синего — от высоты.

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <title>Единицы vmin и vmax</title>
    <style>
      body {
        margin: 0;
      }
      div {
        display: inline-block; /* блоки стоят в строку */
        color: white;
      }
      .min {
        width: 40vmin; /* 40% меньшей стороны окна */
        height: 40vmin;
        background-color: black;
      }
      .max {
        width: 40vmax; /* 40% большей стороны окна */
        height: 40vmax;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="min">40vmin</div>
    <div class="max">40vmax</div>
  </body>
</html>

Блоки стоят в строку благодаря свойству display со значением inline-block. Допустим, область просмотра шириной 400px и высотой 700px. Для первого блока с классом min меньшая сторона — 400px:

Ширина и высота одинаковы, получается квадрат 160×160px. Для второго блока с классом max берётся большая сторона — 700px:

Единицы окна поддерживаются всеми современными браузерами и пригодятся при адаптивной вёрстке, которую мы рассмотрим позже в уроке «Адаптивная вёрстка. Часть 1». Размер шрифта только в vw задавать не стоит: он не реагирует на настройку шрифта пользователя, а на узком экране становится слишком мелким. Вычислять размеры и ограничивать их помогают CSS-функции. Например, clamp() позволяет задать размер, который плавно меняется с шириной окна, но остаётся в заданных границах. Разберём эти функции ниже.

Вычисления и границы размера

CSS-функции — это встроенные команды, с помощью которых браузер получает значение свойства из переданных данных. Функцию записывают как имя с круглыми скобками, внутри которых указывают исходные значения или выражение. Их называют аргументами. Браузер вычисляет результат функции динамически и использует его как значение свойства. Если величины, от которых зависит расчёт, меняются — например, ширина окна или размер шрифта, — результат пересчитывается. Например, в width: calc(100% - 2rem) функция calc() рассчитывает ширину блока.

Когда размер зависит сразу от окна и шрифта, CSS может выполнить расчёт сам. calc(100% - 2rem) вычитает из доступной ширины два размера шрифта корня; вокруг знаков + и - нужны пробелы. Можно сочетать совместимые величины, например длину и процент в свойстве width.

  • min(100%, 30rem) выбирает меньшее: блок не шире родителя и не шире 30rem.
  • max(1rem, 2vw) выбирает большее: отступ растёт с окном, но не меньше 1rem.
  • clamp(1.5rem, 1rem + 2vw, 2.5rem) задаёт минимум, желаемый размер и максимум. Заголовок плавно меняется между 1.5rem и 2.5rem; сумму внутри можно писать без calc().

Составляющая в rem учитывает настройку шрифта, а vw — ширину окна. Это не гарантирует нужное увеличение при любых границах: проверьте текст при масштабе 200% и увеличенном шрифте браузера. Если заголовок почти не увеличивается, уменьшите долю vw или задайте размер в rem.

Функции calc(), min(), max() и clamp(): попробуйте сами

Сужайте окно результата: ширина текста ограничена min(), заголовок меняется в границах clamp(). Замените верхнюю границу 2.5rem на 3rem и сравните на широком экране.

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Вычисления размеров</title>
  </head>
  <body>
    <main>
      <h1>Прогулки с Тони</h1>
      <p>Тони любит долгие прогулки в парке. Размер заголовка зависит от окна, а строка текста не растягивается бесконечно.</p>
    </main>
  </body>
</html>
style.css
CSS
main {
  width: calc(100% - 2rem);
  margin: max(1rem, 2vw) auto;
}
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); }
p { width: min(100%, 30rem); }

Потренируйтесь

Введите 40 и выберите vmin. Задайте ширину окна 400px и высоту 700px: получится 160px. Поменяйте ширину и высоту местами — результат сохранится. Затем выберите vw и повторите сравнение: теперь результат зависит только от ширины. Затем выберите vmax и проверьте расчёт от большей стороны.

Можно ввести с единицей: 1.5rem, 12pt, 50vw

Контекст, px
для vw, vmin, vmax
при скрытой панели браузера; для vh, lvh, dvh, vmin, vmax
Абсолютные
Абсолютные
px24pxCSS-пиксель
От окна браузера
От окна браузера
vw1.6667vwот ширины окна24px ÷ 1440px × 100 = 1.6667vw
vh2.6667vhот высоты окна24px ÷ 900px × 100 = 2.6667vh
vmin2.6667vminот меньшей стороны окна24px ÷ 900px × 100 = 2.6667vmin
vmax1.6667vmaxот большей стороны окна24px ÷ 1440px × 100 = 1.6667vmax

Все единицы и настройки — в конвертере единиц CSS

Проверьте себя

Выберите ответ — объяснение появится сразу. Ответ можно менять.

1. При переполнении страницы появилась вертикальная полоса прокрутки, занимающая место. Блоку задали width: 100vw и обнулили отступы <body>. Почему может остаться горизонтальная прокрутка?
Показать ответ

Полоса прокрутки занимает часть окна, а 100vw её не учитывает — 100vw обычно включает ширину появившейся полосы. Для обычного блока в нормальном потоке достаточно width: auto — он заполнит доступную ширину родителя.

2. Область просмотра шириной 400px и высотой 700px. Чему равна ширина блока width: 40vmin?
Показать ответ

160px — vmin берёт меньшую сторону области просмотра — 400px: 400 × 40% = 160px.

3. Корневой шрифт 16px, окно 400px. Чему равен clamp(1rem, 5vw, 2rem)?
Показать ответ

20px — 5vw = 20px: значение находится между минимумом 16px и максимумом 32px.

Задание

В редакторе — афиша прогулки с Тони. На телефоне заголовок должен оставаться читаемым, а на широком экране описание не должно растягиваться в длинную строку. Посмотрите образец результата, затем выключите глаз, чтобы видеть свои изменения. Оформление афиши уже задано в base.css; измените style.css:

  1. Ограничьте ширину абзаца меньшим из значений 100% и 30rem с помощью min().
  2. Задайте заголовку размер через clamp(): минимум 1.5rem, желаемое значение 1rem + 2vw, максимум 2.5rem.

Ожидаемый результат: при расширении области результата заголовок растёт, но останавливается на верхней границе; при сужении не становится меньше нижней. Абзац занимает доступную ширину, пока она меньше 30rem, а затем перестаёт расширяться. Перетащите разделитель между кодом и результатом и проверьте оба состояния. При размере шрифта корня 16px границы заголовка — 24px и 40px, предельная ширина абзаца — 480px.

Выполните задание

base.css
CSS
/* Оформление афиши. Размер заголовка и ширина описания задаются в style.css. */
body {
  margin: 16px;
  background: #f7f2e8;
  color: #263d32;
  font-family: Arial, sans-serif;
  line-height: 1.6;
}
.eyebrow {
  color: #5b6250;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
h1 {
  margin: 16px 0;
  line-height: 1.15;
}
p {
  box-sizing: border-box;
  padding: 16px;
  border-left: 4px solid #b66324;
  background: #fffdf8;
}
footer {
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid #c5c9b8;
}
index.html
HTML
<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Прогулка с Тони</title>
</head>
<body>
  <header>
    <small class="eyebrow">Клуб неспешных прогулок</small>
    <h1>За новыми запахами!</h1>
  </header>
  <p>Тони зовёт исследовать парк: заглянуть под старый дуб, послушать уток у пруда и найти самую длинную палку.
    Возьмите воду, удобную обувь и хорошее настроение — торопиться сегодня некуда.</p>
  <footer>
    <strong>Воскресенье · 10:00</strong><br>
    Встречаемся у входа в парк<br>
    <small>Маршрут: 2 км · Темп: сколько нужно Тони</small>
  </footer>
</body>
</html>
style.css
CSS
h1 {
  font-size: 2rem;
}
p {
  width: 100%;
}

Для самопроверки откройте решение и сравните правила. При ширине области результата 400px желаемый размер заголовка — 16px + 8px = 24px; при 800px — 32px; при 1600px сумма равна 48px, но ограничение оставит 40px. Увеличьте масштаб браузера до 200% и убедитесь, что текст остаётся читаемым. Решение можно скопировать или скачать и проверить в отдельной учебной папке, сохранив my-site для следующего урока.

История единиц окна

Поддержка vw, vh, vmin и vmax в старых браузерах

Единицы окна появились в браузерах в начале 2010-х годов, и долгое время их поддержка была неполной. Opera Mini и UC Browser for Android их не поддерживали. Internet Explorer 9 понимал вместо vmin устаревшую запись vm; Internet Explorer 10 и 11 и Edge 12–14 не поддерживали vmax. В iOS Safari стабильная поддержка появилась с 8-й версии: в 6-й не было vmax, в 7-й некорректно работала vh.

Сегодня vw, vh, vmin и vmax работают во всех современных браузерах. Единицы svh, lvh и dvh появились в 2022 году и тоже поддерживаются современными браузерами.