Галерея — это две вещи: сетка маленьких картинок и окно, в котором открывается большая. Обе делаются почти без кода: сетку раскладывает CSS, а окно умеет сам браузер — для этого есть тег <dialog>.

Когда галерея, а когда просто картинки

Если снимков три-четыре и каждый что-то объясняет, ставьте их прямо в текст: разглядывать их отдельно никто не будет. Галерея нужна, когда снимков десяток и больше, они равнозначны, а подробности видны только на большом размере, — фотографии, работы, скриншоты.

Рядом на этом сайте

Фоторяд шагов на странице про Open Server устроен ровно так же, как описано ниже: щелчок по снимку открывает его в окне. Разница только в раскладке — там шаги идут по порядку, а в галерее снимки равнозначны.

Разметка: сетка ссылок, а не сетка картинок

Главный приём здесь такой: превью оборачивается ссылкой на большой файл. Тогда галерея работает даже с выключенным JavaScript — щелчок просто откроет снимок отдельной страницей. Скрипт потом перехватит этот щелчок и покажет окно.

Один снимок в сетке
<ul class="gallery">
  <li class="gallery__item">
    <a class="gallery__link" href="/assets/img/work-1.webp" data-lightbox>
      <img src="/assets/img/work-1-thumb.webp"
           width="400" height="300" loading="lazy"
           alt="Главная страница сайта школы: шапка и первый экран">
    </a>
  </li>
</ul>

Атрибуты width и height обязательны, даже если размер задан в CSS. Браузер по ним заранее оставляет место под картинку, и текст под галереей не прыгает, пока снимки грузятся. А loading="lazy" откладывает загрузку тех, до которых читатель ещё не долистал.

Окно увеличения: готовый тег вместо своего кода

Самодельное окно из <div> нужно учить четырём вещам: закрываться по Esc, не выпускать фокус наружу, возвращать фокус на ту картинку, с которой открылось, и затемнять страницу. Тег <dialog>, открытый методом showModal, делает всё это сам.

Весь лайтбокс целиком
const box = document.createElement('dialog');
box.className = 'lightbox';
box.innerHTML = '<img class="lightbox__img" alt="">';
document.body.append(box);

document.addEventListener('click', (event) => {
  const link = event.target.closest('a[data-lightbox]');
  if (!link) return;

  event.preventDefault();                  // не уходим на страницу снимка
  const img = box.querySelector('img');
  img.src = link.href;
  img.alt = link.querySelector('img').alt; // подпись берём у превью
  box.showModal();                         // Esc и возврат фокуса — сами
});

// щелчок по затемнению закрывает окно
box.addEventListener('click', (event) => {
  if (event.target === box) box.close();
});

Обработчик висит на всём документе, а не на каждой ссылке: так галерея переживёт добавление снимков скриптом и не потребует переподписки. Затемнение настраивается псевдоэлементом ::backdrop — это тоже готовая возможность браузера.

Разметку окна пишет скрипт

В HTML страницы пустого <dialog> быть не должно. Иначе на странице без JavaScript висит скрытый блок, а на странице с ним — разметка, которую придётся копировать во все файлы сайта. Окно нужно ровно тогда, когда по снимку щёлкнули, — тогда его и создаём.

Вес страницы: превью — это отдельный файл

Частая ошибка новичка — поставить в сетку тот же файл, что открывается в окне, и уменьшить его стилями. Страница с двадцатью снимками по три мегабайта весит шестьдесят мегабайт и на телефоне не открывается вовсе.

Два размера одного снимка
Файл Размер Формат
Превью в сетке 400×300, до 60 КБ WebP
Большой снимок в окне по длинной стороне 1600, до 400 КБ WebP

Что попросить у нейросети

Мой запрос

Нужен лайтбокс на чистом JavaScript, без библиотек и сборщика. Разметка галереи: список, в каждом пункте ссылка с атрибутом data-lightbox на большой снимок, внутри ссылки превью с осмысленным alt.

Окно сделай тегом dialog и открывай методом showModal, разметку окна создавай скриптом, в HTML её быть не должно. Нужны кнопки «Предыдущий» и «Следующий», переход по стрелкам клавиатуры, закрытие по Esc и по щелчку в затемнение, подпись под снимком из alt превью.

Пока большой снимок грузится, покажи в окне превью, растянутое на его место, — иначе окно открывается пустым. Комментарии по-русски.

Ответ нейросети

Про стрелки и Esc. Esc обрабатывать не нужно: showModal уже закрывает окно этой клавишей. А вот стрелки повесьте на событие keydown самого окна, а не документа, — иначе они будут листать галерею и тогда, когда окно закрыто.

Про подпись. Если подпись выводится текстом под снимком, у картинки в окне alt надо оставить пустым. Иначе программа чтения экрана прочитает одно и то же дважды.

Про прокрутку под окном. При showModal браузер сам не всегда блокирует прокрутку страницы позади. Надёжнее на время открытого окна ставить overflow: hidden элементу html и снимать при закрытии.

Про подмену снимка. Ставьте src превью сразу, а большой снимок грузите в отдельный объект Image и подставляйте по событию загрузки. Тогда окно никогда не мигает белым.

Галерея из Evolution CMS

Снимки в CMS хранят двумя способами. Первый — TV-поле типа «изображение» с возможностью добавлять строки: вся галерея лежит в одной странице, и редактор добавляет снимки кнопкой «Добавить». Второй — папка ресурсов, где каждый снимок отдельная запись с описанием и датой.

Что выбрать под задачу
Способ Когда подходит
TV-поле со списком картинок Галерея принадлежит одной странице: снимки товара, фото мероприятия
Папка ресурсов Снимки нужны и по отдельности: у каждого свой адрес, описание, дата

В обоих случаях разметку одного снимка задаёт чанк — та же, что в первом блоке кода, только с метками полей вместо настоящих адресов. Превью при этом делает не редактор вручную: уменьшенную копию строит phpThumb, которому передают нужную ширину.

Частые ошибки

Чем чаще всего ломают галерею
Ошибка Чем плохо Как правильно
Превью — это большой файл в CSS Страница весит десятки мегабайт Отдельный уменьшенный файл под сетку
Окно собрано из div Фокус уходит на страницу позади, Esc не закрывает Тег dialog и метод showModal
Превью не обёрнуто ссылкой Без JavaScript снимок не открыть никак Ссылка на большой файл, щелчок перехватывает скрипт
У картинок нет размеров Текст прыгает, пока снимки грузятся width и height в разметке
Пустой alt у всех снимков Для части читателей галереи просто нет Описание того, что на снимке, у каждого превью

Что должно получиться

  • Щелчок по превью открывает снимок в окне поверх страницы
  • Окно закрывается по Esc и щелчком в затемнение, фокус возвращается на превью
  • С выключенным JavaScript снимок всё равно открывается по ссылке
  • Сетка не прыгает при загрузке: у картинок заданы размеры
  • Превью весит десятки килобайт, а не мегабайты