Как сверстать галерею фотографий на CSS Grid

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

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

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

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

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

Теперь давайте верстать галерею. Начнем мы с того, что сделаем секцию — <section>. Внутри разместим контейнер — блок, который центрирует контент. Ну, а затем реализуем верхушку. Тут будет заголовок и кнопка.

<div class="gallery__top">
  <h2 class="gallery__title">Галерея</h2>
   <button class="gallery__btn">Загрузить</button>
</div>

Саму галерею будет делать стандартно — списком, поскольку у нас одинаковые элементы идут друг за другом. Для списка, помимо прочего, добавим класс grid. Это нужно для того, чтобы управлять CSS Grid на этом элементе.

<ul class="gallery__list grid">
   <li class="gallery__item">
   </li>
</ul>

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

<section class="gallery">
  <div class="container">
    <div class="gallery__top">
      <h2 class="gallery__title">Галерея</h2>
      <button class="gallery__btn">Загрузить</button>
    </div>
    <ul class="gallery__list grid">
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/1.png" alt="Горное озеро" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Горное озеро
            </h3>
            <time datetime="2026-06-12 00:00" class="gallery-card__time">12 Июня 2026</time>
          </div>
        </div>
      </li>
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/2.png" alt="Ночной город" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Ночной город
            </h3>
            <time datetime="2026-06-10 00:00" class="gallery-card__time">10 Июня 2026</time>
          </div>
        </div>
      </li>
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/3.png" alt="Океанский пляж" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Океанский пляж
            </h3>
            <time datetime="2026-06-08 00:00" class="gallery-card__time">8 Июня 2026</time>
          </div>
        </div>
      </li>
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/4.png" alt="Лесная тропа" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Лесная тропа
            </h3>
            <time datetime="2026-06-06 00:00" class="gallery-card__time">6 Июня 2026</time>
          </div>
        </div>
      </li>
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/5.png" alt="Водопад" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Водопад
            </h3>
            <time datetime="2026-06-04 00:00" class="gallery-card__time">4 Июня 2026</time>
          </div>
        </div>
      </li>
      <li class="gallery__item">
        <div class="gallery-card">
          <img src="img/6.png" alt="Городские улицы" class="gallery-card__pic">
          <div class="gallery-card__text">
            <h3 class="gallery-card__title">
              Городские улицы
            </h3>
            <time datetime="2026-06-02 00:00" class="gallery-card__time">2 Июня 2026</time>
          </div>
        </div>
      </li>
    </ul>
  </div>
</section>

Обязательно заполняйте alt у изображений по смыслу, а также поддерживайте верный формат атрибута datetime.

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

Подключим файл стилей через <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;
  padding: 20px;
  background: #f4f6f9;
  font-family: "Inter", sans-serif;
}

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

Далее создаем контейнер. Это блок, который должен быть по центру. Видим в макете ширину 1200, но нам стоит быть умнее, и предусмотреть отступы. Ведь если сайт будет сжиматься, его контент не должен липнуть к краям. Чтобы это реализовать, нужно добавить padding. Именно поэтому, когда мы добавим отступ, нужно и ширину изменить. Сделаем отступы по 15, получится 1200+15+15 = 1230.

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

Далее поработаем с классом grid. Нам нужно определить нашу сетку. Если посмотреть в макет, увидим три широкие колонки, разделенные отступом. Именно на это и стоит ориентироваться.

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

Мы задаем display: grid, а затем свойство grid-template-columns, которое управляет колонками. Написав функцию repeat(), и передав в нее два параметра (первый — количество колонок, второй — их размер), мы получаем три идентичных по ширине колонки, которые будут тянуться. И сюда же добавляем gap.

Далее реализуем верхушку. Тут все просто. С помощью flexbox разместим элементы в строку, раскинем их по сторонам, и зададим стили самим элементам. Не забудьте написать font-family для кнопки.

.gallery__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 36px;
  gap: 20px;
}

.gallery__title {
  margin: 0;
  font-weight: 700;
  font-size: 32px;
  line-height: 100%;
  color: #111827;
}

.gallery__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 12px;
  padding: 12px 24px;
  font-family: inherit;
  font-weight: 500;
  font-size: 16px;
  color: #f4f6f9;
  background: #111827;
  cursor: pointer;
  transition: background 0.3s;
}

.gallery__btn:hover {
  background: #2a3b5f;
}

В качестве стилей списка будут только сбросы. Сброс margin, padding и list-style. А дальше уже делаем карточку. Здесь обязательно нужно указать overflow: hidden, иначе картинка не обрежется по углу карточки. Самому изображению обязательно нужно задать display: block и max-width: 100%, чтобы изображение встало по размеру карточки. В остальном просто забираем стили из макета.

.gallery__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery-card {
  position: relative;
  overflow: hidden;
  border-radius: 16px;
  background: #fff;
}

.gallery-card__pic {
  display: block;
  max-width: 100%;
}

.gallery-card__text {
  padding: 16px;
}

.gallery-card__title {
  margin: 0;
  margin-bottom: 8px;
  font-weight: 600;
  font-size: 20px;
  line-height: 100%;
  color: #111827;
}

.gallery-card__time {
  font-weight: 400;
  font-size: 12px;
  line-height: 100%;
  color: #6b7280;
}

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

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

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