Пошук уроків, статей та іншого контенту
Зберіть завершений адаптивний сайт від макета до фінальної версії, поєднавши семантику, компонування, типографіку та доступність.
У фінальному проєкті потрібно зібрати повноцінний адаптивний сайт від початкового макета до завершеної версії.
Під час роботи ви поєднаєте:
семантичну HTML-структуру;
компонування за допомогою Flexbox і Grid;
адаптивні брейкпойнти;
типографіку та систему відступів;
стани інтерактивних елементів;
доступність для клавіатури та скринридерів;
перевірку сайту на різних розмірах екрана.
Як приклад використаємо односторінковий сайт цифрової студії. Він містить:
шапку з навігацією;
головний екран;
секцію послуг;
секцію з процесом роботи;
заклик до дії;
підвал.
Перед написанням коду не варто одразу створювати div для кожного елемента. Спочатку визначте структуру сторінки.
У макеті можна виділити такі області:
header — логотип і основна навігація.
main — унікальний вміст сторінки.
section — окремі тематичні блоки.
footer — додаткова навігація та контактна інформація.
Визначте:
головний заголовок сторінки;
заголовки секцій;
основну та додаткову кнопки;
найважливіші інформаційні блоки;
порядок читання контенту на малих екранах.
На мобільному екрані контент має залишатися зрозумілим навіть без декоративних елементів. Якщо з макета прибирається зображення або змінюється порядок блоків, це не повинно порушувати логіку сторінки.
Зручно заздалегідь визначити набір змінних:
колір фону;
колір тексту;
акцентний колір;
максимальну ширину контейнера;
базовий радіус;
розмір основного відступу.
Це дає змогу змінювати дизайн централізовано, а не шукати однакові значення в усьому файлі.
Семантичні елементи описують призначення контенту, а не його зовнішній вигляд.
<header>
<nav aria-label="Основна навігація">
<!-- Посилання на основні розділи сторінки -->
</nav>
</header>
<main>
<section aria-labelledby="hero-title">
<h1 id="hero-title">...</h1>
</section>
<section aria-labelledby="services-title">
<h2 id="services-title">...</h2>
</section>
</main>
<footer>
<!-- Додаткова інформація про сайт -->
</footer>Важливі правила:
на сторінці має бути один основний h1;
рівні заголовків не варто пропускати без причини;
навігацію потрібно розміщувати в nav;
повторювані тематичні блоки можна представляти елементами article;
кнопка виконує дію, а посилання веде на іншу адресу або розділ;
для кожної секції бажано мати заголовок;
декоративні елементи не повинні потрапляти в дерево доступності.
Більшість секцій мають однакову горизонтальну структуру:
.container {
width: min(100% - 2rem, 70rem);
margin-inline: auto;
}Такий контейнер:
має відступи від країв на малих екранах;
не розтягується безмежно на великих;
автоматично центрується.
Для двовимірних сіток використовуйте Grid:
.services-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}Для рядків, у яких важливе вирівнювання елементів по одній осі, зручніший Flexbox:
.header-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
}Не задавайте всі розміри шрифтів лише фіксованими значеннями. Для заголовків зручно використовувати clamp():
h1 {
font-size: clamp(2.5rem, 7vw, 6rem);
}Функція clamp() приймає:
мінімальне значення;
гнучке значення;
максимальне значення.
Завдяки цьому заголовок плавно змінюється між розмірами екрана, а не стрибає лише в момент спрацювання медіазапиту.
Для основного тексту потрібно перевірити:
достатню висоту рядка;
контраст із фоном;
комфортну довжину рядка;
читабельність на мобільних екранах.
Нижче наведено завершений приклад односторінкового адаптивного сайту. Збережіть його у файлі index.html і відкрийте у браузері.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Northstar — цифрова студія для створення сучасних вебпродуктів."
/>
<title>Northstar — цифрова студія</title>
<style>
:root {
--color-bg: #f5f6f2;
--color-surface: #ffffff;
--color-text: #18201c;
--color-muted: #68736d;
--color-accent: #c9f25d;
--color-accent-dark: #9dbd36;
--color-border: #dfe4df;
--color-dark: #18201c;
--radius-small: 0.75rem;
--radius-large: 1.5rem;
--shadow-soft: 0 1rem 3rem rgb(24 32 28 / 8%);
--container-width: 72rem;
--page-padding: 1.25rem;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background: var(--color-bg);
color: var(--color-text);
font-family:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", sans-serif;
line-height: 1.6;
}
img,
svg {
display: block;
max-width: 100%;
}
a {
color: inherit;
}
.skip-link {
position: fixed;
top: 0.75rem;
left: 0.75rem;
z-index: 10;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
background: var(--color-accent);
color: var(--color-dark);
transform: translateY(-150%);
transition: transform 180ms ease;
}
.skip-link:focus {
transform: translateY(0);
}
.container {
width: min(
calc(100% - (var(--page-padding) * 2)),
var(--container-width)
);
margin-inline: auto;
}
.site-header {
border-bottom: 1px solid var(--color-border);
}
.header-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
min-height: 5.5rem;
}
.logo {
display: inline-flex;
align-items: center;
gap: 0.6rem;
color: var(--color-text);
font-size: 1.1rem;
font-weight: 800;
letter-spacing: -0.03em;
text-decoration: none;
}
.logo-mark {
width: 1.5rem;
aspect-ratio: 1;
border-radius: 0.4rem;
background: var(--color-accent);
transform: rotate(45deg);
}
.main-nav ul {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.main-nav a {
color: var(--color-muted);
font-size: 0.95rem;
text-decoration: none;
}
.main-nav a:hover {
color: var(--color-text);
}
.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 3rem;
padding: 0.75rem 1.25rem;
border: 1px solid transparent;
border-radius: 999px;
background: var(--color-accent);
color: var(--color-dark);
font-weight: 750;
text-decoration: none;
transition:
background-color 180ms ease,
transform 180ms ease;
}
.button:hover {
background: var(--color-accent-dark);
transform: translateY(-2px);
}
.button--outline {
border-color: var(--color-text);
background: transparent;
}
.button--outline:hover {
background: var(--color-text);
color: var(--color-surface);
}
:focus-visible {
outline: 3px solid var(--color-accent-dark);
outline-offset: 4px;
}
.hero {
padding-block: clamp(4rem, 10vw, 9rem);
}
.hero-grid {
display: grid;
grid-template-columns: minmax(0, 1.3fr) minmax(18rem, 0.7fr);
align-items: end;
gap: 3rem;
}
.eyebrow {
margin: 0 0 1rem;
color: var(--color-muted);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1,
h2,
h3 {
letter-spacing: -0.05em;
line-height: 1.05;
}
h1 {
max-width: 10ch;
margin-bottom: 1.5rem;
font-size: clamp(3.2rem, 9vw, 7.5rem);
}
.hero-description {
max-width: 34rem;
margin-bottom: 2rem;
color: var(--color-muted);
font-size: clamp(1.05rem, 2vw, 1.25rem);
}
.button-group {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.hero-card {
min-height: 20rem;
padding: 1.5rem;
border-radius: var(--radius-large);
background: var(--color-dark);
box-shadow: var(--shadow-soft);
color: var(--color-surface);
}
.hero-card-content {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
min-height: 17rem;
}
.hero-card-label {
color: var(--color-accent);
font-size: 0.85rem;
font-weight: 700;
}
.hero-card h2 {
max-width: 10ch;
margin-bottom: 0;
font-size: clamp(2rem, 4vw, 3.5rem);
}
.section {
padding-block: clamp(4rem, 8vw, 7rem);
}
.section-header {
display: flex;
align-items: end;
justify-content: space-between;
gap: 2rem;
margin-bottom: 2rem;
}
.section-title {
max-width: 12ch;
margin-bottom: 0;
font-size: clamp(2.3rem, 5vw, 4.5rem);
}
.section-description {
max-width: 28rem;
margin-bottom: 0;
color: var(--color-muted);
}
.services-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.service-card {
min-height: 18rem;
padding: 1.5rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-large);
background: var(--color-surface);
}
.service-number {
display: block;
margin-bottom: 4rem;
color: var(--color-muted);
font-size: 0.9rem;
font-weight: 700;
}
.service-card h3 {
margin-bottom: 0.75rem;
font-size: 1.5rem;
}
.service-card p {
margin-bottom: 0;
color: var(--color-muted);
}
.process {
background: var(--color-dark);
color: var(--color-surface);
}
.process .section-description {
color: #b5beb8;
}
.process-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1px;
overflow: hidden;
border: 1px solid #3c4540;
border-radius: var(--radius-large);
background: #3c4540;
}
.process-item {
min-height: 13rem;
padding: 1.5rem;
background: var(--color-dark);
}
.process-item strong {
display: block;
margin-bottom: 3rem;
color: var(--color-accent);
font-size: 0.9rem;
}
.process-item h3 {
margin-bottom: 0.75rem;
font-size: 1.35rem;
}
.process-item p {
margin-bottom: 0;
color: #b5beb8;
font-size: 0.95rem;
}
.contact-card {
display: flex;
align-items: end;
justify-content: space-between;
gap: 2rem;
padding: clamp(2rem, 6vw, 5rem);
border-radius: var(--radius-large);
background: var(--color-accent);
}
.contact-card h2 {
max-width: 11ch;
margin-bottom: 0;
font-size: clamp(2.5rem, 6vw, 5rem);
}
.contact-card .button {
flex-shrink: 0;
background: var(--color-dark);
color: var(--color-surface);
}
.contact-card .button:hover {
background: #35403a;
}
.site-footer {
padding-block: 2rem;
border-top: 1px solid var(--color-border);
}
.footer-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.footer-inner p {
margin-bottom: 0;
color: var(--color-muted);
font-size: 0.9rem;
}
@media (max-width: 52rem) {
.header-inner {
align-items: flex-start;
flex-direction: column;
padding-block: 1.25rem;
}
.hero-grid {
grid-template-columns: 1fr;
}
.hero-card {
max-width: 32rem;
}
.services-grid {
grid-template-columns: repeat(2, 1fr);
}
.process-list {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 36rem) {
:root {
--page-padding: 1rem;
}
.main-nav ul {
gap: 0.75rem 1rem;
}
.main-nav a {
font-size: 0.9rem;
}
.header-button {
display: none;
}
.section-header,
.contact-card,
.footer-inner {
align-items: flex-start;
flex-direction: column;
}
.services-grid,
.process-list {
grid-template-columns: 1fr;
}
.service-card {
min-height: auto;
}
.service-number {
margin-bottom: 3rem;
}
.process-item {
min-height: auto;
}
.process-item strong {
margin-bottom: 2rem;
}
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<a class="skip-link" href="#main-content">Перейти до основного вмісту</a>
<header class="site-header">
<div class="container header-inner">
<a class="logo" href="/" aria-label="Northstar, на головну">
<span class="logo-mark" aria-hidden="true"></span>
Northstar
</a>
<nav class="main-nav" aria-label="Основна навігація">
<ul>
<li><a href="#services">Послуги</a></li>
<li><a href="#process">Процес</a></li>
<li><a href="#contact">Контакти</a></li>
</ul>
</nav>
<a class="button header-button" href="#contact">Обговорити проєкт</a>
</div>
</header>
<main id="main-content">
<section class="hero" aria-labelledby="hero-title">
<div class="container hero-grid">
<div>
<p class="eyebrow">Цифрова студія · Київ</p>
<h1 id="hero-title">Створюємо продукти з характером.</h1>
<p class="hero-description">
Допомагаємо командам перетворювати складні ідеї на зрозумілі,
швидкі та корисні цифрові продукти.
</p>
<div class="button-group">
<a class="button" href="#contact">Почати розмову</a>
<a class="button button--outline" href="#services">
Наші послуги
</a>
</div>
</div>
<aside class="hero-card" aria-label="Ключова інформація">
<div class="hero-card-content">
<span class="hero-card-label">01 / 03</span>
<h2>Ідея, яка працює для людей.</h2>
</div>
</aside>
</div>
</section>
<section class="section" id="services" aria-labelledby="services-title">
<div class="container">
<div class="section-header">
<h2 class="section-title" id="services-title">Що ми робимо</h2>
<p class="section-description">
Поєднуємо стратегію, дизайн і технології, щоб створювати цілісний
досвід для користувачів.
</p>
</div>
<div class="services-grid">
<article class="service-card">
<span class="service-number">01</span>
<h3>Стратегія</h3>
<p>Досліджуємо проблему, аудиторію та цілі майбутнього продукту.</p>
</article>
<article class="service-card">
<span class="service-number">02</span>
<h3>Дизайн</h3>
<p>Створюємо зрозумілі інтерфейси з продуманою візуальною мовою.</p>
</article>
<article class="service-card">
<span class="service-number">03</span>
<h3>Розробка</h3>
<p>Перетворюємо макети на швидкі, адаптивні та доступні сайти.</p>
</article>
</div>
</div>
</section>
<section class="section process" id="process" aria-labelledby="process-title">
<div class="container">
<div class="section-header">
<h2 class="section-title" id="process-title">Як ми працюємо</h2>
<p class="section-description">
Прозорий процес допомагає приймати рішення та рухатися вперед без
зайвої складності.
</p>
</div>
<ol class="process-list">
<li class="process-item">
<strong>01</strong>
<h3>Знайомство</h3>
<p>Формулюємо завдання та визначаємо критерії успіху.</p>
</li>
<li class="process-item">
<strong>02</strong>
<h3>Концепція</h3>
<p>Шукаємо найкраще рішення для потреб бізнесу й аудиторії.</p>
</li>
<li class="process-item">
<strong>03</strong>
<h3>Створення</h3>
<p>Проєктуємо, розробляємо та перевіряємо кожен важливий сценарій.</p>
</li>
<li class="process-item">
<strong>04</strong>
<h3>Запуск</h3>
<p>Готуємо фінальну версію та передаємо продукт у роботу.</p>
</li>
</ol>
</div>
</section>
<section class="section" id="contact" aria-labelledby="contact-title">
<div class="container">
<div class="contact-card">
<h2 id="contact-title">Маєте ідею? Почнімо.</h2>
<a class="button" href="mailto:hello@northstar.example">
Написати нам
</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer-inner">
<a class="logo" href="/" aria-label="Northstar, на головну">
<span class="logo-mark" aria-hidden="true"></span>
Northstar
</a>
<p>© 2026 Northstar Studio. Усі права захищено.</p>
</div>
</footer>
</body>
</html>Посилання:
<a class="skip-link" href="#main-content">Перейти до основного вмісту</a>дозволяє користувачам клавіатури пропустити повторювану навігацію та одразу перейти до основного вмісту.
Воно приховане в звичайному стані, але стає видимим після отримання фокуса.
Стиль :focus-visible потрібен для навігації клавіатурою:
:focus-visible {
outline: 3px solid var(--color-accent-dark);
outline-offset: 4px;
}Не видаляйте стандартний фокус без заміни. Правило на кшталт:
*:focus {
outline: none;
}погіршує доступність, якщо для фокуса не створено альтернативний стиль.
У прикладі використано:
aria-label для опису призначення навігації;
aria-labelledby для зв’язку секції з її заголовком;
aria-hidden="true" для декоративного ромба логотипа;
aria-label для інформаційної картки.
ARIA не замінює семантичний HTML. Спочатку потрібно вибрати правильний HTML-елемент, а вже потім додавати ARIA там, де це справді необхідно.
У прикладі навігація не ховається за меню з JavaScript. На середніх екранах елементи переходять на новий рядок, а на вузьких екранах кнопка в шапці приховується:
@media (max-width: 36rem) {
.header-button {
display: none;
}
}Це прийнятне рішення для статичного демонстраційного сайту, де всі основні посилання залишаються доступними.
Для реального проєкту приховування кнопки потрібно узгодити з вимогами макета й бізнес-логікою. Важливі дії не повинні ставати недоступними на мобільних пристроях.
Перевірте сторінку щонайменше в таких режимах:
вузький мобільний екран;
широкий мобільний екран;
планшет;
ноутбук;
великий монітор.
Звертайте увагу не лише на те, чи «поміщається» контент. Перевірте:
чи не виходить текст за межі контейнера;
чи не злипаються кнопки;
чи достатні відступи між інтерактивними елементами;
чи не стають заголовки надто великими;
чи читається текст у кожній секції;
чи не з’являється горизонтальна прокрутка;
чи логічно змінюється порядок блоків.
Тимчасово додайте до DevTools або CSS:
* {
outline: 1px solid rgb(255 0 0 / 15%);
}Так можна побачити елемент, який виходить за межі сторінки.
Не варто одразу виправляти проблему правилом:
body {
overflow-x: hidden;
}Це приховає симптом, але не усуне причину. Спочатку знайдіть елемент із надмірною шириною, довгим текстом, фіксованим відступом або некоректною сіткою.
Пройдіть сторінку, використовуючи лише:
Tab — перейти до наступного інтерактивного елемента;
Shift + Tab — повернутися назад;
Enter — активувати посилання;
Space — активувати кнопку, якщо вона є.
Перевірте, що:
порядок фокуса відповідає порядку читання;
фокус завжди помітний;
немає елементів, до яких неможливо дістатися клавіатурою;
skip link працює.
Текст має чітко відрізнятися від фону. Особливо уважно перевірте:
сірий текст на світлому фоні;
текст на акцентній кнопці;
текст у темній секції;
стани наведення та фокуса.
Не використовуйте колір як єдиний спосіб передати інформацію. Наприклад, помилку у формі потрібно позначати не лише червоним кольором, а й текстовим повідомленням.
Перегляньте структуру заголовків у панелі доступності браузера. Вона має відображати логіку документа:
h1 Створюємо продукти з характером
h2 Що ми робимо
h3 Стратегія
h3 Дизайн
h3 Розробка
h2 Як ми працюємо
h3 Знайомство
h3 Концепція
h3 Створення
h3 Запуск
h2 Маєте ідею? Почнімо.Медіазапит prefers-reduced-motion враховує налаштування користувача, який не хоче бачити анімації:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}Навіть невеликі переходи потрібно або вимикати, або суттєво скорочувати для такого режиму.
Перед завершенням перевірте код за такими категоріями.
документ має <!doctype html>;
встановлено lang="uk";
є meta charset і meta viewport;
є змістовний title;
є описова метаінформація;
структура використовує семантичні елементи;
посилання мають зрозумілий текст;
порядок заголовків логічний.
повторювані кольори винесено у змінні;
ширина контенту обмежена контейнером;
немає зайвих фіксованих ширин;
сітка коректно перебудовується;
інтерактивні елементи мають hover і focus-visible;
текст не залежить від одного конкретного розміру екрана;
немає горизонтального переповнення.
відступи між секціями послідовні;
заголовки мають виразну ієрархію;
картки виглядають як частини однієї системи;
кнопки мають однакову висоту та стиль;
на мобільному екрані немає надто дрібного тексту;
контент не притиснутий до країв viewport.
divdiv не пояснює призначення вмісту. Замість цього використовуйте:
header для шапки;
nav для навігації;
main для основного вмісту;
section для тематичних розділів;
article для самостійних карток;
footer для підвалу.
Правило на кшталт:
.card {
width: 320px;
}може спричинити переповнення на малому екрані. Краще використовувати Grid, minmax(), відносні одиниці та адаптивні правила.
Якщо елемент веде до іншої секції, сторінки або адреси, використовуйте посилання:
<a href="#contact">Зв’язатися</a>Якщо елемент запускає дію на поточній сторінці, використовуйте кнопку:
<button type="button">Відкрити меню</button>Видалення outline без альтернативи робить сайт незручним для користувачів клавіатури.
Не потрібно створювати медіазапит для кожної моделі пристрою. Вибирайте точки зміни тоді, коли поточне компонування перестає працювати.
Краще мати два-три зрозумілі брейкпойнти, ніж багато правил, які складно підтримувати.
overflow: hiddenЦе може приховати важливий контент або зробити сторінку непридатною для клавіатурної навігації. Спочатку усуньте елемент, що спричинив переповнення.
Не використовуйте заголовок лише для збільшення тексту. Якщо текст не є заголовком, стилізуйте абзац або інший відповідний елемент.
Створіть власну версію сайту за цим процесом:
Визначте тему сайту та його цільову аудиторію.
Намалюйте макет для мобільного й широкого екрана.
Виділіть семантичні області сторінки.
Складіть набір кольорів, відступів і типографічних розмірів.
Реалізуйте HTML-структуру без прив’язки до візуальних деталей.
Додайте контейнер і базове компонування.
Реалізуйте десктопну та мобільну версії.
Додайте стани hover і focus-visible.
Перевірте сторінку з клавіатури.
Перевірте відсутність горизонтальної прокрутки.
Перевірте заголовки, контраст і доступність.
Перегляньте код і видаліть дублювання.
Проєкт можна вважати завершеним, якщо:
сторінка має зрозумілу семантичну структуру;
на сторінці є один логічний h1;
усі основні секції мають заголовки;
контент адаптується щонайменше до мобільного, планшетного та широкого екранів;
сторінка не має горизонтального переповнення;
навігацією можна користуватися з клавіатури;
фокус інтерактивних елементів помітний;
кольори та розміри централізовано описані через CSS-змінні;
типографіка залишається читабельною на різних екранах;
декоративні елементи не заважають скринридерам;
користувач розуміє призначення кожної кнопки та секції;
код можна змінювати без масового копіювання стилів.
Завершений адаптивний сайт — це не просто сторінка, яка візуально нагадує макет. Якісний результат поєднує:
логічну HTML-семантику;
передбачуване компонування;
адаптивну сітку;
читабельну типографіку;
послідовну систему стилів;
доступність для клавіатури та допоміжних технологій;
перевірку на реальних розмірах екрана.
Працюйте послідовно: спочатку визначте структуру й контент, потім реалізуйте компонування, після цього додайте візуальні деталі та завершіть перевіркою доступності й адаптивності.