Пошук уроків, статей та іншого контенту
Адаптуйте багатосекційний сайт до смартфонів, планшетів і широких екранів за допомогою media queries та responsive-одиниць.
Адаптивний інтерфейс змінює свою структуру та розміри залежно від ширини екрана. Один і той самий сайт має бути зручним:
на смартфоні в портретній орієнтації;
на планшеті;
на ноутбуці;
на широкому моніторі.
Адаптивність — це не просто зменшення всіх елементів. На вузькому екрані можуть змінюватися:
кількість колонок;
напрямок flex-контейнерів;
розмір заголовків;
відступи;
видимість або розташування навігації;
ширина кнопок і полів;
спосіб розміщення зображень.
Основний інструмент CSS для адаптивності — media query, або медіазапит.
Фіксовані одиниці на кшталт px корисні для тонких деталей: рамок, тіней, маленьких відступів. Але розміри великих блоків краще будувати на відносних одиницях.
Відсоток обчислюється від розміру батьківського елемента:
.container {
width: 90%;
}Такий контейнер займатиме 90% ширини батьківського елемента.
remrem залежить від розміру шрифту кореневого елемента, тобто html.
html {
font-size: 16px;
}
.card {
padding: 1.5rem;
}Якщо базовий розмір шрифту дорівнює 16px, то 1.5rem — це 24px.
rem зручно використовувати для:
шрифтів;
внутрішніх відступів;
зовнішніх відступів;
радіусів та інших узгоджених розмірів.
vw і vh1vw — 1% ширини області перегляду;
1vh — 1% висоти області перегляду.
.hero {
min-height: 70vh;
}Такий блок матиме мінімальну висоту, що дорівнює 70% висоти вікна браузера.
Не варто будувати весь інтерфейс лише на vw та vh, адже текст може стати надто малим або великим. Для заголовків краще поєднувати їх із clamp().
clamp()Функція clamp() задає:
мінімальне значення;
бажане гнучке значення;
максимальне значення.
.hero__title {
font-size: clamp(2rem, 5vw, 4.5rem);
}Заголовок:
не буде меншим за 2rem;
намагатиметься використовувати 5vw;
не перевищить 4.5rem.
Це дозволяє плавно змінювати розмір без великої кількості медіазапитів.
Медіазапит застосовує CSS лише за певної умови.
@media (min-width: 48rem) {
.layout {
display: grid;
grid-template-columns: 1fr 1fr;
}
}У цьому прикладі дві колонки з’являться, якщо ширина області перегляду становить щонайменше 48rem.
Поширені умови:
@media (max-width: 47.99rem) {
/* Стилі для вузьких екранів */
}
@media (min-width: 48rem) {
/* Стилі для планшетів і ширших екранів */
}
@media (min-width: 75rem) {
/* Стилі для широких екранів */
}Точні точки переходу не є універсальними. Їх варто обирати не за моделями пристроїв, а за моментом, коли поточний макет перестає бути зручним.
Під час підходу mobile first спочатку створюють стилі для найвужчого екрана, а потім розширюють макет за допомогою min-width.
.navigation {
display: none;
}
@media (min-width: 48rem) {
.navigation {
display: flex;
}
}Переваги такого підходу:
базові стилі простіші;
мобільна версія не залежить від перевизначення великої кількості властивостей;
легше контролювати порядок ієрархії;
спочатку опрацьовується найобмеженіший простір.
На широкому моніторі текст і блоки не повинні розтягуватися на всю ширину екрана. Для цього використовують контейнер:
.container {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}Вираз 100% - 2rem залишає горизонтальні відступи, а 72rem обмежує максимальну ширину.
Це компактна альтернатива такому запису:
.container {
width: calc(100% - 2rem);
max-width: 72rem;
margin-inline: auto;
}Для багатосекційного сайту часто використовують CSS Grid. На смартфоні картки розташовуються в одну колонку, а на ширших екранах — у дві або три.
.cards {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 42rem) {
.cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 70rem) {
.cards {
grid-template-columns: repeat(3, 1fr);
}
}Одиниця fr означає частину вільного простору контейнера.
Також можна створити гнучку сітку без окремого медіазапиту:
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: 1rem;
}Кожна картка не буде меншою за 16rem, а кількість колонок автоматично змінюватиметься відповідно до доступної ширини.
Нижче наведено повну сторінку з кількома секціями:
шапкою та навігацією;
hero-секцією;
секцією переваг;
секцією про команду;
формою зворотного зв’язку;
підвалом.
Базові стилі призначені для смартфонів. Медіазапити поступово адаптують сайт для ширших екранів.
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Northstar Studio</title>
<style>
:root {
--color-background: #f5f7fb;
--color-surface: #ffffff;
--color-text: #172033;
--color-muted: #657089;
--color-primary: #4f46e5;
--color-primary-dark: #3730a3;
--color-border: #dce1ec;
--radius: 1rem;
--shadow: 0 1rem 2.5rem rgb(23 32 51 / 8%);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
color: var(--color-text);
background: var(--color-background);
font-family: Arial, sans-serif;
line-height: 1.6;
}
img {
display: block;
max-width: 100%;
}
a {
color: inherit;
}
.container {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.site-header {
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
.header-inner {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
padding-block: 1rem;
}
.logo {
color: var(--color-primary);
font-size: 1.25rem;
font-weight: 700;
text-decoration: none;
}
.navigation {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.75rem 1rem;
}
.navigation a {
color: var(--color-muted);
font-size: 0.95rem;
text-decoration: none;
}
.navigation a:hover,
.navigation a:focus-visible {
color: var(--color-primary);
}
.hero {
display: grid;
gap: 2rem;
align-items: center;
min-height: 70vh;
padding-block: 4rem;
}
.hero__content {
max-width: 38rem;
}
.eyebrow {
margin: 0 0 0.75rem;
color: var(--color-primary);
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
margin-bottom: 1.25rem;
font-size: clamp(2.25rem, 9vw, 4.5rem);
line-height: 1.05;
}
h2 {
margin-bottom: 0.75rem;
font-size: clamp(1.75rem, 4vw, 2.75rem);
line-height: 1.15;
}
h3 {
margin-bottom: 0.5rem;
font-size: 1.25rem;
}
.hero__description,
.section-heading__description {
color: var(--color-muted);
font-size: 1.05rem;
}
.hero__actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 1.5rem;
}
.button {
display: inline-block;
padding: 0.8rem 1.2rem;
border: 2px solid var(--color-primary);
border-radius: 999px;
font-weight: 700;
text-align: center;
text-decoration: none;
}
.button--primary {
color: #ffffff;
background: var(--color-primary);
}
.button--primary:hover,
.button--primary:focus-visible {
background: var(--color-primary-dark);
border-color: var(--color-primary-dark);
}
.button--secondary {
color: var(--color-primary);
background: transparent;
}
.hero__visual {
display: grid;
place-items: center;
min-height: 16rem;
padding: 2rem;
border-radius: 2rem;
color: #ffffff;
background:
linear-gradient(135deg, rgb(79 70 229 / 95%), rgb(14 165 233 / 90%));
box-shadow: var(--shadow);
}
.hero__visual span {
font-size: clamp(3rem, 18vw, 8rem);
font-weight: 700;
line-height: 1;
}
.section {
padding-block: 4rem;
}
.section--light {
background: var(--color-surface);
}
.section-heading {
max-width: 40rem;
margin-bottom: 2rem;
}
.cards {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.card {
padding: 1.5rem;
border: 1px solid var(--color-border);
border-radius: var(--radius);
background: var(--color-surface);
}
.card__number {
display: inline-grid;
place-items: center;
width: 2.5rem;
height: 2.5rem;
margin-bottom: 1rem;
border-radius: 50%;
color: #ffffff;
background: var(--color-primary);
font-weight: 700;
}
.card p {
margin-bottom: 0;
color: var(--color-muted);
}
.about {
display: grid;
gap: 2rem;
align-items: center;
}
.about__panel {
padding: 2rem;
border-radius: var(--radius);
background: #e9eafe;
}
.about__panel strong {
display: block;
margin-bottom: 0.25rem;
color: var(--color-primary-dark);
font-size: 2rem;
}
.contact-layout {
display: grid;
gap: 2rem;
}
.contact-list {
display: grid;
gap: 1rem;
padding: 0;
margin: 0;
list-style: none;
}
.contact-list li {
padding-bottom: 1rem;
border-bottom: 1px solid var(--color-border);
}
.contact-list span {
display: block;
color: var(--color-muted);
}
.contact-form {
display: grid;
gap: 1rem;
padding: 1.5rem;
border-radius: var(--radius);
background: var(--color-surface);
box-shadow: var(--shadow);
}
.form-field {
display: grid;
gap: 0.4rem;
}
.form-field label {
font-weight: 700;
}
.form-field input,
.form-field textarea {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid var(--color-border);
border-radius: 0.6rem;
color: var(--color-text);
background: #ffffff;
font: inherit;
}
.form-field textarea {
min-height: 8rem;
resize: vertical;
}
.contact-form .button {
cursor: pointer;
font: inherit;
}
.site-footer {
padding-block: 1.5rem;
color: #dbe2ff;
background: #172033;
}
.footer-inner {
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: center;
text-align: center;
}
.site-footer p {
margin: 0;
}
@media (min-width: 42rem) {
.container {
width: min(100% - 3rem, 72rem);
}
.header-inner {
flex-direction: row;
justify-content: space-between;
}
.hero {
grid-template-columns: minmax(0, 1.1fr) minmax(16rem, 0.9fr);
gap: 3rem;
padding-block: 6rem;
}
.hero__visual {
min-height: 22rem;
}
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.about {
grid-template-columns: minmax(0, 1.5fr) minmax(12rem, 0.7fr);
}
.contact-layout {
grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
align-items: start;
}
.section {
padding-block: 5rem;
}
}
@media (min-width: 70rem) {
.container {
width: min(100% - 4rem, 76rem);
}
.cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.hero {
gap: 5rem;
}
.header-inner {
padding-block: 1.25rem;
}
}
</style>
</head>
<body>
<header class="site-header">
<div class="container header-inner">
<a class="logo" href="#top">Northstar Studio</a>
<nav class="navigation" aria-label="Основна навігація">
<a href="#services">Послуги</a>
<a href="#about">Про нас</a>
<a href="#contact">Контакти</a>
</nav>
</div>
</header>
<main id="top">
<section class="hero">
<div class="container hero__content">
<p class="eyebrow">Цифрові продукти</p>
<h1>Створюємо інтерфейси для будь-якого екрана</h1>
<p class="hero__description">
Проєктуємо зрозумілі та швидкі вебсайти, які однаково добре
працюють на смартфонах, планшетах і широких моніторах.
</p>
<div class="hero__actions">
<a class="button button--primary" href="#contact">Обговорити проєкт</a>
<a class="button button--secondary" href="#services">Наші послуги</a>
</div>
</div>
<div class="container hero__visual" aria-hidden="true">
<span>RWD</span>
</div>
</section>
<section class="section section--light" id="services">
<div class="container">
<div class="section-heading">
<p class="eyebrow">Наш підхід</p>
<h2>Що отримує ваш продукт</h2>
<p class="section-heading__description">
Кожна частина інтерфейсу пристосована до доступного простору.
</p>
</div>
<div class="cards">
<article class="card">
<span class="card__number">01</span>
<h3>Гнучка структура</h3>
<p>
Сітки та відступи змінюються без горизонтального прокручування.
</p>
</article>
<article class="card">
<span class="card__number">02</span>
<h3>Зрозуміла навігація</h3>
<p>
Користувач швидко знаходить потрібний розділ на будь-якому екрані.
</p>
</article>
<article class="card">
<span class="card__number">03</span>
<h3>Єдина система</h3>
<p>
Типографіка, кольори та компоненти залишаються послідовними.
</p>
</article>
</div>
</div>
</section>
<section class="section" id="about">
<div class="container about">
<div>
<p class="eyebrow">Про студію</p>
<h2>Дизайн, який масштабується разом із бізнесом</h2>
<p>
Ми починаємо з найважливішого контенту та будуємо структуру так,
щоб вона залишалася зручною під час розширення проєкту.
</p>
</div>
<div class="about__panel">
<strong>96%</strong>
<span>наших проєктів спочатку тестуються на мобільних екранах</span>
</div>
</div>
</section>
<section class="section section--light" id="contact">
<div class="container contact-layout">
<div>
<p class="eyebrow">Зв’язатися</p>
<h2>Поговорімо про ваш проєкт</h2>
<p>
Опишіть завдання, а ми підготуємо наступні кроки.
</p>
<ul class="contact-list">
<li>
<strong>Електронна пошта</strong>
<span>hello@example.com</span>
</li>
<li>
<strong>Графік роботи</strong>
<span>Пн–Пт, 09:00–18:00</span>
</li>
</ul>
</div>
<form class="contact-form">
<div class="form-field">
<label for="name">Ваше ім’я</label>
<input id="name" name="name" type="text" required>
</div>
<div class="form-field">
<label for="email">Електронна пошта</label>
<input id="email" name="email" type="email" required>
</div>
<div class="form-field">
<label for="message">Повідомлення</label>
<textarea id="message" name="message" required></textarea>
</div>
<button class="button button--primary" type="submit">
Надіслати
</button>
</form>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer-inner">
<p>© 2025 Northstar Studio</p>
<p>Адаптивні рішення для сучасного вебу</p>
</div>
</footer>
</body>
</html>На смартфоні логотип і навігація розташовані вертикально:
.header-inner {
display: flex;
flex-direction: column;
}Після ширини 42rem вони переходять в один ряд:
@media (min-width: 42rem) {
.header-inner {
flex-direction: row;
justify-content: space-between;
}
}У мобільній версії текст і візуальний блок розташовані один під одним:
.hero {
display: grid;
gap: 2rem;
}На ширшому екрані з’являються дві колонки:
@media (min-width: 42rem) {
.hero {
grid-template-columns: minmax(0, 1.1fr) minmax(16rem, 0.9fr);
}
}minmax(0, 1.1fr) дозволяє колонці стискатися, не створюючи зайвого горизонтального переповнення.
На вузькому екрані одна картка займає всю ширину. На середньому — дві картки в ряд, на широкому — три:
.cards {
grid-template-columns: 1fr;
}
@media (min-width: 42rem) {
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 70rem) {
.cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}Розмір заголовка змінюється плавно:
h1 {
font-size: clamp(2.25rem, 9vw, 4.5rem);
}Для мобільного екрана значення не стане меншим за 2.25rem, а на широкому не перевищить 4.5rem.
Під час перевірки змініть ширину вікна браузера або скористайтеся режимом емуляції пристроїв у DevTools.
Перевірте:
Чи немає горизонтального прокручування.
Чи не виходять кнопки за межі екрана.
Чи залишається текст читабельним.
Чи не стають колонки надто вузькими.
Чи зберігається правильний порядок секцій.
Чи зручно натискати кнопки та посилання на сенсорному екрані.
Чи не зникає важливий контент під час зміни ширини.
Перевіряйте не лише стандартні ширини. Корисно тестувати проміжні значення, наприклад ширину, на якій три колонки вже не поміщаються, але медіазапит для двох колонок ще не спрацював.
Проблемний варіант:
.content {
width: 1200px;
}На екрані, вужчому за 1200px, блок створить горизонтальне прокручування.
Краще:
.content {
width: min(100% - 2rem, 75rem);
margin-inline: auto;
}meta viewportБез цього тегу мобільний браузер може відображати сторінку у віртуальній ширині, через що медіазапити працюватимуть неочікувано:
<meta name="viewport" content="width=device-width, initial-scale=1.0">Не потрібно створювати окремий запит для кожної моделі смартфона. Визначайте точку переходу тоді, коли поточна структура вже не працює.
Наприклад, якщо три картки стають надто вузькими, саме це є підставою для переходу до двох колонок.
Якщо для кожного елемента створено багато дрібних медіазапитів, CSS стає складним для підтримки. Спочатку використовуйте:
max-width;
minmax();
min();
max();
clamp();
перенесення елементів через flex-wrap.
Медіазапит додавайте тоді, коли справді потрібно змінити структуру.
height: 100vh для великих секційНа мобільних пристроях висота панелі браузера може змінюватися. Через це секція з height: 100vh іноді обрізає контент.
Якщо потрібна мінімальна висота, безпечніше використовувати:
.hero {
min-height: 70vh;
}Або дозволити секції збільшуватися разом із контентом.
Довгі слова, URL або рядки без пробілів можуть виходити за межі контейнера. Для текстових блоків можна додати:
.text-block {
overflow-wrap: anywhere;
}Якщо спочатку створити складний десктопний макет, а потім намагатися «стиснути» його для смартфона, можуть виникнути конфлікти стилів. Підхід mobile first змушує спочатку визначити основну структуру та пріоритетний контент.
Адаптивний інтерфейс змінює структуру відповідно до ширини екрана.
Медіазапити застосовують CSS за певних умов.
Підхід mobile first починається зі стилів для смартфонів і розширюється через min-width.
rem, %, vw, vh, fr та clamp() допомагають створювати гнучкі розміри.
max-width контейнера захищає контент від надмірного розтягування на широких екранах.
CSS Grid і Flexbox дають змогу змінювати кількість колонок та напрямок блоків.
Адаптивність потрібно перевіряти на різних ширинах, зокрема на проміжних значеннях.
Хороший responsive-дизайн не лише зменшує елементи, а й змінює структуру так, щоб інтерфейс залишався зручним.