Что мы создаём
В этом уроке разберём готовый сниппет, который добавляет на страницы WordPress красивую галерею на основе Swiper.js с открытием фотографий в лайтбоксе. Особенность решения — оно работает только на страницах определённого типа записи, не нагружая остальные страницы сайта лишними скриптами.

Что получится в итоге:
- адаптивный слайдер с 1–3 колонками в зависимости от ширины экрана;
- автопрокрутка с паузой при наведении;
- открытие полноразмерного фото в лайтбоксе по клику;
- навигация в лайтбоксе стрелками мыши и клавиатуры;
- закрытие лайтбокса по клику на фон или клавише Escape;
- шорткод работает только на страницах типа
development.
Что понадобится
- WordPress сайт
- Плагин Advanced Custom Fields (ACF) — бесплатная версия подойдёт
- Поле типа Gallery в ACF с именем
gallery, привязанное к нужному типу записи - Доступ к файлу
functions.phpвашей темы (или возможность добавить сниппет через плагин, например Code Snippets)
Шаг 1. Создаём поле галереи в ACF
- Зайдите в ACF → Группы полей → Добавить новую.
- Назовите группу, например
Галерея объекта. - Добавьте поле:
- Тип поля: Gallery
- Имя поля:
gallery(важно — именно так, строчными буквами)
- В разделе Правила отображения выберите:
Тип записи→равно→development(или ваш тип записи). - Сохраните группу.
Шаг 2. Добавляем сниппет в functions.php
Откройте файл functions.php вашей активной темы и вставьте код в конец файла. Если вы используете плагин Code Snippets — создайте новый сниппет и вставьте туда.
php
add_shortcode('custom_acf_gallery', 'display_acf_gallery_slider_lightbox');
function display_acf_gallery_slider_lightbox($atts) {
// Работаем только на страницах типа 'development'
if (!is_singular('development')) {
return '';
}
$images = get_field('gallery');
// ... остальной код сниппета
}
Важно: не редактируйте
functions.phpнапрямую без резервной копии. Ошибка в этом файле может сделать сайт недоступным. Безопаснее использовать плагин Code Snippets.
Шаг 3. Разбираем код по частям
Регистрация шорткода
php
add_shortcode('custom_acf_gallery', 'display_acf_gallery_slider_lightbox');
Эта строка говорит WordPress: когда в контенте встретится — вызвать функцию display_acf_gallery_slider_lightbox и вставить её результат на место шорткода.
Ограничение по типу записи
php
if (!is_singular('development')) {
return '';
}
Первое, что делает функция — проверяет тип текущей страницы. Если это не development, шорткод просто возвращает пустую строку. Благодаря этому скрипты и стили Swiper не загружаются там, где они не нужны.
Если у вас другой тип записи — замените 'development' на свой, например 'projects' или 'property'.
Получение изображений из ACF
php
$images = get_field('gallery');
get_field() — функция ACF, которая берёт данные поля gallery для текущей записи. Возвращает массив изображений, каждое из которых содержит:
$image['url']— полный URL оригинала;$image['sizes']['large']— URL в размере large (обычно 1024px);$image['alt']— alt-текст изображения.
Уникальный ID слайдера
php
$slider_id = 'swiper-' . uniqid();
uniqid() генерирует уникальную строку на основе времени. Это позволяет безопасно использовать шорткод несколько раз на одной странице — каждый слайдер получит свой ID и не будет конфликтовать с другими.
Подключение стилей Swiper
php
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
Стили Swiper подключаются через CDN прямо в выводе шорткода. Это простое решение, но если на странице несколько галерей — стили подключатся несколько раз. Для продакшн-сайта лучше вынести подключение в wp_enqueue_styles.
HTML-структура слайдера
php
<div id="swiper-xxxx" class="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide" onclick="...">
<img src="..." alt="..." />
</div>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
Это стандартная структура Swiper.js. Каждый слайд — это одно изображение из галереи ACF. На каждый слайд вешается обработчик onclick, который открывает лайтбокс с нужным индексом.
Инициализация Swiper
javascript
const swiper = new Swiper("#swiper-xxxx", {
loop: true,
speed: 600,
grabCursor: true,
autoplay: { delay: 3000, disableOnInteraction: false, pauseOnMouseEnter: true },
pagination: { el: ".swiper-pagination", clickable: true },
navigation: { nextEl: ".swiper-button-next", prevEl: ".swiper-button-prev" },
breakpoints: {
320: { slidesPerView: 1, spaceBetween: 15 },
768: { slidesPerView: 2, spaceBetween: 20 },
1024: { slidesPerView: 3, spaceBetween: 30 }
}
});
Ключевые параметры:
loop: true— бесконечная прокрутка;autoplay— автопрокрутка каждые 3 секунды, останавливается при наведении мыши;breakpoints— адаптив: 1 слайд на мобильных, 2 на планшетах, 3 на десктопе.
Лайтбокс — как это работает
Все URL оригинальных изображений сохраняются в глобальный объект window.acfGalleries:
javascript
window.acfGalleries["swiper-xxxx"] = ["url1.jpg", "url2.jpg", ...];
При клике на слайд вызывается openCustomLightbox(index, galId), которая запоминает текущую галерею и индекс фото, затем показывает лайтбокс.
HTML лайтбокса создаётся один раз и вставляется в конец <body>, даже если на странице несколько галерей:
javascript
if (!document.getElementById("custom-vanilla-lightbox")) {
// создаём лайтбокс только один раз
}
Навигация по лайтбоксу:
- Стрелки на экране — клик по
cvl-prev/cvl-next; - Клавиатура — стрелки влево/вправо, Escape для закрытия;
- Клик на фон — закрывает лайтбокс.
Шаг 4. Вставляем шорткод на страницу
После добавления кода в functions.php откройте редактор любой записи типа development и вставьте шорткод:
[custom_acf_gallery]
Не забудьте загрузить изображения в поле Gallery этой записи через ACF.
Шаг 5. Настройка под свой проект
Изменить тип записи
Замените 'development' на нужный тип в двух местах:
php
if (!is_singular('development')) {
Изменить имя поля ACF
Если ваше поле называется иначе — замените 'gallery':
php
$images = get_field('your_field_name');
Изменить количество слайдов
Отредактируйте breakpoints в инициализации Swiper:
javascript
breakpoints: {
320: { slidesPerView: 1, spaceBetween: 15 },
768: { slidesPerView: 3, spaceBetween: 20 }, // 3 на планшете
1024: { slidesPerView: 4, spaceBetween: 30 } // 4 на десктопе
}
Отключить автопрокрутку
Удалите или закомментируйте строку autoplay:
javascript
// autoplay: { delay: 3000, disableOnInteraction: false, pauseOnMouseEnter: true },
Изменить соотношение сторон изображений
Найдите в CSS:
css
aspect-ratio: 4/3;
И замените на нужное, например 16/9 или 1/1.
Возможные проблемы
Галерея не отображается Убедитесь, что поле ACF называется именно gallery и в него загружены изображения для данной записи.
Шорткод показывает текст Код не был добавлен в functions.php, либо в нём синтаксическая ошибка. Проверьте через плагин Health Check или посмотрите логи PHP.
Лайтбокс не открывается Откройте консоль браузера (F12) и проверьте ошибки JavaScript. Возможен конфликт с другим плагином, который переопределяет window.openCustomLightbox.
Слайдер не инициализируется Возможно, Swiper уже подключён другим плагином и возникает конфликт версий. В этом случае уберите строку подключения скрипта Swiper из сниппета и убедитесь, что нужная версия подключается глобально.
Итог
Мы разобрали сниппет, который без сторонних плагинов галерей добавляет полноценный слайдер с лайтбоксом на WordPress. Решение легковесное — используются только Swiper.js с CDN и нативный JavaScript без jQuery. Ограничение по типу записи гарантирует, что лишние скрипты не загружаются на других страницах сайта.
Полный код сниппета можно скопировать из начала этой статьи и адаптировать под свой проект, изменив название типа записи и поля ACF.
Весь код сниппета целиком
add_shortcode('custom_acf_gallery', 'display_acf_gallery_slider_lightbox');
function display_acf_gallery_slider_lightbox($atts) {
// Выполняем только на страницах с типом записи 'development'
if (!is_singular('development')) {
return '';
}
$images = get_field('gallery');
if( $images ):
$slider_id = 'swiper-' . uniqid();
$image_urls = [];
// 1. СТИЛИ (Swiper + Lightbox)
$output = '
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<style>
/* --- Стили Слайдера --- */
#' . $slider_id . ' { width: 100%; padding-bottom: 40px; }
#' . $slider_id . ' .swiper-slide { border-radius: 8px; overflow: hidden; cursor: zoom-in; }
#' . $slider_id . ' .swiper-slide img { width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block; }
#' . $slider_id . ' .swiper-button-next,
#' . $slider_id . ' .swiper-button-prev { color: #ffffff; text-shadow: 0 2px 6px rgba(0,0,0,0.4); transition: transform 0.3s ease, opacity 0.3s; }
#' . $slider_id . ' .swiper-button-next:hover { transform: translateX(3px); }
#' . $slider_id . ' .swiper-button-prev:hover { transform: translateX(-3px); }
#' . $slider_id . ' .swiper-button-next:after,
#' . $slider_id . ' .swiper-button-prev:after { font-size: 24px !important; font-weight: 300; }
#' . $slider_id . ' .swiper-pagination-bullet { width: 8px; height: 8px; background-color: #bbbbbb; opacity: 0.6; transition: all 0.3s ease; }
#' . $slider_id . ' .swiper-pagination-bullet:hover { opacity: 1; }
#' . $slider_id . ' .swiper-pagination-bullet-active { width: 24px; border-radius: 4px; background-color: #333333; opacity: 1; }
/* --- Стили Лайтбокса --- */
#custom-vanilla-lightbox { display: none; position: fixed; z-index: 999999; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.9); align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease; }
#custom-vanilla-lightbox.active { display: flex; opacity: 1; }
#custom-vanilla-lightbox img { max-width: 90%; max-height: 90%; border-radius: 4px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); object-fit: contain; }
.cvl-close { position: absolute; top: 20px; right: 30px; color: #fff; font-size: 40px; font-weight: bold; cursor: pointer; user-select: none; transition: color 0.2s; z-index: 10; }
.cvl-close:hover { color: #bbb; }
.cvl-nav { position: absolute; top: 50%; transform: translateY(-50%); color: #fff; font-size: 50px; font-weight: bold; cursor: pointer; user-select: none; padding: 20px; transition: color 0.2s; z-index: 10; }
.cvl-nav:hover { color: #bbb; }
.cvl-prev { left: 20px; }
.cvl-next { right: 20px; }
</style>';
// 2. HTML СЛАЙДЕРА
$output .= '<div id="' . $slider_id . '" class="swiper">';
$output .= '<div class="swiper-wrapper">';
foreach( $images as $index => $image ):
$image_urls[] = esc_url($image['url']);
$output .= '<div class="swiper-slide" onclick="window.openCustomLightbox(' . $index . ', \'' . $slider_id . '\')">';
$output .= '<img src="' . esc_url($image['sizes']['large']) . '" alt="' . esc_attr($image['alt']) . '" />';
$output .= '</div>';
endforeach;
$output .= '</div>'; // swiper-wrapper
$output .= '<div class="swiper-pagination"></div>';
$output .= '<div class="swiper-button-prev"></div>';
$output .= '<div class="swiper-button-next"></div>';
$output .= '</div>'; // swiper
$json_urls = json_encode($image_urls);
// 3. СКРИПТЫ (Swiper + Логика Лайтбокса)
$output .= '
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// --- Инициализация Swiper ---
const swiper = new Swiper("#' . $slider_id . '", {
loop: true,
speed: 600,
grabCursor: true,
autoplay: { delay: 3000, disableOnInteraction: false, pauseOnMouseEnter: true },
pagination: { el: ".swiper-pagination", clickable: true },
navigation: { nextEl: ".swiper-button-next", prevEl: ".swiper-button-prev" },
breakpoints: {
320: { slidesPerView: 1, spaceBetween: 15 },
768: { slidesPerView: 2, spaceBetween: 20 },
1024: { slidesPerView: 3, spaceBetween: 30 }
}
});
// --- Инициализация данных для Лайтбокса ---
if (!document.getElementById("custom-vanilla-lightbox")) {
const lbHtml = `
<div id="custom-vanilla-lightbox">
<span class="cvl-close" onclick="window.closeCustomLightbox()">×</span>
<span class="cvl-nav cvl-prev" onclick="window.changeCustomLightbox(-1)">❮</span>
<img id="cvl-image" src="" alt="">
<span class="cvl-nav cvl-next" onclick="window.changeCustomLightbox(1)">❯</span>
</div>
`;
document.body.insertAdjacentHTML("beforeend", lbHtml);
}
if (typeof window.acfGalleries === "undefined") {
window.acfGalleries = {};
}
window.acfGalleries["' . $slider_id . '"] = ' . $json_urls . ';
if (typeof window.openCustomLightbox !== "function") {
window.currentLightboxIndex = 0;
window.currentLightboxGallery = "";
window.openCustomLightbox = function(index, galId) {
window.currentLightboxIndex = index;
window.currentLightboxGallery = galId;
window.updateLightboxImage();
const lb = document.getElementById("custom-vanilla-lightbox");
lb.style.display = "flex";
setTimeout(() => { lb.classList.add("active"); }, 10);
document.body.style.overflow = "hidden";
};
window.closeCustomLightbox = function() {
const lb = document.getElementById("custom-vanilla-lightbox");
lb.classList.remove("active");
setTimeout(() => { lb.style.display = "none"; }, 300);
document.body.style.overflow = "";
};
window.changeCustomLightbox = function(step) {
const images = window.acfGalleries[window.currentLightboxGallery];
window.currentLightboxIndex += step;
if (window.currentLightboxIndex >= images.length) window.currentLightboxIndex = 0;
else if (window.currentLightboxIndex < 0) window.currentLightboxIndex = images.length - 1;
window.updateLightboxImage();
};
window.updateLightboxImage = function() {
const images = window.acfGalleries[window.currentLightboxGallery];
document.getElementById("cvl-image").src = images[window.currentLightboxIndex];
};
const lbElem = document.getElementById("custom-vanilla-lightbox");
lbElem.addEventListener("click", function(e) {
if (e.target === this) window.closeCustomLightbox();
});
document.addEventListener("keydown", function(e) {
const lbIsActive = document.getElementById("custom-vanilla-lightbox").classList.contains("active");
if (!lbIsActive) return;
if (e.key === "Escape") window.closeCustomLightbox();
else if (e.key === "ArrowRight") window.changeCustomLightbox(1);
else if (e.key === "ArrowLeft") window.changeCustomLightbox(-1);
});
}
});
</script>';
return $output;
endif;
return '';
}
