Рубрикатор

Здесь можно найти крутой материал на любую тему из digital : через поиск, рубрику и даже #тег.

{{ errors.first('search') }}

Выберите категорию

Все Все Разработка Агентство Разработка Дизайн Разработка Новости Разработка Образование Разработка Продвижение Разработка Разработка

Облако #тегов

#digital news #SMM #TAGREE #TikTok #бизнес #вдохновение #веб-дизайн #дизайн #кейс #маркетинг #мир #программирование #продвижение #разработка #реклама #сайты #социальные сети #таргетинг #типографика #фронтенд
Школа 04.01.2022
Курс: Junior frontend-разработчик

Flexbox и Grid Layout

Изображение

1. Flexbox — теория

До появления flexbox для управления потоком и построения сеток использовались табличная вёрстка (с таблицами нам ещё предстоит познакомиться), блочно-строчные элементы и float. На данный момент эти техники очень сильно устарели и имеют множество проблем и ограничений.

Хотя сами CSS-свойства и HTML-элементы, затронутые выше, по-прежнему отлично решают те задачи, для которых они были созданы, вопросы построения аккуратных равноразмерных сеток и адаптивности веб-страницы стояли очень остро.

CSS Flexible Box Layout, или просто flexbox, был разработан как модель одномерного-направленного макета — в виде строки или столбца, позволяющая наиболее оптимально заполнять доступное пространство, автоматически выставляя ширину и высоту дочерних элементов.

Модель flexbox состоит из двух типов элементов: родительского — flex-контейнер, и дочерних — flex-элементы. Чтобы создать flex-контейнер, мы задаём значение flex или  inline-flex для свойства display контейнера. Как только мы это сделаем, прямые потомки этого контейнера станут flex-элементами.

В отличие от блочно-строчных между flex-элементами отсутствуют пробелы, что упрощает их расположение и расчёт ширины контейнера (в примерах ниже отступы будут оставлены намеренно для большей наглядности).

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

Главная ось

Главная ось задаёт направление потока flex-элементов и определяется свойством flex-direction, которое может принимать следующие значения:

  • row — главная ось направлена так же, как и ориентация текста, по умолчанию слева направо. Элементы отображаются в ряд. Значение по умолчанию.

  • row-reverse — аналогично значению row, но в обратном направлении: меняются местами начальная и конечная точки, а главная ось по умолчанию направлена справа налево.

  • column — главная ось располагается вертикально и направлена сверху вниз.

  • column-reverse — аналогично значению column, но в обратном направлении: меняется положение начальной и конечной точек, а главная ось направлена снизу вверх.

flex-basis и свойства flex-элементов

Базовый размер флекс элемента по основной оси задаёт свойство flex-basis. Значение по умолчанию — auto. Если направление — строка, то в нормальных условиях свойство аналогично значению width, если колонка — то height.

Как видно из примеров выше, flex-элементы отображаются в ряд или колонку, начиная от  начала или конца главной оси в зависимости от свойства flex‑direction, при этом по умолчанию:

  • не растягиваются по основной оси;
  • растягиваются, чтобы заполнить побочную ось;
  • не переносятся на новую строку (колонку);
  • если места по основной оси окажется недостаточно, то flex-элементы сожмутся до своей минимальной ширины, а затем выпадут из контейнера.

Давайте зададим флекс-элементам { flex‑basis: 100px; }  и посмотрим на следующие примеры:

В первом примере ширина элементов составляет 100px, во втором — элементы пропорционально сжались, чтобы поместиться в свой контейнер, в третьем — элементы сжались до своего минимума и затем выпали из контейнера, так как им не хватило места.

Элементы могут переноситься на новую строку. За это отвечает свойство flex-wrap, которое надо задать flex-контейнеру. Значения:

  • nowrap — нет переноса, элементы расположены в одну строку. Контейнер может быть переполнен. Значение по умолчанию;
  • wrap — элементы переносятся на новые строки в направлении в соответствии со значением flex‑direction;

  • wrap-reverse — элементы переносятся на новые строки, но в обратном направлении.

Абсолютно аналогично флекс-элементы будут себя вести, если мы ограничим высоту контейнеру и зададим направление главной оси — в колонку:

Элементы переносятся в новую колонку и при этом стараются заполнить всё доступное пространство по побочной оси, перпендикулярной основной.

flex-grow и flex-shrink

По умолчанию размер флекс-элемента по главной оси зависит от контента или определяется значением flex-basis.

CSS-свойство flex-grow определяет как много свободного пространства во flex-контейнере должно быть назначено текущему элементу. Принимает численные значения, в том числе и дробные, и по умолчанию равно нулю.

