Обычный поток и интервалы
Оформим страницу в обычном потоке: ограничим ширину, зададим интервалы и сохраним рост блоков по содержимому.
После настройки блочной модели карточки имеют внутренние поля и не выпускают обложки за доступную ширину. Теперь нужно организовать пространство страницы. Пока оставим записи в одной колонке. Это полезное промежуточное состояние: прежде чем включать Flexbox и Grid, посмотрим, что уже умеет обычный поток и какие задачи не требуют отдельного позиционирования.
В обычном горизонтальном документе блоки идут друг за другом сверху вниз, а текст внутри строк переносится по доступной ширине. Высота каждого блока влияет на расположение следующего. Если описание курса станет длиннее, нижняя карточка опустится. Мы будем использовать это свойство вместо фиксированных координат, которые пришлось бы пересчитывать после любого изменения текста.
Оболочка страницы
В каркасе уже есть div.lab-shell внутри шапки, основного содержимого и футера. Добавим к нему правило:
.layout-lab {
line-height: 1.6;
}
.layout-lab .lab-shell {
inline-size: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.layout-lab .lab-header,
.layout-lab .lab-footer {
padding-block: 1.25rem;
}
.layout-lab .lab-main {
padding-block: 2rem;
}
Ожидается общий выровненный край у содержимого трёх областей. На обычном узком окне оболочка занимает доступную ширину за вычетом двух rem, распределённых по сторонам. На широком окне рост остановится на семидесяти двух rem, а свободное пространство окажется снаружи. Это ограничение ширины содержимого, а не фиксированная ширина всей страницы.
margin-inline: auto позволяет распределить оставшееся место по обеим сторонам коробки. В нашей горизонтальной записи это лево и право. padding-block задаёт поля по вертикальному направлению потока. Логические свойства выражают отношение к направлению текста, поэтому их смысл лучше запоминать через inline и block, а не через обязательную замену одного физического названия другим.
Не ставьте position: absolute оболочке ради выравнивания. Такой элемент перестанет участвовать в обычном расположении соседних блоков, а футер может попасть под длинный каталог. Здесь нет задачи наложить один слой на другой. Ширина, поля и естественный поток дают нужный результат без координат. Механика блочного и строчного размещения разобрана в MDN.
Интервалы внутри карточки
Браузер задаёт заголовкам и абзацам собственные внешние отступы. Для управляемого примера сначала обнулим их только внутри нашей страницы, затем установим нужное отношение между соседями:
.layout-lab h1,
.layout-lab h2,
.layout-lab h3,
.layout-lab p {
margin-block: 0;
}
.layout-lab .course-title {
font-size: 1.25rem;
line-height: 1.3;
}
.layout-lab .course-card > * + * {
margin-block-start: .75rem;
}
Последний селектор выбирает прямого ребёнка, перед которым есть другой прямой ребёнок. Первый элемент карточки не получает дополнительный верхний интервал. Между обложкой, заголовком, описанием и метаданными появляется одна повторяемая величина. Она не задаёт расстояния между любыми вложенными элементами на всей странице. Прямое отношение в селекторе сохраняет границу ответственности компонента.
Можно было бы назначить отдельное поле каждому типу текста. Однако тогда добавление новой подписи потребовало бы ещё одного специального правила. Отношение между соседями проще для нашей повторяемой структуры. Оно не является универсальным решением для любого содержимого: если один промежуток должен быть больше другого, потребуется осмысленное уточнение. В каталоге текущий набор элементов достаточно однороден.
Число 1.6 у line-height не имеет единицы и масштабируется с размером текста. У названия карточки мы выбрали отдельное более плотное значение. Это демонстрационные параметры, а не измеренная оптимальная типографика. Подробные задачи чтения уже рассмотрены в уроке о вертикальном ритме. Сейчас нас интересует механика интервалов в потоке.
Расстояния между записями
У списка уберём маркеры и внутреннее поле, а затем зададим промежуток между соседними элементами. Эти правила относятся к списку, не к внутреннему содержимому карточки:
.layout-lab .course-list {
list-style: none;
padding: 0;
margin-block: 1.5rem 0;
}
.layout-lab .course-list > li + li {
margin-block-start: 1rem;
}
.layout-lab .lab-intro > * + *,
.layout-lab .lab-request > * + * {
margin-block-start: 1rem;
}
.layout-lab .lab-request {
margin-block-start: 3rem;
}
.layout-lab .course-description {
overflow-wrap: anywhere;
}
Ожидается отделение карточек друг от друга и большой промежуток перед заявкой. Семантический список остаётся в HTML, хотя визуальные маркеры убраны. В одиннадцатом уроке заменим межэлементные поля на gap сетки и явно отменим это старое правило. Так переход между механизмами будет виден, а не спрятан за случайным удвоением промежутков.
В обычном блочном потоке некоторые вертикальные внешние отступы могут схлопываться. Поэтому два заданных поля не всегда дают простую сумму. Внутренние поля и рамки, а также другой контекст форматирования меняют условия. Если расстояние отличается от ожидания, посмотрите коробки соседей в инструментах разработчика. Не нужно добавлять пустой абзац ради получения промежутка: он не выражает нужную связь и зависит от текста.
Рост содержимого и длинные слова
Замените короткое описание Markdown двумя предложениями. Ожидается более высокая карточка и смещение следующих записей вниз. У нас нет ограничения высоты, поэтому текст должен остаться видимым. Сравните это с временным block-size: 12rem: длинное содержание может выйти за коробку. После опыта уберите ограничение. Его появление должно быть вызвано конкретной задачей, а не желанием сделать текущие карточки одинаковыми.
overflow-wrap: anywhere разрешает перенос длинной последовательности, когда иначе она не помещается. Обычные слова всё равно используют нормальные возможности переноса. Это полезно для необычного адреса или длинного обозначения в описании, но не заменяет осмысленную редактуру. В заголовках и коде потребуются свои решения: не стоит глобально ломать каждый фрагмент одинаковым правилом.
Сохранение обычного потока полезно и при редактировании. Когда автор добавит новое предложение, ему не потребуется искать координату следующей карточки. Однако естественный рост не освобождает от проверки слишком длинного содержания. Оно может сделать каталог трудным для просмотра, хотя наложений технически не будет. В таком случае меняют редакционную структуру описания, а не принудительно скрывают всё после второй строки.
Попробуйте временно добавить пустой абзац в карточку. Он тоже становится ребёнком и получает отношение соседей из нашего селектора, хотя содержательной роли не имеет. Удалите его после опыта. Такой результат показывает, почему интервалы лучше задавать CSS, а не пустыми тегами: разметка с лишними узлами меняет и структуру, и вычисляемые отношения.
Отдельная строка в исходнике не обязательно создаёт отдельную строку на странице. Два span в метаданных могут оставаться рядом, хотя редактор записал каждый с новой строки. Блочный абзац вокруг них задаёт общую область, а текст внутри неё следует правилам строк. Если хочется поменять отношение этих частей, выбирайте CSS-контекст или подходящую структуру, а не вставляйте множество переносов в файл. В следующем уроке Flexbox сделает группу метаданных управляемой именно как набор элементов.
На этом шаге каталог остаётся вертикальным и уже ведёт себя предсказуемо при изменении длины текста. Шапка пока тоже использует обычный поток, поэтому бренд и навигация не обязаны стоять по краям одной строки. В следующем уроке применим Flexbox именно к этому локальному отношению: разместим небольшую группу элементов вдоль одной оси и позволим ей переноситься, когда места не хватает.