29 Модуль 6 Современный CSS 17 мин урок 4 из 25

Subgrid и сложные сетки

Почему кнопки в карточках стоят на разной высоте и как заставить вложенные сетки подчиняться общим строкам.

Что ты научишься делать

  • Поймёшь, почему вложенная сетка не выравнивается с соседями
  • Включишь subgrid по строкам и получишь ровные заголовки и кнопки
  • Разложишь большой макет на именованные области и линии

#Знакомая неровность

Три карточки в ряд. У первой заголовок в одну строку, у второй — в три. Кнопки внизу оказываются на разной высоте, и весь ряд выглядит неопрятно.

Причина не в ошибке. Каждая карточка — своя сетка, со своими строками. Родительский грид знает только про колонки, а что происходит внутри карточек, ему безразлично.

subgrid отменяет эту изоляцию: карточка перестаёт заводить свои строки и встраивается в строки родителя.

Интерактивный разбор доступен при включённом JavaScript.

#Как это пишется

Нужны две вещи. Родитель объявляет строки, а ребёнок занимает их все сразу и говорит, что берёт строки родителя.

CSScards.css
.cards {  display: grid;  grid-template-columns: repeat(3, 1fr);  /* Четыре строки: заголовок, текст, цена, кнопка */  grid-template-rows: auto 1fr auto auto;  gap: 16px;}.card {  display: grid;  /* Карточка растягивается на все четыре строки родителя… */  grid-row: span 4;  /* …и берёт их вместо своих */  grid-template-rows: subgrid;}
Кнопки встают в линию
В песочницу

#Subgrid по колонкам

Тот же приём работает и по горизонтали. Классический случай — форма, где подписи и поля должны стоять в две ровные колонки, а каждая пара при этом остаётся отдельным блоком со своей рамкой.

CSSform.css
.form {  display: grid;  /* Колонка подписей ровно по самой длинной из них */  grid-template-columns: auto 1fr;  gap: 12px;}.form__row {  display: grid;  grid-column: span 2;  grid-template-columns: subgrid;  align-items: center;}

#Именованные линии и области

В большом макете считать номера линий невозможно: добавил колонку — и все числа поехали. Линиям и областям дают имена.

CSSpage.css
.page {  display: grid;  grid-template-columns:    [full-start] minmax(16px, 1fr)    [content-start] minmax(0, 1100px) [content-end]    minmax(16px, 1fr) [full-end];}/* Обычный блок живёт в колонке контента */.page > * {  grid-column: content;}/* А этот разворачивается во всю ширину экрана */.hero {  grid-column: full;}

Приём называют «полноширинный прорыв»: сетка страницы задаётся один раз, и любой блок может выйти на всю ширину одной строкой, без отрицательных отступов и обёрток.

Пара имён content-start и content-end автоматически даёт область content — писать grid-column: content достаточно.

#minmax, auto-fit и auto-fill

Две функции, без которых большие сетки не собираются. minmax() задаёт вилку: «не уже такого, но тянись до такого». repeat() с ключевыми словами позволяет не указывать количество колонок вообще.

Разница между auto-fit и auto-fill сбивает с толку всех, а видна только когда элементов мало.

ЗаписьПоведение при трёх карточках в широком ряду
repeat(auto-fill, minmax(240px, 1fr))Создаёт колонки под всю ширину, лишние остаются пустыми. Карточки не растягиваются
repeat(auto-fit, minmax(240px, 1fr))Пустые колонки схлопываются, три карточки растягиваются на всю ширину

Правило выбора простое: если хочешь, чтобы неполный ряд растянулся, — auto-fit. Если карточки должны сохранять ширину и прижиматься к началу, — auto-fill.

#Как это выглядит в реальном проекте

CSScatalog.css
@layer components {  .catalog {    display: grid;    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));    /* Строки перечислены явно — иначе subgrid не к чему привязать */    grid-auto-rows: auto 1fr auto auto;    gap: 20px;  }  .product {    display: grid;    grid-row: span 4;    grid-template-rows: subgrid;    gap: 8px;  }  /* Длинные названия больше не ломают ряд:     кнопки и цены стоят на одной линии во всех карточках */  .product__title { margin: 0; font-size: 1rem; }  .product__price { align-self: end; font-weight: 700; }}

#Отладка сеток

Сетку почти невозможно отлаживать по коду — её нужно видеть. В инструментах разработчика рядом с элементом, у которого стоит display: grid, появляется значок grid. Включи его.

Браузер нарисует поверх страницы линии сетки с номерами и именами. Сразу видно, где линия на самом деле проходит, почему блок занял не ту ячейку и куда делось пустое место. У вложенного subgrid линии продолжают родительские — это лучший способ убедиться, что подчинение действительно включилось.

#Про masonry

Раскладка «кирпичной кладкой», где карточки разной высоты подтягиваются вверх без просветов, долго существовала только на скриптах.

В CSS она обсуждается и уже есть в некоторых браузерах за флагом или в экспериментальном виде, но единого решения пока нет: спецификация менялась несколько раз. На неё сегодня не закладываются — если такая раскладка нужна в работе, берут колоночный вариант через columns или скрипт.

