Как разработать карусель с помощью одного CSS с анимацией, управляемой прокруткой
-
Структурируйте HTML с помощью семантических элементов
<figure>внутри прокручиваемого контейнера сrole="region". -
Примените
scroll-snap-type: x mandatoryк контейнеру иscroll-snap-align: centerк каждому элементу. -
Сгенерируйте кнопки переключения слайдов с помощью псевдоэлементов
::scroll-button(left)и::scroll-button(right). -
Создайте точечные индикаторы с помощью
::scroll-marker()на каждом элементе и стилизуйте их активное состояние с помощью:checked. -
Привяжите анимацию появления слайда к положению прокрутки с помощью
animation-timeline: view(inline)иanimation-range. -
Добавьте индикатор прогресса с помощью именованного
scroll-timeline, привязанного к псевдоэлементу::beforeконтейнера. -
Поместите все новые возможности в блоки
@supports, чтобы браузеры без их поддержки могли “откатиться” к прокручиваемому контейнеру. -
В целях обеспечения доступности используйте
@media (prefers-reduced-motion: reduce)для отключения анимации.
Конец эпохи каруселей, реализованных с помощью JavaScript
Исторически создание карусели в веб-разработке означало использование какой-либо JS-библиотеки. Slick, Swiper, Flickity, Embla Carousel и другие заполняли пробел платформы. Каждая из них увеличивала размер сборки, имела свои особенности обеспечения доступности и накладные расходы на обработку событий. Полный минифицированный пакет Swiper v11 весит приблизительно 140 КБ; даже модульные импорты составляют 40-60 КБ для ядра и навигации. Умножьте это на бесчисленные маркетинговые страницы, галереи товаров в электронной коммерции и макеты с большим количеством медиаконтента, которые зависят от каруселей, и расходы сильно возрастут: долгое время до интерактивности, блокировка основного потока во время инициализации и большой размер передаваемых данных на каждой странице, где используется слайдер.
Ситуация с доступностью еще хуже. Карусели, управляемые JS, нередко игнорируют навигацию с помощью клавиатуры, не отображают правильную семантику ARIA и непредсказуемым образом перехватывают фокус. Только самые добросовестные авторы библиотек добавили поддержку программ чтения с экрана. Остальные оставили это без внимания.
Спецификация CSS Overflow Level 5 представила псевдоэлементы ::scroll-button() и ::scroll-marker(). Спецификация Scroll-Driven Animations Level 1 обеспечила широкую, но пока не универсальную поддержку движков; поддержка Firefox остается частичной по состоянию на середину 2026 года. Вместе эти API позволяют создавать полностью функциональные и доступные карусели без использования JS.
В этой статье подробно описывается, как создать полноценную карусель изображений с кнопками навигации, точками-маркерами прокрутки, функцией привязки и анимационными эффектами, управляемыми прокруткой, — и все это на чистом CSS. Никаких библиотек. Никаких скриптов. Никакой сборки.
Предварительные требования и поддержка браузеров
Что нужно знать
Предполагается средний уровень владения CSS, хорошие знания пользовательских свойств, псевдоэлементов и компоновки Grid/Flexbox. Знание свойств scroll-snap полезно, но не обязательно, поскольку их основы рассматриваются в статье.
Поддержка браузеров
По состоянию на середину 2026 года API для каруселей с прокруткой пользуются широкой, но не полной поддержкой.
Браузеры на основе Chromium (Chrome 135+, Edge 135+) предоставляют полную поддержку ::scroll-button(), ::scroll-marker(), ::scroll-marker-group и animation-timeline: scroll()/view(). Safari 19+ также обеспечивает полную поддержку. Firefox предлагает частичную поддержку за флагом: scroll-snap и animation-timeline работают, но ::scroll-button() и ::scroll-marker() на момент написания статьи остаются за флагом layout.css.scroll-driven-animations.enabled (проверено в Firefox 130; название флага может отличаться в более ранних или поздних версиях). Без флага пользователи Firefox получают прокручиваемый контейнер без элементов управления навигацией.
Правильная стратегия — прогрессивное улучшение. В неподдерживающих браузерах карусель превращается в горизонтально прокручиваемый контейнер. Блоки @supports, показанные ниже, обрабатывают это без проблем. На страницах Can I Use для “CSS Scroll-Driven Animations” и “CSS scroll-snap” представлены актуальные матрицы совместимости.
Понимание ключевых свойств CSS API, управляемых прокруткой
scroll-snap-type и scroll-snap-align
Привязка к прокрутке — основа любой CSS-карусели. Применение scroll-snap-type к прокручиваемому контейнеру определяет ось привязки и строгость. Применение scroll-snap-align к дочерним элементам определяет, где каждый элемент должен привязываться внутри области просмотра контейнера.
<div class="carousel"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div></div>
.carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; gap: 1rem;}.item { flex: 0 0 80%; scroll-snap-align: center;}
Установка mandatory заставляет контейнер докручивать слайд до следующей точки привязки после завершения прокрутки. Выравнивание center означает, что каждый элемент располагается по центру видимой области. Эта комбинация обеспечивает дискретное, “послайдовое” движение, определяющее поведение карусели.
::scroll-button(left) и ::scroll-button(right) — нативная навигация
Этот псевдоэлемент генерирует интерактивные элементы управления навигацией прямо из прокручиваемого контейнера, без элементов <button> в разметке. Браузер создает их как доступные, фокусируемые элементы управления, которые прокручивают контейнер по одной “странице” в заданном направлении при активации.
Этот псевдоэлемент принимает четыре значения направления: left, right, up и down. Для горизонтальной карусели ::scroll-button(left) и ::scroll-button(right) производят кнопки “Назад/вперед”, соответственно. Для отображения псевдоэлемента требуется значение content. Это свойство принимает текст, изображения CSS или URI данных SVG.
Важная особенность поведения: браузер автоматически отключает кнопку прокрутки при достижении контейнером соответствующей границы прокрутки. При инициализации карусели ::scroll-button(left) находится в отключенном состоянии. В конце отключается ::scroll-button(right). Можно стилизовать это состояние, исключив логику ручной проверки границ прокрутки, которую обычно используют JS-карусели.
Для генерации псевдоэлементов контейнер прокрутки должен иметь overflow: auto или overflow: scroll.
::scroll-marker-group и ::scroll-marker() — нативные точечные индикаторы
Применение ::scroll-marker() к потомкам контейнера прокрутки приводит к генерации точечного индикатора (или любого стилизованного маркера) для каждого потомка. Скобки в ::scroll-marker() — это часть синтаксиса функции псевдоэлемента; в текущей спецификации функция не принимает аргументы. Браузер группирует эти маркеры в псевдоэлемент ::scroll-marker-group на самом контейнере.
Установка display: flex в ::scroll-marker-group приводит к выстраиванию маркеров в строку. Добавление justify-content: center центрирует строку с точками. Положением этой строки можно управлять с помощью стандартных свойств позиционирования или order.
Браузер управляет активным состоянием автоматически. Маркер, соответствующий текущему привязанному элементу, получает псевдокласс :checked. Клик по любому маркеру прокручивает контейнер к соответствующему элементу. Добавление scroll-behavior: smooth к контейнеру приводит к включению анимации прокрутки. Это заменяет обработчики кликов, наблюдатели пересечений и управление состоянием, которые используют библиотеки JS-каруселей для синхронизации точечных индикаторов с позицией прокрутки.
animation-timeline: scroll() и animation-timeline: view() — анимации, управляемые прокруткой
Анимации, управляемые прокруткой, отличаются от других анимаций CSS тем, что они привязываются не ко времени, а к прогрессу прокрутки. Два типа временной шкалы служат разным целям.
В случае animation-timeline: scroll(), анимация отслеживает общий прогресс прокрутки контейнера. Когда контейнер прокручен на 50%, анимация завершена на 50%. Это хорошо подходит для индикаторов прогресса и эффектов на уровне контейнера.
В случае animation-timeline: view(), анимация отслеживает видимость элемента в области просмотра контейнера прокрутки. Анимация выполняется при входе, пересечении и выходе элемента из видимой области. Это хорошо подходит для эффектов на уровне элемента, таких как затухание/появление, масштабирование и параллакс.
animation-range используется для ограничения того, какая часть временной шкалы управляет анимацией. Например, animation-range: entry 0% entry 100% ограничивает анимацию стадией входа, от момента, когда первая часть элемента становится видимой, до момента, когда он становится полностью видимым. Первый видимый элемент при загрузке страницы может сразу находиться в состоянии to, поскольку он уже прошел стадию входа; используйте animation-fill-mode или начальные правила прозрачности/масштабирования для обработки этого крайнего случая. В режиме письма “слева-направо” ключевое слово inline в view(inline) определяет горизонтальную (строчную) ось для горизонтальных контейнеров прокрутки.
Шаг 1 — построение структуры HTML
Семантическая разметка для доступной карусели
Нам не нужны элементы <button> для навигации или обертка <nav> для точек. Эти элементы управления генерируются псевдоэлементами CSS. Разметка содержит только элементы <figure> внутри контейнера.
<div class="carousel" role="region" aria-label="Featured images"> <figure class="carousel-item"> <img src="https://picsum.photos/seed/1/800/300" alt="Mountain landscape at sunrise" /> <figcaption>Mountain Sunrise</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/2/800/300" alt="Ocean waves crashing on rocks" /> <figcaption>Coastal Waves</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/3/800/300" alt="Dense forest canopy from below" /> <figcaption>Forest Canopy</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/4/800/300" alt="Desert dunes under a clear sky" /> <figcaption>Desert Dunes</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/5/800/300" alt="Snow-covered mountain peak" /> <figcaption>Alpine Peak</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/6/800/300" alt="Wildflower meadow in spring" /> <figcaption>Spring Meadow</figcaption> </figure></div>
Атрибуты role="region" и aria-label определяют карусель как ориентир для вспомогательных технологий. Каждый элемент <figure> со своими <img> и <figcaption> предоставляет самодостаточный, семантически значимый контент.
Шаг 2 — реализация привязки прокрутки и макета
Плиточный трек карусели
Контейнер карусели использует grid-auto-flow: column для горизонтального расположения элементов. Каждый элемент масштабируется для заполнения всей ширины контейнера, оставляя видимыми части соседних элементов для обозначения возможности прокрутки.
Привязка к прокрутке
Установка scroll-snap-type: x mandatory к контейнеру обеспечивает четкую привязку слайдов друг к другу. scroll-snap-align: center центрирует каждый элемент после завершения жеста прокрутки.
.carousel { display: grid; grid-auto-flow: column; grid-auto-columns: 85%; gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; scrollbar-width: auto; padding: 1rem; position: relative; box-sizing: border-box; scroll-timeline: --carousel inline;}.carousel::-webkit-scrollbar { display: none;}.carousel-item { scroll-snap-align: center; border-radius: 0.75rem; overflow: clip; overflow-clip-margin: 0px; margin: 0;}.carousel-item img { width: 100%; height: 300px; object-fit: cover; display: block;}.carousel-item figcaption { padding: 0.75rem 1rem; font-size: 1rem; background: #111; color: #fff;}
scrollbar-width: auto сохраняет встроенную полосу прокрутки в качестве средства навигации в браузерах, которые не поддерживают ::scroll-button(). Когда эти псевдоэлементы доступны, полоса прокрутки скрывается внутри блока @supports (см. раздел, посвященный прогрессивному улучшению). box-sizing: border-box гарантирует, что grid-auto-columns: 85% и padding: 1rem разрешаются предсказуемо, без переполнения элементов или неправильного масштабирования. Использование overflow: clip на .carousel-item (вместо overflow: hidden) предотвращает визуальное переполнение из-за скругления границ без обрезки дочерних элементов figcaption при движении по оси X (translateX), используемых для параллаксной анимации. grid-auto-columns: 85% гарантирует, что каждый элемент занимает 85% ширины контейнера, оставляя края соседних элементов видимыми для обозначения возможности прокрутки. scroll-behavior: smooth гарантирует, что программная и запускаемая маркером прокрутка анимируется, а не происходит мгновенно. scroll-timeline: --carousel inline создает именованную временную шкалу прокрутки в контейнере для использования анимацией индикатора прогресса.
Шаг 3 — добавление навигации с помощью ::scroll-button()
Генерация кнопок “Назад/вперед”
Эти кнопки генерируются путем применения ::scroll-button(left) и ::scroll-button(right) к контейнеру .carousel. Для отображения каждого псевдоэлемента требуется свойство content.
Стилизация кнопок прокрутки
@supports selector(::scroll-button(right)) { .carousel::scroll-button(left), .carousel::scroll-button(right) { position: absolute; top: 50%; transform: translateY(-50%); z-index: 10; width: 3rem; height: 3rem; border-radius: 50%; background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 1.25rem; display: grid; place-content: center; cursor: pointer; border: 2px solid rgba(255, 255, 255, 0.3); transition: background 0.2s ease, opacity 0.2s ease; } .carousel::scroll-button(left) { content: "◀"; left: 0.5rem; } .carousel::scroll-button(right) { content: "▶"; right: 0.5rem; } .carousel::scroll-button(left):hover, .carousel::scroll-button(right):hover { background: rgba(0, 0, 0, 0.85); } .carousel::scroll-button(left):disabled, .carousel::scroll-button(right):disabled { opacity: 0.3; cursor: default; }}
Согласно черновику спецификации CSS Pseudo-Elements Level 5 псевдокласс :disabled автоматически применяется при достижении каруселью границ прокрутки. Нет необходимости в обнаружении этих границ с помощью JS. Переход (transition) прозрачности и фона предоставляет визуальный отклик на наведение курсора и изменение состояния. Защитник @supports selector(::scroll-button(right)) предотвращает ошибки парсинга в неподдерживающих браузерах.
Шаг 4 — добавление точечных индикаторов с помощью ::scroll-marker()
Генерация маркеров на основе элементов карусели
Каждый .carousel-item получает псевдоэлемент ::scroll-marker(). Свойство content создает видимую точку.
Позиционирование группы маркеров
Псевдоэлемент контейнера ::scroll-marker-group содержит все сгенерированные маркеры и стилизован как выровненная по центру флекс-строка.
Стилизация активных/неактивных состояний
@supports selector(::scroll-marker()) { .carousel::scroll-marker-group { display: flex; justify-content: center; gap: 0.5rem; padding-top: 1rem; position: relative; bottom: 0; } .carousel-item::scroll-marker() { content: ""; width: 0.75rem; height: 0.75rem; border-radius: 50%; background: rgba(0, 0, 0, 0.25); border: 2px solid transparent; transition: background 0.3s ease, transform 0.3s ease; cursor: pointer; } .carousel-item::scroll-marker():checked { background: #0070f3; transform: scale(1.3); border-color: #0070f3; }}
Псевдокласс :checked на ::scroll-marker() отражает текущий привязанный элемент. Обратите внимание на скобки в ::scroll-marker():checked — только такой составной селектор считается валидным. Когда пользователь кликает по неактивной точке браузер выполняет прокрутку к соответствующему элементу карусели (с анимацией при установке scroll-behavior: smooth на контейнере). transition свойств background и transform длительностью 300 мс создает эффект плавного перехода между состояниями точки.
Шаг 5 — эффекты анимации, управляемой прокруткой
Появление и масштабирование при прокрутке с помощью view()
Привязка анимации к видимости каждого элемента внутри контейнера прокрутки создает эффекты появления, которые реагируют на взаимодействие с пользователем, а не на произвольное время. Первый элемент полностью виден при загрузке страницы (стадия его входа завершена), поэтому он исключается из анимации, управляемой прокруткой, и отображается с полной прозрачностью и масштабом немедленно.
@keyframes fade-scale-in { from { opacity: 0.3; transform: scale(0.9); } to { opacity: 1; transform: scale(1); }}/* Первый элемент виден при загрузке страницы; рендерим его в финальном состоянии */.carousel-item:first-child { animation: none; opacity: 1; transform: scale(1);}
Временная шкала view(inline) отслеживает горизонтальную видимость каждого элемента. animation-range: entry 0% entry 100% ограничивает анимацию стадией входа. Когда элемент прокручивается в область просмотра с любой стороны, он переходит от 30% прозрачности и 90% размера к полной видимости и размеру. Привязка анимации, управляемой прокруткой, к элементам (кроме первого) выполняется в блоке @supports, чтобы браузеры без поддержки не получили объявление animation-timeline, которое они не смогут распарсить.
Эффект параллакса подписей
Применение отдельной временной шкалы к элементам <figcaption> создает параллаксное смещение, при котором подписи смещаются с другой скоростью, чем соответствующие им изображения. .carousel-item использует overflow: clip (вместо overflow: hidden), поэтому подпись не обрезается при движении по оси X (translateX).
@keyframes caption-parallax { from { transform: translateX(-30px); opacity: 0; } to { transform: translateX(0); opacity: 1; }}
Небольшая задержка animation-range, начинающаяся с entry 20%, означает, что анимация подписи начинается после того, как изображение начало плавно появляться, что создает эффект постепенного раскрытия. animation-timeline и animation-range для figcaption применяются внутри защитника @supports (см. раздел, посвященный прогрессивному улучшению).
Привязка индикатора прогресса к позиции прокрутки
Псевдоэлемент ::before на контейнере, анимированный с помощью именованной временной шкалы прокрутки --carousel, создает индикатор выполнения, отслеживающий общее положение прокрутки.
.carousel::before { content: ""; position: absolute; top: 0; left: 0; height: 3px; width: calc(100% - 2rem); background: #0070f3; transform-origin: left; z-index: 20; pointer-events: none;}@keyframes progress-bar { from { transform: scaleX(0); } to { transform: scaleX(1); }}
Временная шкала --carousel ссылается на индикатор прогресса прокрутки по оси линии контейнера (определенной с помощью scroll-timeline: --carousel inline на .carousel). По мере прокрутки карусели, индикатор увеличивается от 0 до полной ширины. width: calc(100% - 2rem) учитывает левый и правый отступы контейнера, чтобы индикатор соответствовал видимой ширине. pointer-events: none предотвращает перехват индикатором кликов по элементам карусели. transform: scaleX() дружелюбно к компоновщику; анимация выполняется вне основного потока без затрат на компоновку или отрисовку. animation-timeline для ::before применяется внутри защитника @supports (см. раздел, посвященный прогрессивному улучшению).
Готовая карусель — полный код
Полный HTML
<div class="carousel" role="region" aria-label="Featured images"> <figure class="carousel-item"> <img src="https://picsum.photos/seed/1/800/300" alt="Mountain landscape at sunrise" /> <figcaption>Mountain Sunrise</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/2/800/300" alt="Ocean waves crashing on rocks" /> <figcaption>Coastal Waves</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/3/800/300" alt="Dense forest canopy from below" /> <figcaption>Forest Canopy</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/4/800/300" alt="Desert dunes under a clear sky" /> <figcaption>Desert Dunes</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/5/800/300" alt="Snow-covered mountain peak" /> <figcaption>Alpine Peak</figcaption> </figure> <figure class="carousel-item"> <img src="https://picsum.photos/seed/6/800/300" alt="Wildflower meadow in spring" /> <figcaption>Spring Meadow</figcaption> </figure></div>
Полный CSS
/* ============================================== Базовый макет карусели и привязка прокрутки ============================================== */.carousel { display: grid; grid-auto-flow: column; grid-auto-columns: 85%; gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; scrollbar-width: auto; padding: 1rem; position: relative; box-sizing: border-box; scroll-timeline: --carousel inline;}.carousel::-webkit-scrollbar { display: none;}/* Индикатор прогресса — базовое позиционирование (анимация применяется в @supports) */.carousel::before { content: ""; position: absolute; top: 0; left: 0; height: 3px; width: calc(100% - 2rem); background: #0070f3; transform-origin: left; transform: scaleX(0); z-index: 20; pointer-events: none;}.carousel-item { scroll-snap-align: center; border-radius: 0.75rem; overflow: clip; overflow-clip-margin: 0px; margin: 0;}/* Первый элемент находится в области просмотра при загрузке страницы; рендерим его в финальном состоянии */.carousel-item:first-child { opacity: 1; transform: scale(1);}.carousel-item img { width: 100%; height: 300px; object-fit: cover; display: block;}.carousel-item figcaption { padding: 0.75rem 1rem; font-size: 1rem; background: #111; color: #fff;}/* ============================================== Анимация ============================================== */@keyframes fade-scale-in { from { opacity: 0.3; transform: scale(0.9); } to { opacity: 1; transform: scale(1); }}@keyframes caption-parallax { from { transform: translateX(-30px); opacity: 0; } to { transform: translateX(0); opacity: 1; }}@keyframes progress-bar { from { transform: scaleX(0); } to { transform: scaleX(1); }}/* ============================================== Кнопки прокрутки с защитой от неподдерживающих браузеров ============================================== */@supports selector(::scroll-button(right)) { .carousel { scrollbar-width: none; } .carousel::-webkit-scrollbar { display: none; } .carousel::scroll-button(left), .carousel::scroll-button(right) { position: absolute; top: 50%; transform: translateY(-50%); z-index: 10; width: 3rem; height: 3rem; border-radius: 50%; background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 1.25rem; display: grid; place-content: center; cursor: pointer; border: 2px solid rgba(255, 255, 255, 0.3); transition: background 0.2s ease, opacity 0.2s ease; } .carousel::scroll-button(left) { content: "◀"; left: 0.5rem; } .carousel::scroll-button(right) { content: "▶"; right: 0.5rem; } .carousel::scroll-button(left):hover, .carousel::scroll-button(right):hover { background: rgba(0, 0, 0, 0.85); } .carousel::scroll-button(left):disabled, .carousel::scroll-button(right):disabled { opacity: 0.3; cursor: default; }}/* ============================================== Маркеры прокрутки с защитой ============================================== */@supports selector(::scroll-marker()) { .carousel::scroll-marker-group { display: flex; justify-content: center; gap: 0.5rem; padding-top: 1rem; } .carousel-item::scroll-marker() { content: ""; width: 0.75rem; height: 0.75rem; border-radius: 50%; background: rgba(0, 0, 0, 0.25); border: 2px solid transparent; transition: background 0.3s ease, transform 0.3s ease; cursor: pointer; } .carousel-item::scroll-marker():checked { background: #0070f3; transform: scale(1.3); border-color: #0070f3; }}/* ============================================== Анимация, управляемая прокруткой, с защитой ============================================== */@supports (animation-timeline: view()) { .carousel-item:not(:first-child) { animation: fade-scale-in 1s linear both; animation-timeline: view(inline); animation-range: entry 0% entry 100%; } .carousel-item figcaption { animation: caption-parallax 1s linear both; animation-timeline: view(inline); animation-range: entry 20% entry 100%; } .carousel::before { animation: progress-bar 1s linear both; animation-timeline: --carousel; }}/* ============================================== Отключение анимации ============================================== */@media (prefers-reduced-motion: reduce) { .carousel-item, .carousel-item figcaption, .carousel::before { animation: none; animation-timeline: none; animation-range: normal; }}
Демо
Прогрессивное улучшение и резерв
Определение поддержки с помощью @supports
Оборачивание стилей новых псевдоэлементов в блоки @supports позволяет браузерам, которые их не поддерживают, просто рендерить прокручиваемый контейнер. В полном CSS выше ::scroll-button(), ::scroll-marker() и animation-timeline помещены в блоки @supports. Структура такая:
/* Кнопки прокрутки: рендерятся только в поддерживающих браузерах */@supports selector(::scroll-button(right)) { /* Скрываем полосу прокрутки, стилизуем ::scroll-button(left) и ::scroll-button(right) */}/* Маркеры прокрутки: рендерятся только в поддерживающих браузерах */@supports selector(::scroll-marker()) { /* Стилизуем ::scroll-marker-group и ::scroll-marker() с состоянием :checked */}/* Анимация, управляемая прокруткой: привязываем временные шкалы только в случае поддержки */@supports (animation-timeline: view()) { /* Привязываем анимации fade-scale-in, caption-parallax и progress-bar */}
Без этих возможностей пользователи получат горизонтально прокручиваемый контейнер с видимой полосой прокрутки. Главный пользовательский опыт остается неизменным.
Когда по-прежнему нужен JS
CSS-карусели не охватывают все варианты использования. Автовоспроизведение с паузой при наведении курсора требует таймеров, которые невозможно реализовать с помощью CSS. Бесконечный цикл требует манипуляций с DOM или клонированных узлов. Нам по-прежнему нужны скрипты для динамической загрузки контента (ленивая загрузка слайдов, получаемых из API). Сложные требования к доступности, такие как аннотации в реальном времени при смене активного слайда или пользовательское управление фокусом, выходящее за рамки того, что предоставляет браузер, по-прежнему требуют тонкого слоя JS. Правильный подход заключается в том, чтобы рассматривать эти CSS API как основу и добавлять скрипты только для тех действий, которые платформа еще не обрабатывает нативно.
Чеклист реализации
-
семантический HTML с
role="region"иaria-label -
scroll-snap-type: x mandatoryна контейнере -
scroll-snap-alignна каждом элементе -
scroll-behavior: smoothна контейнере для анимированной прокрутки -
::scroll-button(left)и::scroll-button(right)со стилизованным контентом внутри@supports -
::scroll-marker()на элементах с активным состоянием через:checkedвнутри@supports -
позиционирование и стилизация
::scroll-marker-groupвнутри@supports -
резервы
@supportsдля неподдерживающих браузеров (полоса прокрутки видна, когда недоступны кнопки) -
тестирование навигации с помощью клавиатуры (Tab, стрелки)
-
отключение анимации через
@media (prefers-reduced-motion: reduce) -
тестирование в Chrome, Safari и Firefox
Значение для фронтенд-разработки
То, о чем мы говорили в статье, является частью более широкой траектории платформы. Вложенность CSS, :has(), запросы к контейнеру, а теперь и анимация, управляемая прокруткой, заменяют логику, реализация которой раньше требовала JS или сборщика. Улучшение производительности очевидно: 0 КБ JS в сборке для функционала карусели (в сравнении с 40-140 КБ библиотеки) и анимации, управляемые прокруткой, которые выполняются в потоке компоновщика с ускорением графического процессора, что позволяет избежать компоновки и отрисовки в основном потоке для свойств, доступных компоновщику, таких как transform и opacity.
Разработчикам, работающим со Swiper, Flickity или Embla, следует оценить, действительно ли их сценарии использования выходят за рамки того, что предоставляют современные CSS API. CSS сейчас обрабатывает навигацию с привязкой, точечные индикаторы, анимацию появления при прокрутке и отслеживание прогресса “из коробки”. Если вашей карусели не требуется автовоспроизведение, бесконечный цикл или динамическая загрузка слайдов, откажитесь от библиотеки. Начните внедрять эти API постепенно, оберните новые функции в @supports и откажитесь от зависимостей, которые эти спецификации были призваны заменить.
Для более подробного ознакомления с рассматриваемыми API можно обратиться к спецификации W3C CSS Overflow Level 5 и документации MDN по анимации, управляемой прокруткой, которые являются авторитетными источниками информации.
ссылка на оригинал статьи https://habr.com/ru/articles/1034960/