Как сверстать панель аналитики

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

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

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

В этой статье мы сверстаем панель аналитики, подобную тем, которые можно встретить в CRM-системах, интернет-магазинах и административных панелях. По пути разберем сложную сетку на CSS Grid, научимся комбинировать Grid и Flexbox, оформим статистические карточки, список популярных товаров и увидим, как строятся реальные Dashboard-интерфейсы.

Создаем 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>

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

<div class="content">
    <div class="container grid">

    </div>
</div>

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

<div class="content__main">
  <h2 class="content__title">
    Панель аналитики
  </h2>
  <p class="content__descr">
    Основные показатели интернет-магазина за последние семь дней
  </p>
</div>
<div class="content__revenue">
  <h2 class="content__subtitle">
    Выручка
  </h2>
  <div class="content__val">
    2,48 млн
  </div>
  <div class="content__percent">
    ▲ +14%
  </div>
</div>

Далее идет контент чуть сложнее. Слева — список популярных товаров, а справа список статистики. Делаем все это именно списками, и обязательно выделяем карточки товара как отдельный элемент — <article>, так как этот тег отлично подходит для независимых элементов.

Обратите внимание на заголовки. «Популярные товары» это h2, заголовки товаров — h3, а вот заголовки в статистике уже h4. Все по иерархии, постепенно уменьшается смысловая нагрузка.

  <div class="content__popular">
    <h2 class="content__caption">
      Популярные товары
    </h2>
    <ul class="content__items">
      <li class="content__item">
        <article class="product">
          <img src="img/1.png" alt="MacBook Air" class="product__pic">
          <div class="product__text">
            <h3 class="product__title">
              MacBook Air
            </h3>
            <div class="product__quantity">
              184 продажи
            </div>
          </div>
        </article>
      </li>
      <li class="content__item">
        <article class="product">
          <img src="img/2.png" alt="AirPods Pro" class="product__pic">
          <div class="product__text">
            <h3 class="product__title">
              AirPods Pro
            </h3>
            <div class="product__quantity">
              126 продаж
            </div>
          </div>
        </article>
      </li>
      <li class="content__item">
        <article class="product">
          <img src="img/3.png" alt="Studio Display" class="product__pic">
          <div class="product__text">
            <h3 class="product__title">
              Studio Display
            </h3>
            <div class="product__quantity">
              48 продаж
            </div>
          </div>
        </article>
      </li>
    </ul>
  </div>
  <div class="content__statistics">
    <ul class="content__list">
      <li class="content__list-item">
        <h4 class="content__subt">Заказы</h4>
        <div class="content__meaning">
          184
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Клиенты</h4>
        <div class="content__meaning">
          96
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Конверсия</h4>
        <div class="content__meaning">
          5.8%
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Возвраты</h4>
        <div class="content__meaning">
          67
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Средний чек</h4>
        <div class="content__meaning">
          ₽13k
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Товаров</h4>
        <div class="content__meaning">
          812
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Отзывы</h4>
        <div class="content__meaning">
          41
        </div>
      </li>
      <li class="content__list-item">
        <h4 class="content__subt">Подписки</h4>
        <div class="content__meaning">
          67
        </div>
      </li>
    </ul>
  </div>
</div>

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

Подключим файл стилей через <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/InterBold.woff2");
  font-display: swap;
  font-weight: 700;
}

body {
  margin: 0;
  padding: 20px;
  background: #20242b;
  font-family: "Inter", sans-serif;
}

.container {
  margin: 0 auto;
  padding: 0 15px;
  max-width: 1310px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
}

Здесь обращаем внимание на grid, мы описываем в нем общую структуру сетки. repeat(12, 1fr) — 12 указано, потому что в макете сетка из 12 колонок. Размер контейнера же в макете 1280, но поскольку мы добавляем слева и справа по 15 пикселей, в итоге выходит 1310.

Далее поработаем с элементами первой строки. Они очень простые, поэтому детально останавливаться не будем. Укажу лишь, что мы явно используем здесь grid-column, чтобы дать понять браузеру, сколько колонок должна занимать каждая часть.

.content__main {
  grid-column: 8 span;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 20px;
  padding: 24px;
  background: #4338ca;
}

.content__title {
  margin: 0;
  margin-bottom: 12px;
  font-weight: 700;
  font-size: 36px;
  line-height: 100%;
  color: #fff;
}

.content__descr {
  margin: 0;
  font-weight: 400;
  font-size: 16px;
  line-height: 120%;
  color: rgba(255, 255, 255, 0.9);
}

.content__revenue {
  display: flex;
  flex-direction: column;
  gap: 12px;
  grid-column: 4 span;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 20px;
  padding: 24px;
  background: #2d333d;
}

.content__subtitle {
  margin: 0;
  font-weight: 400;
  font-size: 14px;
  line-height: 100%;
  color: #94a3b8;
}

.content__val {
  font-weight: 700;
  font-size: 48px;
  line-height: 100%;
  color: #fff;
}

.content__percent {
  font-weight: 700;
  font-size: 16px;
  line-height: 100%;
  color: #22c55e;
}

Идем дальше, и здесь у нас две колонки с двумя списками. Займемся заголовком и списком популярных товаров.

.content__popular {
  grid-column: 4 span;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 20px;
  padding: 24px;
  background: #2d333d;
}

.content__caption {
  margin: 0;
  margin-bottom: 20px;
  font-weight: 700;
  font-size: 14px;
  line-height: 100%;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: #94a3b8;
}

.content__items {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.content__item:last-child .product {
  border: none;
}

Здесь есть один важный момент — это работа с border. Саму обводку мы еще не задали, она будет ниже, но уже сейчас отмечаем, что у последнего элемента списка ищем .product, и у него сбрасываем обводку, т. к. по макету она не нужна.

Далее стилизуем непосредственно товары. Из важного здесь конечно работа с изображением. Ему стоит четко задать размеры, скругления, а также указать object-fit: cover, чтобы при вставке изображения даже другого размера, оно встало как надо.

.product {
  display: flex;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid #555;
  padding: 12px 0;
}

.product__pic {
  display: block;
  border-radius: 12px;
  width: 52px;
  height: 52px;
  object-fit: cover;
}

.product__title {
  margin: 0;
  margin-bottom: 8px;
  font-weight: 700;
  font-size: 16px;
  line-height: 100%;
  color: #fff;
}

.product__quantity {
  font-weight: 400;
  font-size: 12px;
  line-height: 100%;
  color: #94a3b8;
}

Теперь статистика. Здесь я решил снова использовать grid, и поэтому внутри блока мы создадим свою сетку, которая будет состоять из 4х колонок, по количеству элементов в строке — grid-template-columns: repeat(4, 1fr);. Остальное в этом блоке делается просто, поэтому просто показываю код.

.content__statistics {
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 20px;
  padding: 24px;
  grid-column: 8 span;
  background: #2d333d;
}

.content__list {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.content__list-item {
  border-radius: 16px;
  padding: 16px;
  min-height: 122px;
  background: #363d48;
}

.content__subt {
  margin: 0;
  margin-bottom: 12px;
  font-weight: 400;
  font-size: 12px;
  line-height: 100%;
  color: #94a3b8;
}

.content__meaning {
  font-weight: 700;
  font-size: 28px;
  line-height: 100%;
  color: #fff;
}

Теперь вы умеете создавать Dashboard-интерфейсы с помощью CSS Grid. Мы построили многоуровневую сетку, объединили различные компоненты страницы, сверстали карточки статистики и список популярных товаров. Именно подобные приемы используются при разработке CRM, административных панелей, аналитических сервисов и корпоративных приложений.

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

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