Если все флекс-элементы имеют одинаковый коэффициент flex‑grow, то они получат одинаковую долю свободного пространства, в противном случае оно распределяется в соответствии с соотношением, определённым коэффициентами flex‑grow для каждого элемента.

Помимо фактора растяжения существует и фактор сжатия элементов — CSS-свойство flex-shrink, который будет определять заполнение контейнера в том случае, когда стандартная ширина флекс-элементов превышает ширину контейнера. Принимает численные значения, в том числе и дробные, и по умолчанию равно 1.

В этом примере ширина первого элемента — auto, второго — 300px. Однако размеров первого блока не хватило для вывода содержимого, и размеры флекс-элементов были автоматически пересчитаны. Но, прописав второму элементу { flex‑shrink: 0 }, получим следующее:

Фактически, мы запретили сжатие для второго элемента, его ширина теперь действительно 300px, а первый элемент вынужден был занять оставшееся место.

Существует упрощённая запись трех CSS-свойств в одном — свойство flex, устанавливает flex‑grow, flex‑shrink и flex‑basis. Значение по умолчанию: { flex: 0 1 auto }.

Как видно из примеров выше, размеры наших блоков становятся адаптивными, подстраиваясь под своё содержимое или исходя из заданных им свойств. Всё это позволяет создавать очень гибкие и сложные сетки. Отлично подходит для создания различных листингов — например списка товаров, когда мы хотим получить карточки одной ширины и высоты в каждой строке, не задавая им при этом жёстко высоту, — ведь контент в каждой карточке может отличаться по объёму. С помощью инлайн-блоков подобное сделать невозможно.

justify-content

CSS свойство  justify-content определяет, как браузер распределяет флекс-элементы вдоль главной оси флекс-контейнера после того, как были рассчитаны их размеры и отступы. Задаётся непосредственно флекс-контейнеру.
Это свойство также работает и с grid-сеткой, которую мы будем разбирать далее, поэтому может возникнуть некоторая путаница из применяемых значений. Рассмотрим именно для flexbox. Возможные значения:

  • flex-start — значение по умолчанию, элементы располагаются в начале контейнера:

  • flex-end — элементы располагаются в конце контейнера:

  • center — элементы располагаются в центре контейнера:

  • space-between — элементы равномерно распределяются по всей строке. Первый и последний элементы прижимаются к соответствующим краям контейнера:

  • space-around — элементы равномерно распределяются по всей строке. Перед первым и после последнего элемента имеется пустое пространство, равное половине пространства между двумя соседними элементами:

  • space-evenly — элементы равномерно распределяются по всей строке. Пустое пространство в начале и конце строки равно пространству между двумя соседними элементами.

align-items и align-self

Выравнивать flex-элементы можно не только по главной, но и по побочной оси, в перпендикулярном направлении.

CSS-свойство align-items выравнивает все flex-элементы текущей flex-линии по побочной оси, задаётся flex-контейнеру. Значения:

  • stretch — значение по умолчанию, элементы растягиваются таким образом, чтобы занять всё доступное пространство контейнера;
  • flex-start — элементы располагаются в начале побочной оси контейнера;
  • flex-end — элементы располагаются в конце побочной оси контейнера;
  • center — элементы располагаются в центре побочной оси контейнера;
  • baseline — элементы выравниваются по базовой линии строки.

CSS-свойство align-self выравнивает по побочной оси непосредственно сам flex-элемент, которому это свойство задано. Значение по умолчанию — auto, не влияет на расположение flex-элемента. Остальные значения соответствуют свойству align‑items.

flexbox и внешние отступы

Выстраивать flex-элементы по осям можно не только с помощью свойств justify‑content, align‑items, align‑self, но и внешними отступами в значении auto, причём работает это с обеими осями:

align-content

Flexbox позволяет выравнивать не только элементы, но и строки внутри flex-контейнера по побочной оси. За это отвечает CSS-свойство align-content, задаваемое flex-контейнеру.
Значения:

  • stretch — Значение по умолчанию. Строки равномерно растягиваются, заполняя свободное пространство:

  • flex-start — Строки располагаются в начале побочной оси:

  • flex-end — Строки располагаются в конце побочной оси:

  • center — Строки располагаются в центре побочной оси:

  • space-between — Строки равномерно распределяются вдоль побочной оси с равными отступами. Первая строка располагается у начала побочной оси, последняя — в её конце:

  • space-around — Строки равномерно распределяются вдоль побочной оси с равными отступами. Отступ перед первой строкой и после последней равны половине отступа между двумя соседними строками;
  • space-evenly — Строки равномерно распределяются вдоль побочной оси с равными отступами. Отступ перед первой строкой и после последней равны отступу между двумя соседними строками.

