Subgrid
Сетка Grid выравнивает своих прямых детей. Но внутри карточки — свои элементы: заголовок, текст, кнопка, и о соседних карточках они ничего не знают. Если у одной карточки заголовок в две строки, её кнопка съезжает ниже, чем у соседей. Subgrid (подсетка) решает это: вложенная сетка берёт строки или колонки у родительской.
Проблема
Три карточки в сетке, у каждой своя внутренняя сетка. Заголовки разной длины, и всё ниже них разъезжается:
<style>
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.card { display: grid; gap: 8px; padding: 12px; background: lavender; }
.card h3 { margin: 0; font-size: 16px; }
.card p { margin: 0; }
</style>
<div class="cards">
<div class="card"><h3>Капучино</h3><p>Эспрессо и молочная пена.</p><button>В корзину</button></div>
<div class="card"><h3>Раф с ванильным сахаром и сливками</h3><p>Нежный и сладкий.</p><button>В корзину</button></div>
<div class="card"><h3>Латте</h3><p>Много молока.</p><button>В корзину</button></div>
</div>Текст и кнопка второй карточки стоят ниже, чем у соседей: каждая карточка считает свои строки сама.
grid-template-rows: subgrid
Решение — две строчки. Карточка занимает в родительской сетке три строки (grid-row: span 3), а вместо своих строк берёт эти три: grid-template-rows: subgrid:
<style>
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
gap: 8px;
padding: 12px;
background: lavender;
}
.card h3 { margin: 0; font-size: 16px; }
.card p { margin: 0; }
</style>
<div class="cards">
<div class="card"><h3>Капучино</h3><p>Эспрессо и молочная пена.</p><button>В корзину</button></div>
<div class="card"><h3>Раф с ванильным сахаром и сливками</h3><p>Нежный и сладкий.</p><button>В корзину</button></div>
<div class="card"><h3>Латте</h3><p>Много молока.</p><button>В корзину</button></div>
</div>Теперь строка заголовков одной высоты у всех карточек: по самому длинному. Текст и кнопки выстроились в линию.
Как это работает
Строки теперь принадлежат родителю .cards, а карточка лишь раскладывает своих детей по ним. Высоту каждой строки браузер считает по всем карточкам сразу. Поэтому число в span должно совпадать с числом частей карточки: три части — span 3.
| Свойство карточки | Зачем |
|---|---|
display: grid | карточка — тоже сетка |
grid-row: span 3 | занять три строки родителя |
grid-template-rows: subgrid | взять эти строки себе |
gap | свой промежуток между частями |
gap подсетка наследует от родителя, но его можно переопределить, как в примере.
Колонки тоже
grid-template-columns: subgrid выравнивает колонки. Так делают форму, где все подписи одной ширины, хотя каждая пара «подпись — поле» лежит в своём блоке:
<style>
.form { display: grid; grid-template-columns: max-content 1fr; gap: 8px; max-width: 400px; }
.row { display: grid; grid-column: span 2; grid-template-columns: subgrid; align-items: center; }
</style>
<div class="form">
<div class="row"><label for="n">Имя</label><input id="n"></div>
<div class="row"><label for="e">Электронная почта</label><input id="e"></div>
</div>max-content делает первую колонку шириной по самой длинной подписи, и оба поля начинаются на одной линии.
Поддержка
Subgrid работает во всех основных браузерах с 2023 года. В старом браузере subgrid не распознаётся, карточка становится обычной сеткой, и вёрстка выглядит как в первом примере — неровно, но не сломано.
Частая ошибка
Забыли grid-row: span 3. Карточка занимает в родителе одну строку, и подсетке достаётся только она. Лишним частям места нет — они наезжают друг на друга:
<style>
.cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.card { display: grid; grid-template-rows: subgrid; padding: 12px; background: mistyrose; }
.card h3, .card p { margin: 0; }
</style>
<div class="cards">
<div class="card"><h3>Капучино</h3><p>Эспрессо и пена.</p><button>В корзину</button></div>
<div class="card"><h3>Латте</h3><p>Много молока.</p><button>В корзину</button></div>
</div>subgrid всегда идёт в паре со span на нужное число строк или колонок.