Skip to main content

Що таке vh, vw, vmin та vmax у CSS

vh, vw, vmin та vmax - це CSS viewport-одиниці, де 1 одиниця дорівнює 1% відповідного розміру вікна браузера: vh - висота, vw - ширина, vmin - менша сторона, vmax - більша.

Теорія

TL;DR

  • 100vh заповнює екран зверху донизу, 100vw - зліва направо
  • vmin прив'язується до коротшої сторони вікна, vmax - до довшої. Обидві автоматично перераховуються при повороті пристрою
  • На відміну від px, всі чотири одиниці оновлюються при кожному ресайзі та зміні орієнтації
  • Viewport-одиниці - для контейнерів і hero-секцій, rem - для тексту
  • Підводний камінь: 100vh на iOS Safari виходить за межі видимої області на ~80px через адресний рядок

Швидкий приклад

css
.hero { height: 100vh; /* займає повну висоту вікна */ width: 100vw; /* займає повну ширину вікна */ } .card { width: 40vmin; /* 40% від коротшої сторони, зберігає пропорції при повороті */ height: 40vmin; background: #4a90e2; }

На десктопі 1200x800: 1vh = 8px, 1vw = 12px, 1vmin = 8px. Поверни телефон - і vmin сам переключиться на коротшу сторону. Жодного JavaScript.

Ключова різниця від інших одиниць

Viewport-одиниці завжди відраховуються від вікна браузера, а не від батьківського елемента. 50vw на глибоко вкладеному елементі означає рівно половину екрана. А 50% - половину батьківського контейнера. Це важливо: viewport-одиниці добре підходять для секцій на весь екран, але не для компонентів, що мають масштабуватися відносно свого контейнера.

Коли використовувати

  • Повноекранні hero-секції: height: 100vh
  • Квадратні елементи, які не ламаються при повороті: width: 40vmin; height: 40vmin
  • Модалки, що поміщаються під браузерним chrome: max-height: 90vh
  • Банери, які розтягуються по максимальній стороні: vmax
  • Текст у контенті: rem, не viewport-одиниці

Таблиця порівняння

ОдиницяОсноваPortrait 390x844Landscape 844x390Використання
vhВисота вікна1vh = 8.44px1vh = 3.9pxПовноекранні секції
vwШирина вікна1vw = 3.9px1vw = 8.44pxГоризонтальні блоки
vminМенша сторона1vmin = 3.9px1vmin = 3.9pxКвадратні елементи
vmaxБільша сторона1vmax = 8.44px1vmax = 8.44pxРозтягнуті банери
pxФіксованийЗавжди 1pxЗавжди 1pxТочні розміри

Як браузер обчислює ці одиниці

Браузер вимірює початковий контейнер (прямокутник вікна без смуг прокрутки), ділить на 100 і множить на твоє число. Перерахунок відбувається при кожній зміні розміру вікна або орієнтації пристрою. Складнощі починаються на мобільних: iOS Safari включав адресний рядок у розрахунок vh, тому 100vh виявлявся вищим за видиму область приблизно на 80px. Chrome 108 і iOS 15.4 додали dvh (dynamic viewport height) для вирішення цієї проблеми.

Типові помилки

height: 100vh для повноекранного мобільного layout:

css
/* Неправильно: iOS Safari обрізає ~80px під адресним рядком */ .app { height: 100vh; } /* Правильно */ .app { height: 100vh; /* fallback для старіших браузерів */ height: 100dvh; /* Chrome 108+, iOS 15.4+ - перекриває vh якщо підтримується */ }

Саме ця помилка ламала bottom-навігацію в кількох продакшн-застосунках на Next.js, які я бачив. Два рядки коду повністю її виправляють.

width: 100vw для nav-бару:

css
/* Неправильно: додає ширину смуги прокрутки (~15px), з'являється горизонтальний скрол */ nav { width: 100vw; } /* Правильно */ nav { width: 100%; }

Відсутність fallback для старих браузерів:

css
.box { width: 30%; } /* fallback */ .box { width: 30vmin; } /* сучасні браузери перекривають */

IE 9-11 частково підтримував vh і vw, але vmin і vmax не розумів. Відсотки як fallback вирішують це.

Де зустрічається

  • Tailwind CSS: h-screen компілюється в height: 100vh
  • Bootstrap 5: --bs-full-height: 100vh в offcanvas і модальних компонентах
  • shadcn/ui: повноекранні лоадери використовують height: 100vh
  • Three.js: width: 100vw; height: 100vh для canvas-елементів
  • Next.js landing pages: hero-секції і backdrop модальних вікон

Питання на співбесіді

Q: Яка різниця між 100vh і height: 100%?
A: 100vh завжди дорівнює висоті вікна браузера. height: 100% потребує, щоб кожен батьківський елемент аж до <html> мав явно задану висоту, інакше елемент стискається до нуля.

Q: Чому 100vh не працює правильно в мобільному Safari?
A: Safari включав адресний рядок у розрахунок vh, тому 100vh виявлявся більшим за видиму частину екрана. Для iOS 15.4+ і Chrome 108+ використовуй 100dvh. Для старих браузерів досі актуальний JS-хак із CSS-змінною.

Q: Коли вибрати vmin замість vw?
A: Коли елемент має бути видимим в обох орієнтаціях. vmin завжди прив'язаний до коротшої сторони, тому 40vmin після повороту не вийде за межі екрана.

Q: Що таке dvh, svh і lvh?
A: Нові одиниці з CSS Viewport Units Level 4. dvh - динамічна висота (змінюється зі зміною адресного рядка). svh - мінімальна висота (рядок видно). lvh - максимальна (рядок прихований). Всі основні браузери підтримують їх з 2023 року.

Приклади

Повноекранна hero-секція

css
.hero { width: 100vw; height: 100vh; /* базовий варіант для всіх браузерів */ height: 100dvh; /* динамічна висота для сучасних мобільних */ display: flex; align-items: center; justify-content: center; background: #1a1a2e; }

Стандартний паттерн для landing-сторінок. Друге оголошення height перекриває перше в браузерах з підтримкою dvh, тому окремий media query не потрібен.

Адаптивне модальне вікно

css
.modal { width: clamp(300px, 80vw, 800px); height: clamp(200px, 70vh, 600px); max-width: 90vmin; margin: auto; background: white; border-radius: 8px; overflow: auto; }

На десктопі 1920x1080 modal займе 800x600px. На портретному телефоні стисниться до приблизно 310x480px. Обмеження 90vmin запобігає overflow в обох орієнтаціях без додаткових media queries.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?