order

order — CSS-свойство, определяющее порядок, в котором располагаются flex-элементы в их flex-контейнере вдоль главной оси. Может принимать любые числа, в том числе и отрицательные. Положительные числа сдвигают элемент к концу оси, отрицательные — к началу. Элементы с одинаковым значением order располагаются в том порядке, в каком они находятся в разметке HTML-документа. Значение по умолчанию — 0.

2. Flexbox — вёрстка макета

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

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

Далее последовательно воспроизводите все действия в своём учебном проекте.

Структура страницы

Как мы можем видеть, на всех трёх страницах имеются одинаковые элементы — шапка и футер. С них и начнём.

Первое, что необходимо вспомнить — есть элементы, для которых браузеры автоматически проставляют отступы, которые нам могут быть не нужны. Один из таких элементов — <body>.

Хорошей практикой считается сбросить дефолтные стили сразу для <html> и <body>, чтобы гарантировать одинаковое поведение во всех браузерах:

html,
body {
  width: 100%; 
  margin: 0;
  padding: 0;
}

Здесь стоит обратить внимание, что общая высота футера и шапки нашего сайта составляет всего лишь 602px. Это означает, что если на нашей странице будет совсем немного основного контента — например заголовок и пара абзацев текста, то итоговая высота нашего документа окажется меньше высоты вьюпорта браузера, а под футером останется пустое место, что будет выглядеть очень некрасиво.

Необходимо «прибить» наш футер к низу экрана. Сделать это можно разными способами. Сделаем это с помощью flexbox. Ранее мы уже должны были выделить основные блоки на главной странице. Получиться должно было примерно следующее:

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Lenni Art</title>
    <link rel="stylesheet" href="styles/styles.css">
  </head>
  <body class="body">
    <header class="page-header"></header>
    <main class="main"></main>
    <footer class="page-footer"></footer>
  </body>
</html>

Наша задача сделать так, чтобы <body> по высоте был не менее области просмотра, а <main> автоматически растягивался, занимая всё доступное пространство. При этом шапка у нас будет фиксированная и не занимать место в общем потоке. А это значит, что мы должны для <body> ещё задать вертикальный отступ сверху на величину высоты шапки. Давайте всё это сделаем:

.body {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding-top: 147px;
}

.page-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 147px;
}

.main {
  flex: 1 0 auto;
}

.page-footer {
  flex: 0 0 auto;
}

Итоговый результат получится следующим:

Шапка сайта

Теперь давайте полноценно сверстаем шапку нашего сайта. Первое, на что мы должны обратить внимание, элементы шапки занимают не всю ширину, а только 1334px, располагаясь по центру. Таким образом внутри шапки нам понадобится дополнительная обёртка-контейнер. Сразу стоит отметить, что контейнер нужно всегда делать шире, чем он отрисован, компенсируя «лишнюю ширину» внутренними горизонтальными отступами. Это нужно для того, чтобы при уменьшении вьюпорта наши элементы не прилипли к краю экрана.

<header class="page-header">
  <div class="page-header__container"></div>
</header>
.page-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 147px;
  display: flex;
  align-items: center;
  background-color: rgba(222, 222, 222, 0.5);
  backdrop-filter: blur(20px);
  z-index: 100;
}
.page-header__container {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  width: 100%;
  max-width: 1414px;
  margin: 0 auto;
  padding-left: 40px;
  padding-right: 40px;
}

Помимо позиционирования мы прописали стили фона шапки, которые можем найти во вкладке «Inspect» в правой части экрана. И здесь нас встречает новое CSS-свойство backdrop-filter, визуально размывающая то, что находится у нас под шапкой. Однако мы имеем проблему — плохую поддержку данного свойства браузерами. Так, данное свойство, например, не будет работать в браузере Firefox. Проблему усугубляет то, что затемнение фона при неработающем свойстве недостаточно, и при прокрутке страницы шапка и то, что под ней, будут сливаться, сильно затрудняя восприятие информации.

Эта проблема может быть решена с помощью правила @supports:

.page-header {
  background-color: rgba(222, 222, 222, 0.98);
}

@supports (backdrop-filter: blur(20px)) {
  .page-header {
    background-color: rgba(222, 222, 222, 0.5);
    backdrop-filter: blur(20px);
  }
}

