Настоящая магия выравнивания: укрощаем CSS Grid Subgrid
Присаживайся поудобнее, наливай кофе. Сегодня мы разберем технологию, которая раз и навсегда закрыла один из самых древних и раздражающих споров в веб-разработке: как идеально выровнять разнородный контент в соседних карточках без костылей на JavaScript и без боли в CSS. Речь пойдет о CSS Grid Subgrid.
Представь классическую задачу: сетка из трех карточек тарифов. В каждой есть заголовок, список фич и кнопка внизу. В первом тарифе заголовок в одну строку, во втором — в три. Дизайнер хочет, чтобы заголовки во всех карточках были одинаковой высоты (по самому длинному), списки фич начинались на одном уровне, а кнопки идеально выстраивались по нижней границе. Раньше это было головной болью. Сегодня это решается парой строк кода.
Как мы страдали раньше
До полноценного внедрения Subgrid у нас было три пути, и все три — так себе:
- Хардкод фиксированной высоты. Мы писали что-то вроде
min-height: 80pxдля заголовков. Но ломался масштаб текста, прилетал реальный контент от контент-менеджера, и всё благополучно портилось, вылезая за границы. - Скрипты на JS. Использование
ResizeObserverили старого доброго перебора элементов с вычислением максимальной высоты. Это вызывало микро-дергания интерфейса при загрузке (Layout Shifts) и нагружало поток отрисовки. - Разрушение семантики. Мы дробили карточки на куски. Вместо нормальной структуры карточки мы делали строку заголовков, строку списков и строку кнопок. Экранные читалки (screen readers) после такого просто сходили с ума, а поддерживать этот HTML-винегрет было невозможно.
О том, какие еще ухищрения нам приходилось использовать для построения сложных макетов, ты можешь вспомнить в нашей статье про продвинутый CSS Grid и создание журнальных сеток. Но времена изменились.
Как делать правильно в 2026 году
В современном CSS Grid элемент сетки может сам стать грид-контейнером. Но без subgrid его внутренняя сетка никак не связана с родительской. Свойство subgrid позволяет дочернему элементу сказать: «Эй, я не буду придумывать свои колонки или строки. Я просто возьму те, на которых лежу в родительском гриде».
Благодаря этому карточка занимает, например, три строки родительского грида, а ее внутренние элементы (заголовок, текст, кнопка) намертво привязываются к этим строкам. Если заголовок во второй карточке растянет первую строку родителя, заголовки в первой и третьей карточках автоматически растянутся вслед за ним. Чистая декларативная магия!
Чтобы верстка оставалась по-настоящему адаптивной и гибкой на любых экранах, мы часто сочетаем эту технику с умным расчетом размеров. Рекомендую глянуть, как работают математические функции в CSS, чтобы круто настраивать адаптивные отступы и размеры грид-сетки без единого медиа-запроса.
Готовый сниппет кода
Давай закодим это. Вот максимально простой и понятный пример, который ты можешь скопировать и запустить прямо сейчас:
<div class="grid-container">
<!-- Карточка 1 -->
<article class="card">
<h3>Короткий заголовок</h3>
<p>Простое описание в одну строку.</p>
<button>Купить</button>
</article>
<!-- Карточка 2 -->
<article class="card">
<h3>Очень длинный заголовок, который переносится на несколько строк</h3>
<p>Здесь у нас гораздо больше текста, который детально описывает все преимущества данного выбора.</p>
<button>Купить</button>
</article>
<!-- Карточка 3 -->
<article class="card">
<h3>Средний заголовок</h3>
<p>Среднее по длине описание продукта.</p>
<button>Купить</button>
</article>
</div>
<style>
.grid-container {
display: grid;
/* 3 колонки, а по строкам: каждая карточка будет занимать ровно 3 строки родителя */
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto;
gap: 20px;
}
.card {
display: grid;
/* Карточка растягивается на 3 строки родительского грида */
grid-row: span 3;
/* А теперь магия: строки этой карточки привязываются к строкам родителя */
grid-template-rows: subgrid;
background: #f4f4f9;
padding: 15px;
border-radius: 8px;
}
.card h3 {
margin: 0;
background: #e2e8f0;
}
.card p {
margin: 0;
}
.card button {
align-self: start;
padding: 10px;
background: #3182ce;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Частая ошибка новичков
Самый частый затык у тех, кто только начинает щупать сабгриды — это забыть указать grid-row или grid-column на самом элементе-сабгриде.
Помни: свойство grid-template-rows: subgrid не будет работать, если браузер не понимает, сколько именно строк родительской сетки должен занять этот элемент. Если ты не укажешь явно grid-row: span X (где X — количество внутренних элементов карточки), то элемент по умолчанию займет всего одну строку. В итоге все твои внутренние теги сожмутся в этой единственной строке, и верстка превратится в кашу.
Всегда соотноси количество дочерних элементов в подсетке с числом span в позиционировании контейнера-сабгрида!
🔥 Больше фишек, готовых сниппетов и передовых подходов к CSS мы публикуем в нашем Telegram-канале. Подписывайтесь, чтобы не пропустить!