Пошук уроків, статей та іншого контенту
Застосуємо CSS Grid для адаптивної картки з колонками, рядками, проміжками та перебудовою на вузьких екранах.
У цьому практичному прикладі створимо адаптивний блок карток за допомогою CSS Grid. Макет матиме:
кілька колонок на широкому екрані;
автоматичну зміну кількості колонок;
однакові проміжки між картками;
картку, яка займає більше місця;
перебудову на вузьких екранах;
внутрішню сітку для вмісту кожної картки.
CSS Grid добре підходить для макетів, у яких важливі одночасно і рядки, і колонки.
Створимо контейнер із картками курсів. Перша картка буде розширеною та займатиме дві колонки.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Адаптивна сітка карток</title>
<style>
:root {
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #172033;
background: #eef2f7;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.page {
width: min(100% - 32px, 1120px);
margin: 0 auto;
padding: 48px 0;
}
.page-header {
max-width: 680px;
margin-bottom: 32px;
}
.page-header h1 {
margin: 0 0 12px;
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.05;
}
.page-header p {
margin: 0;
color: #5f6b7a;
font-size: 1.05rem;
line-height: 1.6;
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
grid-auto-rows: minmax(220px, auto);
gap: 20px;
align-items: stretch;
}
.card {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 20px;
min-width: 0;
padding: 24px;
border: 1px solid #dbe2eb;
border-radius: 20px;
background: #ffffff;
box-shadow: 0 12px 30px rgb(28 43 69 / 8%);
}
.card--featured {
grid-column: span 2;
background: #172033;
color: #ffffff;
}
.card__type {
margin: 0 0 8px;
color: #64748b;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.card--featured .card__type {
color: #a9c7ff;
}
.card__title {
margin: 0;
font-size: clamp(1.35rem, 2vw, 2rem);
line-height: 1.15;
}
.card__description {
margin: 0;
color: #5f6b7a;
line-height: 1.6;
}
.card--featured .card__description {
color: #d2d9e5;
}
.card__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-top: 16px;
border-top: 1px solid #e5eaf0;
}
.card--featured .card__footer {
border-top-color: #3a465c;
}
.card__level {
color: #64748b;
font-size: 0.9rem;
}
.card--featured .card__level {
color: #c3cede;
}
.card__link {
color: #2563eb;
font-weight: 700;
text-decoration: none;
white-space: nowrap;
}
.card--featured .card__link {
color: #a9c7ff;
}
.card__link:hover,
.card__link:focus-visible {
text-decoration: underline;
}
@media (max-width: 720px) {
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.card--featured {
grid-column: 1 / -1;
}
}
@media (max-width: 480px) {
.page {
width: min(100% - 24px, 1120px);
padding: 32px 0;
}
.cards {
grid-template-columns: 1fr;
gap: 14px;
}
.card,
.card--featured {
grid-column: auto;
padding: 20px;
}
.card__footer {
align-items: flex-start;
flex-direction: column;
}
}
</style>
</head>
<body>
<main class="page">
<header class="page-header">
<h1>Навчальні програми</h1>
<p>
Оберіть напрям, щоб поглибити знання у веброзробці та створенні
сучасних інтерфейсів.
</p>
</header>
<section class="cards" aria-label="Список навчальних програм">
<article class="card card--featured">
<div>
<p class="card__type">Основний курс</p>
<h2 class="card__title">Fullstack JavaScript</h2>
</div>
<p class="card__description">
Практичний маршрут від основ JavaScript до створення клієнтських і
серверних застосунків.
</p>
<div class="card__footer">
<span class="card__level">Рівень: advanced</span>
<a class="card__link" href="#javascript">Переглянути курс</a>
</div>
</article>
<article class="card">
<div>
<p class="card__type">Технологія</p>
<h2 class="card__title">HTML і CSS</h2>
</div>
<p class="card__description">
Семантична розмітка, адаптивні компоненти та сучасні методи
компонування сторінок.
</p>
<div class="card__footer">
<span class="card__level">Рівень: intermediate</span>
<a class="card__link" href="#html-css">Деталі</a>
</div>
</article>
<article class="card">
<div>
<p class="card__type">Технологія</p>
<h2 class="card__title">React</h2>
</div>
<p class="card__description">
Компонентний підхід, стан застосунку та побудова інтерактивних
інтерфейсів.
</p>
<div class="card__footer">
<span class="card__level">Рівень: advanced</span>
<a class="card__link" href="#react">Деталі</a>
</div>
</article>
<article class="card">
<div>
<p class="card__type">Технологія</p>
<h2 class="card__title">Node.js</h2>
</div>
<p class="card__description">
Створення серверних застосунків, робота з API та обробка даних.
</p>
<div class="card__footer">
<span class="card__level">Рівень: advanced</span>
<a class="card__link" href="#node">Деталі</a>
</div>
</article>
<article class="card">
<div>
<p class="card__type">Інструмент</p>
<h2 class="card__title">Git</h2>
</div>
<p class="card__description">
Керування версіями, командна робота та організація історії проєкту.
</p>
<div class="card__footer">
<span class="card__level">Рівень: beginner</span>
<a class="card__link" href="#git">Деталі</a>
</div>
</article>
</section>
</main>
</body>
</html>Збережіть код у файл index.html і відкрийте його в браузері. Розмір вікна можна змінювати, щоб побачити перебудову сітки.
Основний контейнер карток має клас .cards:
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
grid-auto-rows: minmax(220px, auto);
gap: 20px;
}display: gridВластивість display: grid перетворює елемент на grid-контейнер. Його дочірні елементи, у цьому випадку .card, стають grid-елементами.
grid-template-columnsЦей рядок описує колонки:
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));Він складається з кількох частин:
repeat(...) повторює визначення колонки;
auto-fit створює стільки колонок, скільки поміщається в доступному просторі;
minmax(...) задає мінімальний і максимальний розмір;
260px — бажана мінімальна ширина картки;
1fr — частка вільного простору.
Функція min(100%, 260px) не дозволяє мінімальному розміру колонки бути ширшим за контейнер. Це важливо, коли ширина екрана менша за 260px: картка не створює горизонтальне переповнення.
На широкому екрані можуть утворитися чотири колонки:
[картка] [картка] [картка] [картка]Якщо доступної ширини недостатньо, браузер автоматично зменшить кількість колонок:
[картка] [картка] [картка]Або:
[картка] [картка]grid-auto-rowsgrid-auto-rows: minmax(220px, auto);Ця властивість визначає розмір автоматично створених рядків.
220px — мінімальна висота рядка;
auto — рядок може стати вищим, якщо його вміст цього потребує.
Не варто задавати карткам фіксовану висоту на кшталт height: 220px. Текст може бути довшим, а фіксована висота призведе до переповнення або обрізання вмісту.
gapgap: 20px;gap задає проміжок і між колонками, і між рядками. Це короткий запис для:
column-gap: 20px;
row-gap: 20px;Якщо горизонтальний і вертикальний проміжки мають бути різними, можна записати:
gap: 16px 24px;Перше значення відповідає проміжку між рядками, друге — між колонками.
Розширена картка має модифікатор .card--featured:
.card--featured {
grid-column: span 2;
}span 2 означає, що елемент має зайняти дві колонки, починаючи з позиції, яку вибере алгоритм Grid.
Це відрізняється від:
grid-column: 1 / 3;Запис 1 / 3 явно вказує початкову та кінцеву лінії сітки. Він прив’язує елемент до конкретної позиції. Запис span 2 визначає лише кількість колонок, тому краще підходить для автоматично заповнюваної сітки.
На широкому екрані результат може виглядати так:
[ розширена картка ] [картка]
[картка] [картка] [картка] [картка]Фактичне розташування залежить від доступної ширини та кількості колонок, яку зміг створити браузер.
Кожна картка також є grid-контейнером:
.card {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 20px;
}Три рядки відповідають трьом частинам картки:
заголовку;
опису;
нижній панелі з рівнем і посиланням.
Середній рядок має 1fr, тому він займає весь вільний простір. Завдяки цьому нижня панель розташовується внизу картки, навіть якщо описи мають різну довжину.
┌────────────────────┐
│ Заголовок │ ← auto
│ │
│ Опис │ ← 1fr
│ │
├────────────────────┤
│ Рівень Посилання │ ← auto
└────────────────────┘Такий підхід надійніший, ніж додавання довільних margin-top до нижньої панелі.
.cards {
align-items: stretch;
}Значення stretch розтягує grid-елементи по висоті рядка. У результаті картки в одному рядку мають однакову висоту.
Для внутрішньої нижньої панелі використано:
.card__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}Тут Grid відповідає за структуру картки, а Flexbox — за горизонтальне розташування двох елементів усередині нижньої панелі. Це нормальне поєднання: Grid зручно використовувати для двовимірного макета, а Flexbox — для одновимірного вирівнювання.
Автоматичний auto-fit уже адаптує кількість колонок, але для розширеної картки потрібні додаткові правила.
@media (max-width: 720px) {
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.card--featured {
grid-column: 1 / -1;
}
}На екранах до 720px створюються дві колонки. Розширена картка займає весь рядок:
grid-column: 1 / -1;Тут:
1 — перша лінія сітки;
-1 — остання лінія сітки.
Отже, картка розтягується від початку до кінця рядка:
[ розширена картка ]
[картка] [картка]
[картка] [картка]@media (max-width: 480px) {
.cards {
grid-template-columns: 1fr;
}
.card,
.card--featured {
grid-column: auto;
}
}На вузькому екрані всі картки розташовуються в одну колонку. Для .card--featured скидаємо попереднє розтягування:
grid-column: auto;Це не є обов’язковим для візуального результату в одноколоночній сітці, але робить правило явним і не залишає залежності від попереднього розташування.
Нижня панель картки також переходить у вертикальний режим:
.card__footer {
align-items: flex-start;
flex-direction: column;
}Це запобігає ситуації, коли довгий текст або посилання не поміщаються по ширині.
auto-fit, а коли медіазапитиДля типових карток часто достатньо автоматичної сітки:
.cards {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}Цей варіант добре працює, коли кількість колонок не має бути строго визначена.
Медіазапити потрібні, якщо на певній ширині необхідно гарантувати конкретну структуру:
@media (max-width: 720px) {
.cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 480px) {
.cards {
grid-template-columns: 1fr;
}
}У прикладі поєднано обидва підходи:
на великих екранах кількість колонок визначається автоматично;
на середніх і малих екранах структура задається явно;
розширена картка отримує окремі правила для кожного режиму.
Під час перевірки змініть ширину вікна браузера та переконайтеся, що:
картки не виходять за межі контейнера;
між картками зберігаються однакові проміжки;
розширена картка займає дві колонки на широкому екрані;
на середній ширині розширена картка займає весь рядок;
на малому екрані картки розташовані в одну колонку;
текст усередині карток не обрізається;
нижня панель не створює горизонтального переповнення.
Для налагодження Grid у DevTools можна вибрати контейнер .cards і ввімкнути відображення grid-ліній. Це допоможе побачити номери ліній, фактичні колонки та області, які займають елементи.
Невдалий варіант:
.cards {
grid-template-columns: 300px 300px 300px;
}Такий макет швидко створює горизонтальне переповнення на вузьких екранах.
Краще використовувати гнучкі колонки:
.cards {
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}min-width: 0Grid-елемент за замовчуванням може не стискатися менше за мінімальний розмір свого вмісту. Через це довгий текст або інший невміщуваний контент може розширити колонку.
У картці варто явно додати:
.card {
min-width: 0;
}Невдалий варіант:
.card {
height: 220px;
}Вміст карток може мати різну довжину. Використовуйте автоматичну висоту рядків:
.cards {
grid-auto-rows: minmax(220px, auto);
}grid-columnЯкщо для кожної картки вручну задавати позиції:
.card:nth-child(1) {
grid-column: 1 / 3;
}
.card:nth-child(2) {
grid-column: 3 / 4;
}сітка стає крихкою. Після зміни кількості колонок такі позиції можуть перестати відповідати макету.
Для автоматичної сітки достатньо задавати особливі правила лише тим елементам, які справді відрізняються:
.card--featured {
grid-column: span 2;
}Не потрібно додавати порожні елементи лише для переходу на новий рядок. Grid сам розміщує елементи в наступному доступному місці. Якщо елемент має зайняти весь рядок, використовуйте:
.card--featured {
grid-column: 1 / -1;
}display: grid створює двовимірний контейнер із рядками та колонками.
repeat(auto-fit, minmax(...)) дозволяє автоматично змінювати кількість колонок.
gap задає проміжки між рядками та колонками.
grid-auto-rows: minmax(...) дає рядкам мінімальну висоту без жорсткого обмеження.
grid-column: span 2 розтягує елемент на дві колонки.
grid-column: 1 / -1 розтягує елемент на всю ширину сітки.
Внутрішня сітка grid-template-rows: auto 1fr auto допомагає закріпити нижню панель картки внизу.
На вузьких екранах картки можна перевести спочатку у дві, а потім в одну колонку.
min-width: 0 допомагає уникати горизонтального переповнення вмістом.