Теперь по умолчанию наша шапка будет иметь практически непрозрачный фон, однако если backdrop-filter поддерживается браузером, то будет применено иное правило.

Далее мы видим логотип. Мы можем сделать экспорт лого из Figma в любом удобном для нас формате. Но для любых иконок и всегда по возможности предпочтительнее SVG. Почему — разберём на одном из следующих уроков, а пока просто отметим это для себя. Выделим лого и в правой части экрана нажмём на «Export«.

SVG-файл мы можем открыть любым текстовым редактором и затем просто вставить в разметку HTML-документа. Как правило, логотипы в шапке сайта являются ссылками, ведущими на главную страницу. Таким образом разметка будет выглядеть так:

<header class="page-header">
  <div class="page-header__container">
    <a href="index.html" class="page-header__logo">
      <svg 
        class="page-header__logo-icon" 
        width="202" 
        height="147" 
        viewBox="0 0 202 147" 
        fill="none" 
        xmlns="http://www.w3.org/2000/svg"
       >
        <!-- содержимое свг -->
      </svg>
    </a>
  </div>
</header>

Добавим стили для нашего логотипа:

.page-header__logo {
  flex-shrink: 0;
  width: 202px;
  height: 147px;
  font-size: 0;
  text-decoration: none;
}

.page-header__logo-icon {
  width: 100%;
  height: 100%;
}

SVG-изображение автоматически отцентруется в своих размерах.

Примечание: В качестве иконки мы можем использовать тег <img>, задав путь к соответствующему файлу:

<a href="index.html" class="page-header__logo">
  <img src="assets/icons/logo.svg" alt="logo" class="page-header__logo-icon">
</a>

На первый взгляд так поступать намного лучше, ведь HTML-разметки разительно меньше.

Однако на самом деле такой подход несёт много минусов с точки зрения оптимизации кода и ресурсов и даёт нам меньше возможностей с точки зрения визуальной составляющей и анимации. Самое простое и очевидное — отсутствие возможности сменить цвет иконки при наведению на элемент, ведь это просто внешний файл.

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

Вернёмся к нашему макету.

Далее мы видим главный навигационный блок. В соответствии с методологией БЭМ он не является отдельным независимым блоком, т.к. не может существовать вне шапки.

<nav class="page-header__nav">
  <a href="#" class="page-header__nav-link">Мероприятия</a>
  <a href="#" class="page-header__nav-link">Блог</a>
  <a href="#" class="page-header__nav-link">О нас</a>
  <a href="#" class="page-header__nav-link">Контакты</a>
</nav>
.page-header__nav {
  display: flex;
  align-items: baseline;
  margin: 0 auto;
}

.page-header__nav-link {
  margin: 0 25px;
  color: #1f1e1e;
  font-weight: 600;
  font-size: 18px;
  line-height: 25px;
  text-decoration: none;
  transition: color 0.2s;
}

.page-header__nav-link:hover {
  color: #bc3324;
}

Здесь мы добавили первый текстовый контент — ссылки, и можем увидеть, что внешний вид их отличен от того, что нарисовано в макете. Причина — мы не подключили нужные шрифты. Для наших целей прекрасно подойдёт сервис Google Fonts, им и воспользуемся. Найдём нужный шрифт и подключим его в <head>. Кроме того не забудем задать стили.

<head>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&family=Open+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
.body {
  color: #1F1E1E;
  font-family: "Open Sans", sans-serif;
  font-size: 16px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.375;
}

Мы прописали не только шрифт, но и прочие стили «по умолчанию» для текстового содержимого нашего сайта.

Наконец, справа мы видим кнопку «Войти». Данная кнопка одновременно является элементом БЭМ-блока .page-header и самостоятельным переиспользуемым БЭМ-блоком.

<a href="#" class="btn page-header__user-btn">
  Войти
</a>
.btn {
  display: inline-block;
  box-sizing: border-box;
  padding: 15px 40px;
  border: 1px solid #1F1E1E;
  background-color: transparent;
  outline: none;
  color: #1F1E1E;
  font-family: "Open Sans", sans-serif;
  font-weight: 600;
  font-size: 16px;
  line-height: 20px;
  text-align: center;
  text-decoration: none;
  transition: color 0.2s, background-color 0.2s, border-color 0.2s;
  overflow: hidden;
  cursor: pointer;
}

.btn:hover {
  background-color: #1F1E1E;
  color: #ffffff;
}

Мы получили практически то, что изображено на макете. Но присмотритесь — наш навигационный блок расположен левее, чем на макете, не по центру шапки. Это произошло по той причине, что ширина логотипа и кнопки «Войти» отличаются. Решить проблему можно разными способами. В рамках курса сделаем просто: обернём кнопку «Войти» в <div>, задав ему ту же ширину, что и у логотипа.

