Адаптивный каталог на Grid: карточки от одной до четырёх колонок
- 4 августа 2026
Соберём каталог, который показывает одну колонку на узком экране и максимум четыре на широком. Проверим длинные названия и увеличенный текст, а затем выберем между явными медиавыражениями и автоматическим подбором колонок.
Начните с содержимого и границ задачи
Для примера нужны браузер, редактор и базовые знания HTML и CSS. Создайте рядом index.html и styles.css. Сервер, сборщик и JavaScript здесь не нужны: HTML можно открыть как локальный файл.
Наше условие: порядок карточек сохраняется, их ширина распределяется поровну, горизонтальной прокрутки страницы нет. Четыре колонки — верхняя граница по макету. Число карточек при этом может быть любым: лишние переходят в следующий ряд.
Сначала используем явные медиавыражения. Для каталога с заданными состояниями макета мы рекомендуем этот вариант: число колонок видно в коде, и широкое окно не добавит пятую. Автоматический вариант разберём отдельно.
Если термины «линия сетки» и «ячейка» пока незнакомы, начните с разбора display: grid. Здесь сосредоточимся на ширине карточек и устойчивости содержимого.
Создайте страницу с шестью карточками
Вставьте в index.html этот пример. Товары и цены вымышлены для упражнения. У второй карточки длинное название, у третьей — артикул без пробелов. Они нужны для проверки переноса.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Учебный каталог</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main class="page">
<h1>Лампы для рабочего стола</h1>
<ul class="catalog">
<li class="card">
<h2>Лампа «Луч»</h2>
<p>Тёплый свет и металлическая стойка.</p>
<p class="card__price">2400 ₽</p>
</li>
<li class="card">
<h2>Лампа с регулируемой высотой для большого рабочего стола</h2>
<p>Поворотный плафон. Учебное описание товара.</p>
<p class="card__price">3900 ₽</p>
</li>
<li class="card">
<h2>Лампа «Точка»</h2>
<p>Артикул: DESKLAMP2026ADJUSTABLEBLACKMODEL.</p>
<p class="card__price">3100 ₽</p>
</li>
<li class="card">
<h2>Лампа «Дуга»</h2>
<p>Для узкого стола рядом с окном.</p>
<p class="card__price">2800 ₽</p>
</li>
<li class="card">
<h2>Лампа «Круг»</h2>
<p>Матовый плафон рассеивает свет.</p>
<p class="card__price">3600 ₽</p>
</li>
<li class="card">
<h2>Лампа «Вектор»</h2>
<p>Длинное описание может занимать несколько строк.</p>
<p class="card__price">4200 ₽</p>
</li>
</ul>
</main>
</body>
</html>Список связывает карточки в один каталог, а заголовок каждой карточки помогает различать товары. Весь контент остаётся в исходном порядке. Пока не задавайте фиксированную высоту: длинное название должно увеличить карточку, сохранив текст.
Задайте одну колонку и добавляйте место постепенно
В styles.css запишите полный набор стилей:
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #202020;
background: #f4f4f4;
font: 18px/1.5 system-ui, sans-serif;
}
.page {
max-width: 80rem;
margin-inline: auto;
padding: 1rem;
}
.catalog {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1rem;
margin: 0;
padding: 0;
list-style: none;
}
.card {
display: flex;
flex-direction: column;
min-width: 0;
padding: 1.25rem;
border: 1px solid #777;
background: #fff;
overflow-wrap: anywhere;
}
.card h2 {
margin: 0 0 0.75rem;
font-size: 1.2rem;
}
.card p {
margin: 0 0 1rem;
}
.card .card__price {
margin-top: auto;
margin-bottom: 0;
font-weight: 700;
}
@media (min-width: 40rem) {
.catalog {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 60rem) {
.catalog {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (min-width: 76rem) {
.catalog {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}Откройте страницу. До первого порога карточки стоят одна под другой. Затем появляются две колонки, позже три, а при ширине окна от 76rem — четыре. Порог зависит от ширины окна браузера, а не названия устройства. Это обычные media queries, или медиавыражения.
Числа в примере выбраны для этих учебных карточек. В вашем макете подбирайте порог по содержимому: сужайте окно, пока заголовки и текст остаются читаемыми. Назначать отдельное состояние «для планшета» без такой проверки недостаточно.
repeat(4, ...) повторяет описание колонки четыре раза. Единица fr распределяет свободное место между колонками: при одинаковом 1fr они получают равные доли. Интервалы gap учитываются отдельно. Описание grid-template-columns.
У карточки цена прижата к нижнему краю через внутренний Flexbox. На количество колонок это не влияет. В каждом ряду высоту задаёт самая высокая карточка, поэтому цены соседних товаров выравниваются, а следующий ряд может иметь другую высоту.
Разберитесь с минимумом колонки
minmax(0, 1fr) задаёт диапазон: минимальный размер колонки равен нулю, максимальный определяется её долей свободного пространства. Благодаря этому длинное содержимое не диктует сетке обязательную большую ширину. Текст внутри всё равно должен уметь переноситься. Как работает minmax().
Частая замена — repeat(4, 1fr). Для гибкой колонки такой минимум связан с содержимым. Неразрывный артикул или широкий дочерний элемент способны растянуть сетку. В нашем примере мы явно задаём минимум полосы и разрешаем перенос длинных последовательностей через overflow-wrap: anywhere.
min-width: 0 у карточки снимает её автоматическое ограничение минимальной ширины. Эти свойства закрывают разные уровни: размер колонки, размер элемента и перенос текста. Нужна вся связка.
Нулевой минимум не означает, что карточка будет удобна при любой ширине. Если выставить четыре колонки на экране 320 пикселей, текст останется в полосах, но каждая станет слишком узкой. Поэтому адаптивность включает ещё и выбор количества колонок. Проверьте и ширину, и удобство чтения: сетка может помещаться на экране, хотя слова разбиты на слишком короткие строки.
С изображениями действует тот же принцип. Когда добавите реальные фотографии, задайте им max-width: 100% и подходящие размеры, а затем проверьте пропорции. Не компенсируйте широкое содержимое скрытием переполнения всей страницы: так можно потерять часть карточки.
Попробуйте auto-fit с ограничением числа колонок
Для свободной сетки можно поручить подбор числа колонок браузеру. Удалите три медиавыражения из примера и замените правило .catalog следующим:
.catalog {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, 16rem), 1fr)
);
gap: 1rem;
max-width: 67rem;
margin: 0 auto;
padding: 0;
list-style: none;
}auto-fit повторяет колонки, которые помещаются, а незанятые полосы схлопывает. Карточки получают освободившееся место. auto-fill сохраняет место для пустых полос; разницу особенно видно, если товаров мало. Автоматическое повторение и пустые полосы.
Минимум min(100%, 16rem) выбирает меньшее из ширины контейнера и 16rem. На узкой странице одна колонка поэтому может стать меньше 16rem. Без внутреннего min() жёсткий минимум способен создать переполнение, когда доступной ширины недостаточно даже для одной карточки.
Теперь верхняя граница. Четыре карточки по 16rem и три интервала по 1rem занимают 67rem. Мы ограничили этой величиной сам каталог. Пятая колонка с тем же минимумом и дополнительным интервалом потребовала бы больше места, поэтому она не появится. Если поменяете минимум или gap, пересчитайте ограничение.
Проверьте ограничение. Сам по себе auto-fit не означает «максимум четыре»: без ограничения контейнера широкое окно даст больше колонок. При двух товарах автоматическая сетка может растянуть их на весь каталог, тогда как явная четырёхколоночная оставит незанятые ячейки. Выбирайте ожидаемое поведение заранее.
| Условие макета | Подход |
|---|---|
| В каждом диапазоне нужно строго определённое число колонок | Явные медиавыражения и repeat с числом. |
| Число колонок должно следовать доступной ширине карточек | auto-fit и осмысленный минимум. |
| Пустые места должны сохраняться при малом числе товаров | Явная сетка либо auto-fill. |
| Колонок должно быть не больше четырёх | Явное число или рассчитанный предел ширины контейнера. |
Проверьте каталог на данных, которые неудобны макету
В инструментах разработчика включите подсветку Grid и смотрите на линии колонок. Для варианта с медиавыражениями проверьте ширины 320, 768, 1024 и 1440 пикселей. Считайте колонки: одну на первом размере, две на втором, затем три и, наконец, четыре при самом широком окне. Если настройки пользователя меняют размеры, оцените результат по фактическим порогам rem.
Проверьте границы переходов. На границе полезнее увидеть перенос текста, чем только посчитать карточки.
- У страницы нет горизонтальной прокрутки на ширине 320 пикселей.
- Длинное название и артикул видны полностью.
- При увеличении масштаба браузера до 200% текст сохраняется.
- При двух, шести и 13 карточках порядок не меняется.
- Ни одно широкое окно не создаёт пятую колонку.
- Цены остаются в карточках, а длинный текст увеличивает высоту.
Для самостоятельной задачи добавьте строку наличия над ценой и проверьте три варианта текста: короткий, длинный и отсутствующий. Затем поместите каталог рядом с боковой панелью. Медиавыражение всё ещё смотрит на окно, хотя места в колонке стало меньше; это повод пересмотреть пороги или изучить выражения от контейнера.
Развивайте каталог в адаптивный интерфейс
Сохраните один выбранный вариант сетки и таблицу проверенных ширин. С этим результатом переходите к программе курса «Адаптивная вёрстка» и сверяйте входные требования. Для дальнейшей практики полезно уже уметь объяснить минимум колонки, ограничение четырьмя карточками и поведение длинного содержимого.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.