Простой лайтбокс на CSS и JavaScript

Лайтбокс — это компактное окно поверх страницы, в котором увеличенное изображение открывается без перехода на отдельную страницу. Такой элемент часто используют в фотогалереях, портфолио, каталогах товаров и публикациях с иллюстрациями. Пользователь нажимает на миниатюру и получает удобный просмотр фотографии с затемнённым фоном.

Для базовой реализации не нужны библиотеки вроде jQuery или готовые плагины. Достаточно разметки HTML, нескольких правил CSS и небольшого сценария JavaScript. Такой подход сохраняет контроль над кодом, уменьшает количество зависимостей и позволяет адаптировать внешний вид под конкретную тему WordPress.

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

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

Подготовка HTML-разметки

Для начала создадим несколько миниатюр. Каждая ссылка содержит атрибут data-full, в котором указан адрес полноразмерного файла. Атрибут alt описывает содержимое изображения и помогает пользователям экранных дикторов, а также поисковым системам.

<div class="gallery">
  <a class="gallery__link"
     href="images/photo-1-large.jpg"
     data-full="images/photo-1-large.jpg">
    <img src="images/photo-1-small.jpg"
         alt="Горный пейзаж на рассвете">
  </a>

  <a class="gallery__link"
     href="images/photo-2-large.jpg"
     data-full="images/photo-2-large.jpg">
    <img src="images/photo-2-small.jpg"
         alt="Старый городской мост">
  </a>
</div>

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

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

<div class="lightbox" id="lightbox" hidden>
  <div class="lightbox__backdrop" data-close></div>

  <div class="lightbox__dialog"
       role="dialog"
       aria-modal="true"
       aria-label="Просмотр изображения">
    <button class="lightbox__close"
            type="button"
            aria-label="Закрыть изображение">
      ×
    </button>

    <img class="lightbox__image" src="" alt="">
    <p class="lightbox__caption"></p>
  </div>
</div>

Оформление окна средствами CSS

Затемнённый фон должен перекрывать весь экран, поэтому для .lightbox используется фиксированное позиционирование и высокий z-index. Свойство display: grid помогает быстро расположить содержимое по центру, а внутренние отступы защищают изображение от соприкосновения с краями небольшого экрана.

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgb(0 0 0 / 78%);
  opacity: 1;
  visibility: visible;
  transition: opacity 180ms ease, visibility 180ms ease;
}