<div class="page-header__right-block">
  <a href="#" class="btn page-header__user-btn">
    Войти
  </a>
</div>
.page-header__right-block {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  width: 202px;
}

Вот теперь выглядит всё как нужно:

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

Листинг мероприятий

Теперь давайте обратим своё внимание на второй макет и сверстаем листинг мероприятий.

Рядом с файлом index.html создайте файл events.html, скопировав из первого всю разметку, но оставив <main> пустым. Далее создадим в нашем мэйне новыe БЭМ-блоки:

<main class="main">
  <!-- Здесь будут хлебные крошки -->

  <div class="events">

    <section class="listing events__listing">
      <h2 class="listing__title section-title">Мероприятия</h2>

      <div class="listing__events-list">
       
        <article class="event-card listing__event-card">        
          <!-- Карточка товара -->
        </article>

      </div>
    </section>

    <!-- Здесь будут дополнительные услуги -->
  </div>
</main>

Как видно из разметки, мы выделили .listing в отдельный БЭМ-блок, так как он легко может быть использован на любой другой странице при необходимости. Давайте зададим основные стили нашему листингу и добавим новый переиспользуемый БЭМ-блок .section‑title, который при этом является БЭМ-элементом блока .listing.

Наши стили:

.section-title {
  box-sizing: border-box;
  display: flex;
  width: 100%;
  max-width: 624px;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  max-width: 624px;
  min-height: 62px;
  margin: 0 auto 150px;
  padding: 8px 45px;
  background-color: #1F1E1E;
  color: #ffffff;
  font-family: "Lora", serif;
  font-weight: 500;
  font-size: 36px;
  line-height: 1.277;
  text-align: center;
  text-transform: uppercase;
}

.listing {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1414px;
  margin: 0 auto;
  padding-left: 40px;
  padding-right: 40px;
}

.listing__events-list {
  display: flex;
  flex-wrap: wrap;
}

.events {
  padding: 140px 0 100px;
}

.events__listing {
  margin-bottom: 100px;
}

Теперь сверстаем карточку товара.

<article class="event-card listing__event-card">
  <a href="#" class="event-card__link">
    <picture class="event-card__picture">
      <img src="images/events/nirvana.jpg" alt="nirvana" class="event-card__img" />
    </picture>

    <h4 class="event-card__title">Собираемся и слушаем альбом Nirvana</h4>

    <p class="event-card__description">
      Это третий альбом группы, выпущенный после смерти Курта Кобейна и первый, содержащий студийный материал.
    </p>

    <time datetime="2022-12-07T18:00" class="event-card__time">
      07.12.2022 | начало 18.00
   </time>
  </a>
</article>
.event-card {
  display: flex;
  width: 405px;
  max-width: 100%;
  min-width: 280px;
}

.event-card__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: #1F1E1E;
  text-decoration: none;
}

.event-card__link:hover .event-card__title {
  color: #BC3324;
  text-decoration-color: #BC3324;
}

.event-card__picture {
  display: block;
  position: relative;
  width: 100%;
  margin: 0 0 25px;
  font-size: 0;
  text-decoration: none;
  overflow: hidden;
}

.event-card__picture::after {
  content: "";
  display: block;
  width: 100%;
  padding-top: 107.16%;
}

