Перейти к содержанию

Запросы контейнера для карточки курса

Научим карточку менять внутреннюю компоновку по ширине собственного контейнера с помощью @container.

Медиазапрос уже перестраивает шапку по ширине окна. Но одна и та же карточка может оказаться в широкой одиночной колонке или в узкой ячейке большого каталога. Размер viewport в этих случаях не сообщает её реальное доступное место. Запрос контейнера позволяет привязать решение к окружающей коробке компонента, сохраняя один HTML для разных областей.

В нашем примере узкая карточка оставит обложку над текстом. При достаточной локальной ширине обложка перейдёт в левую колонку, а название, описание и метаданные — в правую. Список остаётся внешней сеткой, внутри статьи появится отдельная. Это два уровня компоновки с разными задачами: распределение курсов на странице и распределение частей одного курса.

Обёртка предоставляет измеряемое место

Каждая статья уже находится внутри li.course-slot. Сделаем эту обёртку именованным контейнером:

.layout-lab .course-slot {
  container-type: inline-size;
  container-name: course;
  min-inline-size: 0;
}

inline-size выбирает запрос по размеру в строчном направлении. В нашем горизонтальном документе это ширина. Имя позволяет указать нужный контейнер в условии. Оно не является HTML-идентификатором и не обязано быть уникальным: все шесть оболочек могут предоставлять одинаковую роль для вложенных карточек. При обработке условия браузер ищет подходящего предка по правилам запросов контейнера.

Важна дополнительная обёртка. Элемент обычно не использует себя как собственный контейнер для изменения своего оформления по этому же размерному запросу. У нас li предоставляет размер, а вложенная article реагирует. Такая граница предотвращает попытку описать зависимость, в которой компонент одновременно определяет измерение и перестраивает собственную коробку без внешней опоры. Механизм изложен в MDN.

Размерное ограничение влияет на участие содержимого в расчёте контейнера. Поэтому этот приём не следует считать просто новым именем медиазапроса. Внешняя Grid-сетка уже распределяет ширину оболочек по своим дорожкам, и для нашего случая есть понятное доступное место. min-inline-size: 0 дополнительно разрешает коробке сжиматься в рамках размещения, не требуя ширины самого длинного потомка.

Внутренняя сетка карточки

Добавьте условие после объявления контейнера:

@container course (min-width: 32rem) {
  .layout-lab .course-card {
    display: grid;
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: .75rem 1.25rem;
    align-content: start;
  }
  .layout-lab .course-cover {
    grid-row: 1 / 4;
    inline-size: 100%;
  }
  .layout-lab .course-card > * + * {
    margin-block-start: 0;
  }
}

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

minmax(0, 1fr) разрешает текстовой колонке использовать доступную долю без дополнительного минимального требования от содержимого. Разрешённые переносы и ширина изображения продолжают действовать. Внутренние поля статьи вычитаются из доступного места самой карточки. Порог относится к контейнеру, а не к ширине текстовой колонки: это различие полезно учитывать при подборе значения.

Старые интервалы margin-block-start между прямыми детьми отменены внутри условия. Теперь их задаёт gap внутренней сетки. Снаружи условия прежний обычный поток сохраняется. Поэтому вертикальный вариант не требует отдельного обратного правила, которое принудительно возвращает каждый элемент в начальное состояние. Базовые стили остаются действующими, дополнительные включаются только при совпадении условия.

Большое окно не всегда даёт широкую карточку

На широком экране наш каталог может иметь три колонки. Каждая из них уже порога, поэтому карточки останутся вертикальными. На некоторой меньшей ширине сетка может перейти к одной достаточно широкой колонке, и карточка станет горизонтальной. Это ожидаемое поведение локального правила, а не противоречие адаптивности. Решение зависит от места компонента, а не от ярлыка «большое устройство».

Для наглядного опыта временно оставьте одну карточку в списке. auto-fit позволит ей получить больше места, и при достаточной ширине условие сработает. Затем восстановите все шесть записей. Такой опыт показывает влияние внешнего распределения на внутренний макет. Не нужно одновременно менять порог и ширину оболочки, иначе будет сложнее установить причину переключения.

Порог тридцать два rem является учебным параметром. Он не получен из измерения конкретной аудитории. Редактор должен открыть оба варианта и проверить, помещаются ли реальные названия, не становится ли обложка чрезмерно маленькой и сохраняется ли понятное отношение описания к курсу. Выбор порога по содержимому остаётся важным и при запросах контейнера.

Базовый вариант сохраняет содержание

Если браузер не поддерживает используемые запросы контейнера, дополнительные правила не дадут горизонтальную компоновку. Базовая карточка при этом остаётся вертикальной: изображение, название и текст присутствуют в документе. Мы не прячем материал до совпадения условия. Такая опора на простое исходное состояние облегчает сопровождение, хотя реальную матрицу поддерживаемых браузеров нужно определить перед выпуском.

Не переносите медиазапрос шапки внутрь карточного контейнера. Шапка решает задачу всей страницы, а оболочка курса — задачу одного компонента. Можно одновременно использовать оба механизма, потому что они обращаются к разным отношениям. Попытка заменить все условия одним видом запроса создаст неправильные зависимости между независимыми областями.

Мы запрашиваем ширину, потому что она естественно задаётся внешней сеткой. Если перейти к запросу по обеим размерным осям без другой организации, содержимое может потерять участие в расчёте высоты контейнера. Такой выбор требует отдельной опоры для размера. Не используйте более широкую настройку containment только потому, что она выглядит универсальнее. У каталога текст должен определять высоту, поэтому текущая задача ограничена строчным размером.

Имя контейнера также помогает при вложении компонентов. Если внутри карточки появится ещё одна измеряемая оболочка, явная роль course позволит сохранить ожидаемую область условия. Название должно отражать назначение, а не текущую ширину или количество колонок.

Не полагайтесь на одно имя в ситуации, где нет подходящего размерного контейнера. Имя и тип должны быть заданы вместе на нужном предке. Сам класс course-slot не включает специальное поведение, пока CSS не объявляет его. Если в новый шаблон перенести только статью без li, правило может перестать срабатывать, хотя его текст не изменился. Тогда нужно восстановить окружение компонента или выбрать другой существующий контейнер по той же роли.

Это отличие важно для повторного использования: компонент имеет контракт не только содержимого, но и окружающего места. Мы зафиксировали его явно. В документации нового шаблона следует указать, кто предоставляет ширину и где начинается независимая внутренняя компоновка.

В инструментах разработчика полезно выбрать сначала li, посмотреть его ширину и имя контейнера, затем выбрать статью и увидеть, применено ли условие. Если карточка не меняется, проверьте подходящего предка, имя и реальный размер. Увеличение viewport само по себе не доказывает, что ширина этой оболочки достигла порога. Следующий урок соединит все состояния в конечный каталог и задаст правила ручной приёмки.

Оглавление курса