#Когда НЕ надо

  • Когда хватает align-items: stretch. Если внутри карточки один-два элемента, ровные кнопки получаются обычным флексом с margin-top: auto. Subgrid — для случая, когда линий несколько.
  • Для двухуровневой вложенности «на всякий случай». Subgrid связывает ребёнка с родителем жёстко: поменяешь строки у родителя — придётся править и grid-row: span у всех детей.
  • Ради выравнивания текста внутри одной карточки. Это задача обычной сетки, лишний уровень тут ничего не даст.

#Поддержка браузерами

ВозможностьСостояние
subgridFirefox первым, с 2019; Safari и Chrome — к концу 2023. Сегодня во всех современных браузерах
Именованные линии и областиПоддерживаются везде с самого появления grid
masonryЕдиного решения нет, спецификация в работе. В продакшене не использовать
CSSФоллбэк на flex
/* База: работает всюду, кнопки прижаты вниз */.card { display: flex; flex-direction: column; }.card__btn { margin-top: auto; }/* Улучшение: строки выравниваются между карточками */@supports (grid-template-rows: subgrid) {  .card {    display: grid;    grid-row: span 4;    grid-template-rows: subgrid;  }}

Где спотыкаются почти все

Написан subgrid без grid-row: span

Почему Ребёнок занимает одну строку родителя, подчиняться нечему. Внешне ничего не меняется, и кажется, что subgrid «не работает».

Как надо Сколько строк перечислено у родителя — столько занимает ребёнок: grid-row: span 4.

У родителя строки не заданы явно

Почему Если строк нет в grid-template-rows или grid-auto-rows, subgrid не к чему привязаться.

Как надо Перечисли строки у родителя — по одной на каждый ряд содержимого карточки.

gap задан и у родителя, и у ребёнка

Почему При subgrid ребёнок наследует расстояния родителя по этой оси. Свой gap перекроет их, и выравнивание поедет.

Как надо Задавай gap по подчинённой оси только у родителя.

Ставка на masonry в рабочем проекте

Почему Спецификация ещё меняется, поведение в браузерах разное. Код придётся переписывать.

Как надо Пока бери columns или скрипт, а masonry держи на заметке.

Subgrid subgrid
Значение grid-template-rows и grid-template-columns: вложенная сетка берёт линии родителя вместо своих.
Именованная линия named grid line
Имя в квадратных скобках в grid-template-columns. Позволяет не считать номера.
Полноширинный прорыв full-bleed
Приём: блок выходит за колонку контента на всю ширину экрана средствами сетки.
Masonry masonry
Раскладка кирпичной кладкой без просветов. В CSS пока не стандартизована.

Проверь себя

  1. Почему кнопки в карточках стоят на разной высоте без subgrid?

    Родительский грид управляет только карточками целиком. Что внутри — забота каждой карточки отдельно.

  2. Что обязательно нужно вместе с <code>grid-template-rows: subgrid</code>?

    Ребёнок должен занять столько строк родителя, сколько собирается подчинить. Иначе подчинять нечего.

  3. Что означает <code>grid-column: content</code> при линиях <code>content-start</code> и <code>content-end</code>?

    Пара имён с суффиксами <code>-start</code> и <code>-end</code> автоматически образует именованную область.

  4. Можно ли сегодня использовать masonry в рабочем проекте?

    Пока берут <code>columns</code> или скрипт. С masonry код пришлось бы переписывать вслед за спецификацией.

  5. В каком случае subgrid избыточен?

    Одну линию выравнивания даёт обычный флекс. Subgrid нужен, когда линий несколько и они должны совпасть у соседей.

Задание

Выровнять ряд карточек

Три карточки с текстом разной длины. Цены и кнопки разъехались по высоте.

Задай родителю четыре строки и включи subgrid у карточек так, чтобы заголовки, тексты, цены и кнопки встали в четыре ровные линии. Разметку не трогай.

Открыть в песочнице
    Подсказка 1

    Родителю нужны четыре строки: grid-template-rows: auto 1fr auto auto;

    Подсказка 2

    Карточке — две строки кода: grid-row: span 4; и grid-template-rows: subgrid;

    Показать решение
    HTMLindex.html
    <div class="cards">  <article class="card">    <h3>Эспрессо</h3>    <p>Двойная порция.</p>    <p class="price">120 ₽</p>    <button type="button">Заказать</button>  </article>  <article class="card">    <h3>Раф на кокосовом молоке</h3>    <p>Мягкий сладковатый напиток для тех, кому обычный латте кажется слишком резким.</p>    <p class="price">240 ₽</p>    <button type="button">Заказать</button>  </article>  <article class="card">    <h3>Фильтр</h3>    <p>Зерно из Эфиопии.</p>    <p class="price">180 ₽</p>    <button type="button">Заказать</button>  </article></div>
    CSSstyle.css
    .cards {  display: grid;  grid-template-columns: repeat(3, 1fr);  grid-template-rows: auto 1fr auto auto;  gap: 12px;}.card {  display: grid;  grid-row: span 4;  grid-template-rows: subgrid;  gap: 6px;  padding: 12px;  background: #f5f5fa;  border-radius: 10px;}.card h3 { margin: 0; font-size: 15px; }.card p { margin: 0; font-size: 13px; color: #5b6070; }.price { font-weight: 700; }button {  padding: 8px;  font: inherit;  font-size: 13px;  color: #fff;  background: #6544e0;  border: 0;  border-radius: 8px;}

    Проверь себя