.event-card__img {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.event-card__title {
  margin: 0 0 15px;
  min-height: 66px;
  color: #1F1E1E;
  font-weight: 600;
  font-size: 24px;
  line-height: 33px;
  text-decoration: underline transparent;
  transition: color 0.2s, text-decoration-color 0.2s;
}

.event-card__description {
  margin: 0 0 auto;
  font-size: 16px;
  line-height: 22px;
}

.event-card__time {
  margin: 15px 0 0;
  font-weight: 300;
  font-size: 14px;
  line-height: 19px;
}

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

В каждом ряду у нас по три карточки. Представим, что список состоит из пяти карточек. Как будут выровнены 4‑я и 5‑я карточки? Как правило, карточки принято выстраивать по левой стороне. Мы можем задать для каждой карточки правый и нижний внешний отступ, а каждой третьей обнулить правый отступ:

.listing__event-card {
  margin-right: 60px;
}

.listing__event-card:nth-child(3n) {
  margin-right: 0;
}

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

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

.listing__events-list {
  display: flex;
  flex-wrap: wrap;
  margin: 0 -30px -120px;
}

.listing__event-card {
  width: calc(33.3333% - 60px);
  margin: 0 30px 120px;
}

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

В данном примере также мы можем увидеть новую для нас возможность — использование CSS-функции calc(), позволяющей автоматически вычислять размер элементов.

Конечный результат будет выглядеть так:

На что ещё стоит обратить внимание?

  • Время мероприятия мы указали с помощью HTML-тега <time> и соответствующего атрибута datetime, что позволяет осуществлять автоматическую машинную обработку даты, например — поисковыми системами.
  • Наш список карточек построен с помощью Flexbox, благодаря чему все карточки в ряду имеют одинаковый размер. Кроме того сама карточка также построена на Flexbox, а текстовому описанию задан нижний автоматический отступ, таким образом дата всегда прибита к нижнему краю карточки вне зависимости от размера текста и заголовка.
  • Для заголовка карточки указали минимальную высоту, равную двум строчкам текста. Не самое идеальное решение, но в большинстве случаев позволит выстроить сетку листинга максимально ровной, так как всё, что связывает карточки между собой, — их общая высота в строке flex-контейнера.
  • В нескольких местах встречается свойство transition, которое задаёт эффект перехода между двумя состояниями элемента. Благодаря этому мы можем плавно менять цвета наших ссылок и кнопок по наведению мыши.

Изображение карточки обёрнуто в тег <picture>. Для чего он нужен, разберём на одном из следующих уроков. Но обратите внимание на стили:

.event-card__picture {
  position: relative;
}

.event-card__picture::after {
  content: "";
  display: block;
  width: 100%;
  padding-top: 107.16%;
}

.event-card__img {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

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

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

Для этого мы воспользовались CSS-свойством object‑fit. Возможные значения:

  • fill — значение по умолчанию, изображение соответствует заданным размерам, но его пропорции игнорируются.
  • contain — изображение масштабируется так, чтобы целиком уместиться в заданных размерах, пропорции сохраняются.
  • cover — изображение масштабируется так, чтобы целиком заполнить заданные размеры, пропорции сохраняются, а всё что не уместилось — обрезается.
  • none — сохраняются исходные параметры изображения, установленные значения ширины и высоты не влияют на отображение, всё что не уместилось — обрезается.

3. Grid Layout — теория

Колонки и строки

Grid Layout представляет собой двумерную сетку для CSS. Это набор пересекающихся горизонтальных и вертикальных линий, образующих столбцы и строки, в которые могут быть размещены элементы.

Как и Flexbox, Grid Layout состоит из grid-контейнера и grid-элементов.

Задав элементу { display: grid } или { display: inline‑grid }, мы создаём grid-контейнер, прямые потомки которого в свою очередь станут grid-элементами.

Давайте сразу рассмотрим живой пример, а затем разберём, что здесь происходит:

.grid {
  display: grid;
  grid-template-columns: 50px 1fr 2fr;
  grid-template-rows: 70px minmax(100px, auto);
  grid-auto-rows: auto;
  grid-gap: 30px 20px;
}

Мы создали grid-контейнер, и указали ещё несколько CSS-свойств, отвечающих за нашу сетку:

  • grid-template-columns — задаёт количество колонок в сетке и может определять ширину каждой из них;
  • grid-template-rows — определяет параметры строк сетки;
  • grid-gap — задаёт отступы между строками и столбцами, является сокращением для свойств row-gap и column-gap. Принимает любые единицы длины.

Если с grid‑gap всё достаточно просто, то на первых двух свойствах стоит остановиться поподробнее.

Для grid‑template‑columns мы задали три значения, которые определяют для нашей сетки три колонки. Ширина первой колонки задана жёстко — 50px, а размер других задан в новых для нас единицах — фракциях (fr), представляющих собой долю имеющегося свободного пространства. Таким образом вторая колонка заняла у нас 1/3 имеющегося места, а третья колонка — 2/3, и мы получили гибкие по ширине блоки.

Для grid‑template‑rows мы указали два значения, которые соответствуют высоте первой и второй строки соответственно, вместе они представляют собой явную сетку. Если места под контент в элементах этих строк окажется недостаточно, то он выпадет.

Для второй строки мы задали размер с помощью функции: minmax(100px, auto). В данном случае минимальная высота строки у нас 100px, а максимальная будет определяться размером содержимого (auto). Максимальная высота также может быть жёстко задана в единицах длины.

При этом grid-элементов у нас такое количество, что количество строк оказалось больше, чем мы задали. Это неявная сетка. В таком случае размер ячеек по умолчанию будет определяться содержимым grid-элементов.

Параметры неявной сетки задаются следующими CSS-свойствами:

  • grid-auto-rows — задаёт размер неявно созданной строки сетки;
  • grid-auto-columns — задаёт размер неявно созданной колонки сетки;

Значение по умолчанию для обоих свойств — auto, что будет соответствовать размеру содержимого.

В нашем пример есть ещё одно неявно заданное свойство { grid‑auto‑flow: row }.

grid-auto-flow — CSS-свойство, определяющее, как grid-элементы заполняют сетку.
Возможные значения:

  • row — элементы заполняют поочерёдно каждую строку, новые строки добавляются по мере необходимости. Это значение по умолчанию;
  • column — элементы заполняют поочерёдно каждый столбец, а новые столбцы добавляются по мере необходимости;
  • dense (row dense, column dense) — ключевое слово, указывающее grid-элементами заполнять свободное пространство сетки. Это может привести к нарушению порядка, т.к. элементы будут выстраиваться не в соответствии со своим расположением, а в соответствии с размером.

Добавим в наш пример свойство { grid‑auto‑flow: column } и посмотрим, что получится:

Мы видим, что наши элементы заполняют не строки, а колонки, создавая новые при необходимости.

В этом же примере мы можем увидеть ещё один способ задания размеров сетки:

grid-template-columns: 35px repeat(2, 50px) repeat(2, 1fr);

Для первой колонки задан размер в 35px. Далее идёт повтор двух колонок с шириной 50px и затем две колонки, занимающие по равной фракции.

Но что делать, если мы не знаем, сколько нам нужно колонок, и хотим, чтобы их количество определялось автоматически в зависимости от заданного размера? Для этого есть специальные значения auto-fill и auto-fit:

Для первого grid-контейнера здесь задано:

grid-template-columns: repeat(auto-fill, minmax(75px, 1fr));

В этом случае строка заполняется таким количеством колонок, которое возможно с учетом заданной ширины.

Второму grid-контейнеру задано:

grid-template-columns: repeat(auto-fit, minmax(75px, 1fr));

В этом случае поведение будет похожим, но если элементы умещаются в одну строку, они заполнят всё имеющееся пространство.

grid-column и grid-row

Огромный плюс Grid Layout заключается в том, что мы можем не только определить параметры двумерной сетки, но и очень гибко распределять grid-элементы, указав, какие именно ячейки сетки они могут занимать.

Давайте снова посмотрим на пример:

Мы задали следующие стили:

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-gap: 20px;
}

.grid__element--1 {
  grid-column-start: 2;
  grid-column-end: 4;
}

.grid__element--2 {
  grid-row-start: 3;
}

.grid__element--3 {
  grid-row: 1 / 3;
}

.grid__element--5 {
  grid-column: 2 / -1;
}

Для первого элемента .grid__element--1 мы задали:

  • grid‑column‑start: 2
  • grid‑column‑end: 4

Это равноценно обобщающему свойству { grid‑column: 2 / 4 }.

Теперь наш первый grid-элемент начинается с первой колонки, а заканчивается перед четвёртой.

Для свойств grid-row-start и grid-row-end аналогично обобщающим является свойство grid-row.

Так для .grid__element--3 задано:

  • grid-row: 1 / 3

Третий элемент начинается на первой строке и заканчивается перед третьей.

Пятый элемент .grid__element--5 начинается со второй колонки, а заканчивается — последней, благодаря значению:

grid‑column: 2 / -1.

Таким образом, мы можем использовать отрицательные значения, указывая последнюю строку или колонку.

Существует также свойство, обобщающее все 4 рассмотренные выше свойства:

.some-grid-element {
   grid-area: 1 / 1 / 4 / 2;
}

grid-area задаёт одновременно:

  • grid-row-start,
  • grid‑column‑start,
  • grid-row-end
  • grid‑column‑end.

Впрочем, данное CSS-свойство обладает ещё одной интересной возможностью, о которой речь пойдёт ниже.

Шаблоны grid-областей

Grid Layout имеет ещё один прекрасный и очень удобный способ задавать параметры сетки — именованные области.

Давайте опять разберём на живом примере:

.body {
  display: grid;
  grid-template-columns: 1fr 100px;
  grid-template-rows: 50px 1fr auto;
  grid-template-areas:
    "header header"
    "main aside"
    "footer footer";
}

.header {
  grid-area: header;
}

.main {
  grid-area: main;
}

.aside {
  grid-area: aside;
}

.footer {
  grid-area: footer;
}

Каждому grid-элементу благодаря свойству grid‑area мы можем задать собственное имя, а затем с помощью свойства grid‑template‑areas заполнить нашу сетку данными элементами.

Выравнивание ячеек

Мы можем не только распределять наши grid-элементы по ячейкам, но и выравнивать их как по горизонтали, так и по вертикали. За это отвечают следующие свойства :

  • justify-items — выравнивает все grid-элементы по горизонтали, задаётся grid-контейнеру;
  • align-items — выравнивает все grid-элементы по вертикали, задаётся grid-контейнеру;
  • justify-self — выравнивает конкретный grid-элемент по горизонтали;
  • align-self — выравнивает конкретный grid-элемент по вертикали;

Возможные значения:

  • stretch — значение по умолчанию. Grid-элемент стремится занять всю ячейку;
  • start — grid-элементы выравниваются в начале ячейки;
  • end — grid-элементы выравниваются в конце ячейки;
  • center — grid-элементы выравниваются в центре ячейки;

Наглядный пример:

4. Grid Layout — вёрстка макета

Поддержка Grid Layout появилась значительно позже Flexbox. Так, например, Flexbox полноценно поддерживается IE11 (правда с некоторыми багами, которые можно обойти), в то время как о Grid Layout для данного браузера можно забыть. Впрочем, если вам требуется поддержка только современных браузеров, об этом можно не переживать и делать всё смело с помощью Grid Layout.

Форма бронирования

На наших макетах присутствует один единственный блок, который невозможно сверстать в соответствии с дизайном и на всём протяжении адаптива без использования Grid Layout: это форма бронирования столика на самом нижнем макете мероприятия.

Мы ещё не ничего не знаем о формах и об адаптиве, поэтому на данный момент сверстаем данный блок «условно» и вернёмся к нему позже.

Сперва рядом с файлом index.html создайте файл event.html, скопировав из первого всю разметку, но оставив <main> пустым. Далее создадим в нашем мэйне новыe БЭМ-блоки:

<main class="main">
  <!-- Здесь будут хлебные крошки -->

  <article class="event">
    <!-- Здесь пропущенное содержимое страницы -->

    <form class="reserve event__reserve">
      <div class="reserve__checks">
        Чекбоксы
      </div>
      <div class="reserve__scheme">
        Схема столов
      </div>
      <div class="reserve__order">
        Оформление заказа
      </div>
      <div class="reserve__legend">
        Легенда
      </div>
    </form>
  </article>
</main>
.event {
  padding: 40px 0 150px;
}

.reserve {
  display: grid;
  gap: 65px 11%;
  grid-template-columns: 405px calc(89% - 405px);
  grid-template-areas: 
    "checks scheme"
    "order legend"; 
  width: 100%;
}

.reserve__checks {
  grid-area: checks;
}

.reserve__order {
  grid-area: order;
}

.reserve__scheme {
  grid-area: scheme;
}

.reserve__scheme {
  grid-area: scheme;
}

Получим следующий результат:

Резюме

Итак, мы познакомились с Flexbox и Grid Layout и теперь умеем строить любые сетки. Впереди нас ждёт вёрстка всего проекта. Но сперва нужно поговорить об автоматизации и оптимизации процесса разработки, что мы и сделаем на следующем уроке.

5. Материалы для самостоятельного изучения

  1. Основные понятия Flexbox:
    developer.mozilla.org/ru/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox
  2. Основные понятия Grid Layout:
    developer.mozilla.org/ru/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout
  3. CSS Grid Genetator:
    https://grid.layoutit.com/
  4. Can I Use — сервис для проверки поддерживаемых web-технологий:
    https://caniuse.com/
{{ netItem.title }}

Меню

Список уроков

1. Введение. Знакомство с HTML 2. Основы CSS и методологии 3. Блочная модель и позиционирование 4. Flexbox и Grid Layout 5. Оптимизация и автоматизация 6. Формы, таблицы и текстовый контент 7. Адаптивная вёрстка 8. Графика 9. Введение в JavaScript 10. Введение в JavaScript. Часть 2 11. Введение в JavaScript. Часть 3

Навигация по странице

Email

hi@tagree.ru

Телефон

+7 499 350 0730

Telegram

t.me/tagree_ru

Москва

+7 499 350 0730

Долгоруковская 7, БЦ «Садовая Плаза», 4 эт.

Томск

+7 382 270 0368

Белая 8, БЦ «Tagree»

Петербург

+7 812 509-31-09

​Большая Монетная, 16

vkyt
Политика конфиденциальности Пользовательское соглашение Cookies

Пользуясь сайтом, соглашаюсь с политикой использования cookies

© 2022, tagree digital agency.