Как сверстать блок статистики с помощью Flexbox

Эта статья основана на одном из практических заданий verstaem.online.

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

Перейти к заданию

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

Создаем HTML-разметку

Сначала создадим базовую HTML-разметку с помощью Emmet. Для этого достаточно ввести ! и нажать Tab. Обратите внимание на атрибут lang. Поскольку основной контент страницы написан на русском языке, здесь используем значение ru.

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Блок статистики</title>
  </head>
  <body>
  </body>
</html>

Переходим к верстке блока. Здесь у нас идут три одинаковых по смыслу элемента подряд, а значит удобно использовать список — <ul>. Это позволит скринридерам озвучивать количество элементов списка, что удобно для незрячих пользователей. Элементы сделаем, конечно, <li>, а вот внутри элементов уже пойдет основа. Давайте зафиксируем сам список.

<ul class="statistics">
  <li class="statistics__item">
  </li>
  <li class="statistics__item">
  </li>
  <li class="statistics__item">
  </li>
</ul>

Элемент <li> здесь будет управлять размером колонки, а уже внутри него построим сами блоки. Сделаем их тегом <div>. Слова «Подписчики», «Просмотры» и «Продажи» явно озаглавливают содержимое блока, поэтому сделаем их заголовками. Здесь выбор уровня заголовка зависит от контекста, но, поскольку у нас его нет, предлагаю представить, что весь этот блок — это секция сайта. Как правило, секциям мы даем заголовок <h2>, а внутренним элементам — <h3>, поэтому здесь я тоже сделаю <h3>. Кружок справа — декоративный элемент, и здесь есть простой совет. Если вы можете сделать декоративный элемент без добавления HTML-элемента — стоит сделать именно так. Ниже я покажу, как именно.

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

<ul class="statistics">
  <li class="statistics__item">
    <div class="statistics__card statistics__card--subs">
      <h3 class="statistics__title">Подписчики</h3>
      <div class="statistics__val">12K</div>
      <div class="statistics__bottom">
        <div class="statistics__date">
          За 30 дней
        </div>
        <div class="statistics__percent">
          +12%
        </div>
      </div>
    </div>
  </li>
  <li class="statistics__item">
    <div class="statistics__card statistics__card--views">
      <h3 class="statistics__title">Просмотры</h3>
      <div class="statistics__val">87K</div>
      <div class="statistics__bottom">
        <div class="statistics__date">
          За неделю
        </div>
        <div class="statistics__percent">
          +4%
        </div>
      </div>
    </div>
  </li>
  <li class="statistics__item">
    <div class="statistics__card statistics__card--purchases">
      <h3 class="statistics__title">Продажи</h3>
      <div class="statistics__val">324</div>
      <div class="statistics__bottom">
        <div class="statistics__date">
          За месяц
        </div>
        <div class="statistics__percent">
          +18%
        </div>
      </div>
    </div>
  </li>
</ul>

Обратите внимание, что для всех внутренних элементов, кроме заголовка, я использовал <div>, и это не ошибка. Если для элемента по смыслу не существует семантического тега, нет ничего плохого в том, чтобы использовать <div>, даже если их в итоге будет визуально много. Также важный момент — это дополнительные классы для карточки, вроде statistics__card--purchases. Это понадобится нам в будущем.

Добавляем стили

Подключим файл стилей через <link> и зададим несколько базовых правил. Для этого задания достаточно переопределить box-sizing, сбросить отступ у <body> и подключить шрифт Inter, который используется в макете.

* {
  box-sizing: border-box;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/InterRegular.woff2");
  font-display: swap;
  font-weight: 400;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/InterMedium.woff2");
  font-display: swap;
  font-weight: 500;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/InterSemiBold.woff2");
  font-display: swap;
  font-weight: 600;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/InterBold.woff2");
  font-display: swap;
  font-weight: 700;
}

body {
  margin: 0;
  font-family: "Inter", sans-serif;
}

Важный нюанс, который вы можете увидеть в коде — это шрифты. Я намеренно указал font-family внутри @font-face одинаковым, потому что сам шрифт имеет одно название — Inter, отличаются лишь его начертания. Именно так мы и будем делать — указываем шрифт для <body> один раз, а затем с помощью font-weight меняем толщину шрифта там, где нам нужно.

Идем дальше, и теперь нам стоит задать стили для списка. Здесь простой сброс, отступы, flexbox, а также max-width, чтобы ограничить ширину списка так, как в макете.

.statistics {
  display: flex;
  flex-wrap: wrap;
  margin: 0 auto;
  padding: 20px;
  max-width: 920px;
  list-style: none;
  gap: 20px;
}

После этого зададим стили элементу списка. Здесь мы используем значение flex: 1, что буквально говорит «каждый элемент займет одну часть от родителя». То есть, если элементов 3 — каждый займет одну треть (без учета отступов). Плюс к этому мы добавляем min-width: 220px, чтобы при добавлении новых элементов, они переносились на новую строку.

.statistics__item {
  flex: 1;
  min-width: 220px;
}

Далее пишем стили карточке. Здесь обводка, отступы, скругления — все просто.

.statistics__card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  border: 1px solid #e4e7ec;
  border-radius: 24px;
  padding: 24px;
  min-height: 165px;
}

Теперь раздадим стили всем текстовым элементам — заголовку, цифре, дате и процентам. Здесь все просто берется из макета, поэтому детально не останавливаемся.

.statistics__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  font-weight: 500;
  font-size: 14px;
  color: #6b7280;
}

.statistics__val {
  font-weight: 700;
  font-size: 44px;
  color: #111827;
}

.statistics__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.statistics__date {
  font-weight: 400;
  font-size: 12px;
  color: #6b7280;
}

.statistics__percent {
  font-weight: 600;
  font-size: 12px;
  color: #10b981;
}

Здесь есть пара нюансов. Первый — сброс отступов. Я не сбрасывал отступ глобально, поэтому нужно сбросить его локально — у элементов, которые имеют отступ по умолчанию. В нашем случае это заголовок <h3>. Также, заголовку я задал display: flex, а также justify-content и align-items. Это понадобится нам для размещения кружочка. Значение space-between позволяет разместить два элемента в строке по краям, что нам и нужно.

Теперь перейдем к стилизации кружочка. Как я говорил, делать такой элемент, создавая, например, пустой <span> — не очень хорошая практика. Гораздо лучше воспользоваться более удобным инструментом — псевдоэлементом ::before или ::after. Он создается с помощью CSS, поэтому нам не нужно будет менять разметку. И здесь мы приходим к идее CSS-переменных. Сперва покажу код, потом поясню.

.statistics__title::after {
  content: "";
  display: block;
  border-radius: 100%;
  width: 10px;
  height: 10px;
  background: var(--bg);
}

Мы создаем псевдоэлемент ::after, используя стандартные стили, задаем размеры, и вот фон для кружочка задаем через переменную var(--bg). Эта переменная еще нигде не задана, поэтому на данный момент цвета применены не будут, но ниже, используя дополнительные классы у карточек, мы можем явно обозначить цвет, который попадет в переменную.

.statistics__card--subs {
  --bg: #7c3aed;
}

.statistics__card--views {
  --bg: #f59e0b;
}

.statistics__card--purchases {
  --bg: #10b981;
}

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

.statistics__card--subs::after {
  background: #7c3aed;
}

.statistics__card--views::after {
  background: #f59e0b;
}

.statistics__card--purchases::after {
  background: #10b981;
}

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

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

Решение задачи

предыдущий пост следующий пост