Що таке 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 через адресний рядок
Швидкий приклад
.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 390x844 | Landscape 844x390 | Використання |
|---|---|---|---|---|
vh | Висота вікна | 1vh = 8.44px | 1vh = 3.9px | Повноекранні секції |
vw | Ширина вікна | 1vw = 3.9px | 1vw = 8.44px | Горизонтальні блоки |
vmin | Менша сторона | 1vmin = 3.9px | 1vmin = 3.9px | Квадратні елементи |
vmax | Більша сторона | 1vmax = 8.44px | 1vmax = 8.44px | Розтягнуті банери |
px | Фіксований | Завжди 1px | Завжди 1px | Точні розміри |
Як браузер обчислює ці одиниці
Браузер вимірює початковий контейнер (прямокутник вікна без смуг прокрутки), ділить на 100 і множить на твоє число. Перерахунок відбувається при кожній зміні розміру вікна або орієнтації пристрою. Складнощі починаються на мобільних: iOS Safari включав адресний рядок у розрахунок vh, тому 100vh виявлявся вищим за видиму область приблизно на 80px. Chrome 108 і iOS 15.4 додали dvh (dynamic viewport height) для вирішення цієї проблеми.
Типові помилки
height: 100vh для повноекранного мобільного layout:
/* Неправильно: iOS Safari обрізає ~80px під адресним рядком */
.app { height: 100vh; }
/* Правильно */
.app {
height: 100vh; /* fallback для старіших браузерів */
height: 100dvh; /* Chrome 108+, iOS 15.4+ - перекриває vh якщо підтримується */
}Саме ця помилка ламала bottom-навігацію в кількох продакшн-застосунках на Next.js, які я бачив. Два рядки коду повністю її виправляють.
width: 100vw для nav-бару:
/* Неправильно: додає ширину смуги прокрутки (~15px), з'являється горизонтальний скрол */
nav { width: 100vw; }
/* Правильно */
nav { width: 100%; }Відсутність fallback для старих браузерів:
.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-секція
.hero {
width: 100vw;
height: 100vh; /* базовий варіант для всіх браузерів */
height: 100dvh; /* динамічна висота для сучасних мобільних */
display: flex;
align-items: center;
justify-content: center;
background: #1a1a2e;
}Стандартний паттерн для landing-сторінок. Друге оголошення height перекриває перше в браузерах з підтримкою dvh, тому окремий media query не потрібен.
Адаптивне модальне вікно
.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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.