Галерея с лайтбоксом на WordPress без плагинов
Галерея с лайтбоксом на WordPress позволяет показывать изображения в компактной сетке, а при клике открывать выбранную фотографию поверх страницы. Посетителю не приходится переходить на отдельную запись или загружать полноразмерный файл в новой вкладке: затемнённое окно отображает картинку, а стрелки помогают листать остальные изображения.
Плагин для такой задачи не обязателен. Достаточно подготовить разметку, добавить несколько правил CSS и подключить небольшой JavaScript-скрипт через тему. Такой подход даёт полный контроль над внешним видом, не создаёт лишних пунктов в панели управления и помогает избежать зависимости от редко обновляемого расширения.
| Подход | Преимущества | Ограничения |
|---|---|---|
| Готовый плагин | Быстрая настройка, дополнительные эффекты, управление из админки | Лишний код, возможные конфликты и зависимость от обновлений |
| Чистые HTML, CSS и JavaScript | Полный контроль, высокая скорость, минимум зависимостей | Разметку и настройки нужно поддерживать самостоятельно |
| Блок галереи WordPress | Удобное добавление изображений в редакторе | Возможности лайтбокса зависят от темы и версии WordPress |
| Внешняя библиотека | Красивые анимации, готовая навигация и поддержка жестов | Нужно отдельно подключать файлы и следить за совместимостью |
Подготовка изображений и структуры галереи
Сначала загрузите фотографии в медиабиблиотеку WordPress. Для миниатюры используйте уменьшенную версию изображения, а для лайтбокса укажите оригинал или заранее подготовленный файл среднего размера. Не стоит открывать снимок размером 6000×4000 пикселей, если посетителю достаточно ширины 1600 пикселей.
Удобная HTML-структура состоит из контейнера галереи и отдельных ссылок. Ссылка нужна для доступности, возможности открыть изображение без JavaScript и корректной работы клавиатуры. Атрибут href содержит большую картинку, data-caption — подпись, а элемент img показывает превью.
<div class="simple-gallery">
<a class="simple-gallery__item"
href="/wp-content/uploads/2025/03/photo-large.jpg"
data-caption="Горный пейзаж">
<img src="/wp-content/uploads/2025/03/photo-thumb.jpg"
alt="Горный пейзаж"
loading="lazy"
width="480"
height="320">
</a>
</div>
Для нескольких изображений повторите блок a, меняя адреса и альтернативные описания. Атрибуты width и height помогают браузеру заранее зарезервировать место, поэтому страница меньше прыгает во время загрузки. Путь к файлам лучше получать средствами WordPress, если галерея формируется в шаблоне или шорткоде.
Разметка окна просмотра
Лайтбокс можно разместить один раз внизу шаблона страницы, сразу перед закрывающим тегом body. JavaScript будет подставлять в него выбранную фотографию. В окне понадобятся затемняющий фон, кнопка закрытия, изображение, подпись и две кнопки навигации.
<div class="lightbox" id="lightbox" aria-hidden="true">
<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>
<button class="lightbox__prev" type="button"
aria-label="Предыдущее изображение">‹</button>
<img class="lightbox__image" src="" alt="">
<button class="lightbox__next" type="button"
aria-label="Следующее изображение">›</button>
<p class="lightbox__caption"></p>
</div>
</div>
Кнопки получают понятные подписи для программ чтения с экрана. Атрибут aria-hidden="true" сообщает, что закрытое окно сейчас неактивно. При открытии его значение меняется на false, а фокус переводится на кнопку закрытия. Это делает интерфейс удобнее для пользователей, которые не применяют мышь.
Оформление сетки и лайтбокса
Сетку удобно создать с помощью CSS Grid. Свойство minmax() позволяет автоматически подбирать число колонок в зависимости от ширины экрана. object-fit: cover выравнивает карточки, даже если исходные фотографии имеют разные пропорции.
.simple-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}
.simple-gallery__item {
display: block;
overflow: hidden;
border-radius: 8px;
}
.simple-gallery__item img {
display: block;
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
transition: transform .2s ease, opacity .2s ease;
}
.simple-gallery__item:hover img,
.simple-gallery__item:focus img {
opacity: .86;
transform: scale(1.03);
}
Само окно просмотра должно занимать весь экран и располагаться поверх остальных элементов. Свойства position: fixed, inset: 0 и высокий z-index решают эту задачу. Ограничьте размеры изображения через max-width и max-height, чтобы оно не выходило за границы небольшого дисплея.
.lightbox {
position: fixed;
inset: 0;
z-index: 9999;
display: none;
place-items: center;
padding: 24px;
}
.lightbox.is-open {
display: grid;
}
.lightbox__backdrop {
position: absolute;
inset: 0;
background: rgb(0 0 0 / 80%);
}
.lightbox__dialog {
position: relative;
z-index: 1;
max-width: 1100px;
max-height: 90vh;
text-align: center;
}
.lightbox__image {
display: block;
max-width: 100%;
max-height: 78vh;
margin: auto;
}
.lightbox__close,
.lightbox__prev,
.lightbox__next {
position: absolute;
border: 0;
color: #fff;
background: rgb(0 0 0 / 55%);
cursor: pointer;
font-size: 30px;
}
.lightbox__close {
top: -44px;
right: 0;
}
.lightbox__prev,
.lightbox__next {
top: 50%;
padding: 4px 12px;
transform: translateY(-50%);
}
.lightbox__prev { left: 12px; }
.lightbox__next { right: 12px; }
.lightbox__caption {
margin: 10px 0 0;
color: #fff;
}
Логика открытия на JavaScript
Скрипт собирает все ссылки внутри текущей галереи, запоминает индекс выбранного элемента и обновляет содержимое окна. При клике отменяется переход по href, после чего показывается полноразмерное изображение. Для вставки подписи безопаснее использовать textContent, а не innerHTML: текст из атрибута не сможет превратиться в исполняемую разметку.
document.querySelectorAll('.simple-gallery').forEach((gallery) => {
const items = [...gallery.querySelectorAll('.simple-gallery__item')];
const lightbox = document.querySelector('#lightbox');
const image = lightbox.querySelector('.lightbox__image');
const caption = lightbox.querySelector('.lightbox__caption');
const closeButton = lightbox.querySelector('.lightbox__close');
let current = 0;
let previousFocus;
function show(index) {
current = (index + items.length) % items.length;
const item = items[current];
image.src = item.href;
image.alt = item.querySelector('img').alt;
caption.textContent = item.dataset.caption || '';
}
function open(index) {
previousFocus = document.activeElement;
show(index);
lightbox.classList.add('is-open');
lightbox.setAttribute('aria-hidden', 'false');
document.body.classList.add('lightbox-lock');
closeButton.focus();
}
function close() {
lightbox.classList.remove('is-open');
lightbox.setAttribute('aria-hidden', 'true');
document.body.classList.remove('lightbox-lock');
image.removeAttribute('src');
if (previousFocus) previousFocus.focus();
}
items.forEach((item, index) => {
item.addEventListener('click', (event) => {
event.preventDefault();
open(index);
});
});
lightbox.querySelector('.lightbox__prev')
.addEventListener('click', () => show(current - 1));
lightbox.querySelector('.lightbox__next')
.addEventListener('click', () => show(current + 1));
closeButton.addEventListener('click', close);
lightbox.querySelector('[data-close]')
.addEventListener('click', close);
document.addEventListener('keydown', (event) => {
if (!lightbox.classList.contains('is-open')) return;
if (event.key === 'Escape') close();
if (event.key === 'ArrowLeft') show(current - 1);
if (event.key === 'ArrowRight') show(current + 1);
});
});
Добавьте правило блокировки прокрутки:
body.lightbox-lock {
overflow: hidden;
}
В полном проекте стоит предусмотреть обработчик ошибки загрузки изображения, сенсорные свайпы и ограничение фокуса внутри диалогового окна. Для небольшой персональной галереи приведённой логики обычно достаточно, а код легко расширить без замены всей реализации. Идеи по аккуратному разделению компонентов можно почерпнуть из материалов о домашнем Kubernetes-кластере, где также важны независимость частей и понятная структура.
Подключение файлов в WordPress
Не вставляйте большой скрипт непосредственно в каждую запись. Создайте, например, файлы gallery.css и gallery.js в дочерней теме, а затем подключите их в functions.php. Функция wp_enqueue_scripts предотвращает дублирование ресурсов и позволяет WordPress правильно сформировать ссылки на файлы.
function site_gallery_assets() {
wp_enqueue_style(
'site-gallery',
get_stylesheet_directory_uri() . '/assets/gallery.css',
array(),
'1.0'
);
wp_enqueue_script(
'site-gallery',
get_stylesheet_directory_uri() . '/assets/gallery.js',
array(),
'1.0',
true
);
}
add_action('wp_enqueue_scripts', 'site_gallery_assets');
Если стили и скрипт нужны только на отдельных страницах, загрузку можно ограничить условием is_page() или проверкой наличия шорткода в содержимом. При работе с родительской темой используйте дочернюю тему, иначе изменения исчезнут после обновления. Для проекта, где много собственных технических решений, полезно вести отдельные заметки в технологическом блоге, чтобы не терять настройки и фрагменты кода.
Добавление галереи через шорткод
Чтобы не редактировать HTML вручную, можно создать простой шорткод. В записи он будет выглядеть так: [simple_gallery ids="12,15,18"]. Функция получает идентификаторы изображений, формирует миниатюры и связывает их с полноразмерными файлами. Все значения, выводимые в HTML, необходимо экранировать.
function simple_gallery_shortcode($atts) {
$atts = shortcode_atts(
array('ids' => ''),
$atts,
'simple_gallery'
);
$ids = array_filter(array_map('absint', explode(',', $atts['ids'])));
if (!$ids) {
return '';
}
$html = '<div class="simple-gallery">';
foreach ($ids as $id) {
$thumb = wp_get_attachment_image_url($id, 'medium');
$full = wp_get_attachment_image_url($id, 'large');
$alt = get_post_meta($id, '_wp_attachment_image_alt', true);
$caption = wp_get_attachment_caption($id);
if (!$thumb || !$full) {
continue;
}
$html .= sprintf(
'<a class="simple-gallery__item" href="%s" data-caption="%s">
<img src="%s" alt="%s" loading="lazy"></a>',
esc_url($full),
esc_attr($caption),
esc_url($thumb),
esc_attr($alt)
);
}
return $html . '</div>';
}
add_shortcode('simple_gallery', 'simple_gallery_shortcode');
Размер large зависит от настроек WordPress и обычно подходит для просмотра на экране. Если фотографии публикуются регулярно, настройте собственный размер через add_image_size(), чтобы не отдавать посетителям слишком тяжёлые файлы. Перед публикацией проверьте, что у каждого изображения есть осмысленный alt, а пустые или удалённые вложения не создают битых ссылок.
Проверка скорости и доступности
Откройте страницу на компьютере, планшете и телефоне. Проверьте, не выходит ли изображение за границы окна, видны ли кнопки на тёмном фоне и не появляется ли горизонтальная прокрутка. Отдельно протестируйте клавиши Tab, Enter, Esc и стрелки: галерея должна оставаться управляемой без мыши.
Для ускорения используйте WebP или AVIF, сжимайте оригиналы перед загрузкой и оставляйте loading="lazy" у превью. Большую картинку можно загружать только после клика, как в приведённом примере. Если на странице несколько галерей, лучше искать лайтбокс внутри соответствующего контейнера или создавать отдельное окно для каждой группы, чтобы индексы изображений не смешивались.
Проверьте HTML через инструменты разработчика и убедитесь, что в консоли нет ошибок. После установки кеширующего плагина или минификации JavaScript повторите тестирование: иногда объединение файлов меняет порядок выполнения скриптов. На странице обратной связи можно сообщить о проблемах совместимости через форму связи, если реализация используется в публичном проекте.
Соберите галерею из нескольких оптимизированных изображений, добавьте шорткод в тестовую запись и проверьте её на реальном мобильном устройстве. Такой вариант лайтбокса сохраняет контроль над кодом, не перегружает WordPress и даёт основу для дальнейшего добавления свайпов, анимаций и фильтрации по категориям.