.lightbox[hidden] {
  display: grid;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.lightbox__dialog {
  position: relative;
  max-width: min(100%, 1100px);
  max-height: 100%;
  transform: scale(1);
  transition: transform 180ms ease;
}

.lightbox[hidden] .lightbox__dialog {
  transform: scale(.94);
}

Для самой фотографии важно задать ограничения по ширине и высоте. Значение object-fit: contain сохраняет пропорции изображения, поэтому оно не будет обрезано. Кнопка закрытия располагается поверх изображения и остаётся доступной даже на мобильном устройстве.

.lightbox__image {
  display: block;
  max-width: 100%;
  max-height: 82vh;
  border-radius: 8px;
  box-shadow: 0 12px 45px rgb(0 0 0 / 35%);
  object-fit: contain;
}

.lightbox__close {
  position: absolute;
  top: -14px;
  right: -14px;
  z-index: 2;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  color: #222;
  background: #fff;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
}

.lightbox__caption {
  margin: 10px 0 0;
  color: #fff;
  text-align: center;
  font-size: .95rem;
}

Переход между состояниями создаёт впечатление плавного появления, хотя фактически JavaScript только добавляет и убирает атрибут hidden. Если в проекте используется тёмная тема, цвета фона, текста и кнопки можно вынести в CSS-переменные.

Логика открытия и закрытия

JavaScript найдёт все ссылки галереи, модальное окно и его дочерние элементы. При клике на миниатюру стандартный переход по ссылке отменяется, адрес изображения берётся из data-full, а альтернативный текст копируется в атрибут alt увеличенной фотографии.

const lightbox = document.querySelector('#lightbox');
const lightboxImage = lightbox.querySelector('.lightbox__image');
const lightboxCaption = lightbox.querySelector('.lightbox__caption');
const closeButton = lightbox.querySelector('.lightbox__close');
const galleryLinks = document.querySelectorAll('.gallery__link');

function openLightbox(link) {
  const image = link.querySelector('img');

  lightboxImage.src = link.dataset.full || link.href;
  lightboxImage.alt = image.alt;
  lightboxCaption.textContent = image.alt;

  lightbox.hidden = false;
  closeButton.focus();
  document.body.classList.add('no-scroll');
}

function closeLightbox() {
  lightbox.hidden = true;
  lightboxImage.src = '';
  document.body.classList.remove('no-scroll');
}

galleryLinks.forEach((link) => {
  link.addEventListener('click', (event) => {
    event.preventDefault();
    openLightbox(link);
  });
});

closeButton.addEventListener('click', closeLightbox);

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

lightbox.addEventListener('click', (event) => {
  if (event.target.hasAttribute('data-close')) {
    closeLightbox();
  }
});

document.addEventListener('keydown', (event) => {
  if (event.key === 'Escape' && !lightbox.hidden) {
    closeLightbox();
  }
});
.no-scroll {
  overflow: hidden;
}

Анимация и доступность

Плавность здесь обеспечивается переходами opacity и transform. Не стоит добавлять сложную анимацию, которая задерживает закрытие окна или мешает просмотру фотографии. Для интерфейса галереи важнее быстрое появление содержимого, предсказуемая кнопка закрытия и корректная работа с клавиатурой.

Состояние диалога обозначено атрибутами role="dialog" и aria-modal="true". Кнопка имеет понятное описание через aria-label, а фокус после открытия переводится на неё. Это позволяет пользователю клавиатуры сразу закрыть окно клавишей Enter или пробелом.

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

let lastTrigger = null;

function openLightbox(link) {
  const image = link.querySelector('img');

  lastTrigger = link;
  lightboxImage.src = link.dataset.full || link.href;
  lightboxImage.alt = image.alt;
  lightboxCaption.textContent = image.alt;
  lightbox.hidden = false;
  closeButton.focus();
  document.body.classList.add('no-scroll');
}

function closeLightbox() {
  lightbox.hidden = true;
  lightboxImage.src = '';
  document.body.classList.remove('no-scroll');

  if (lastTrigger) {
    lastTrigger.focus();
  }
}

Пользователям, чувствительным к движению, можно отключить переходы через медиазапрос prefers-reduced-motion. В таком режиме окно будет открываться без масштабирования и плавного затухания.

@media (prefers-reduced-motion: reduce) {
  .lightbox,
  .lightbox__dialog {
    transition: none;
  }
}

Оптимизация изображений для сайта

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

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

Для ускорения страницы можно применять современные форматы WebP или AVIF, но нужно сохранить запасной вариант для старых браузеров и внешних инструментов. В HTML допустимо использовать элемент picture, а для изображений галереи — атрибуты loading="lazy" и decoding="async".

<img src="images/photo-1-small.webp"
     alt="Горный пейзаж на рассвете"
     loading="lazy"
     decoding="async">

Не следует лениво загружать первое изображение, которое сразу видно пользователю: это может ухудшить показатель Largest Contentful Paint. Для остальных миниатюр отложенная загрузка обычно даёт заметный эффект на длинных страницах WordPress.

Проверка и доработка в WordPress

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

В WordPress скрипт лучше подключать через wp_enqueue_script, а стили — через wp_enqueue_style, а не вставлять одинаковые блоки вручную в каждую запись. Селекторы с префиксом вроде gallery__ или site-lightbox__ снижают риск конфликта с CSS темы и плагинов.

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

Перед публикацией полезно проверить HTML, работу клавиши Escape, возврат фокуса, отсутствие прокрутки фона и отображение при отключённом JavaScript. Не стоит забывать и о защите административной части сайта: рекомендации по хранению паролей помогут снизить риски при работе с WordPress и другими системами.

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

Иконка пользователя в серых тонах

Настройка Windows

Доступ к скрытым параметрам конфигурации, отключение телеметрии, работа с диспетчером задач и тонкая настройка меню «Пуск» в стиле Windows XP для Windows 7.

Иконка браузера в серо-синих тонах

Веб-браузеры

Скрытые страницы конфигурации в Firefox, Chrome и Opera, быстрый текстовый редактор прямо в браузере, эффект Lightbox JS для изображений и настройка внешнего вида Firefox.

Иконка папки в нейтральных серых тонах

WordPress и блоги

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

Изометрический 3D-рендер: светлый веб-сайт с серо-голубым заголовком, парящий над чистой белой поверхностью Изометрический 3D-рендер: тёмно-серая иконка социального букмаркинга на светлом фоне

Обратная связь

Есть вопрос или предложение по темам блога? Напишите — автор на связи.