diff --git a/blocks/carousel/carousel.css b/blocks/carousel/carousel.css index d06af1a..65baf92 100755 --- a/blocks/carousel/carousel.css +++ b/blocks/carousel/carousel.css @@ -1,170 +1,128 @@ -.carousel .carousel-slides-container { +.carousel-wrapper { position: relative; } -.carousel .carousel-slides, -.carousel .carousel-slide-indicators { - list-style: none; - margin: 0; - padding: 0; -} - -.carousel .carousel-slides { - display: flex; - scroll-behavior: smooth; - scroll-snap-type: x mandatory; - overflow: scroll clip; +.carousel.swiper-carousel { + width: 100%; + height: 100%; + margin: 32px 0; + overflow: hidden; } -.carousel .carousel-slides::-webkit-scrollbar { - display: none; +.carousel { + padding-bottom: 28px; + position: relative; } -.carousel .carousel-slide { - flex: 0 0 100%; - scroll-snap-align: start; +.carousel .swiper-slide { + text-align: left; + font-size: 18px; display: flex; - flex-direction: column; + flex-direction: row; + gap: 24px; align-items: flex-start; - justify-content: center; - position: relative; +} + +.carousel .swiper-slide img { + display: block; width: 100%; - min-height: min(40rem, calc(100svh - var(--nav-height))); + height: 100%; + object-fit: cover; } -.carousel .carousel-slide:has(.carousel-slide-content[data-align="center"]) { - align-items: center; +.carousel .swiper-slide:nth-child(2n + 1) > div:nth-child(1) { + width: 55.8333%; } -.carousel .carousel-slide:has(.carousel-slide-content[data-align="right"]) { - align-items: flex-end; +.carousel .swiper-slide:nth-child(2n + 1) > div:nth-child(2) { + width: calc(100% - 55.8333%); + text-align: left; } -.carousel .carousel-slide .carousel-slide-image picture { - position: absolute; - inset: 0; +.carousel .swiper-slide:nth-child(2n) picture { + width: 232px; + display: block; + margin-left: 50px; } -.carousel .carousel-slide .carousel-slide-image picture > img { - height: 100%; - width: 100%; - object-fit: cover; +.carousel .swiper-slide h2 { + margin-top: 24px; + font-size: 28px; + line-height: 36px; + text-transform: uppercase; } -.carousel .carousel-slide .carousel-slide-content { - z-index: 1; - padding: 1rem; - margin: 1.5rem 3rem; - color: white; - background-color: rgba(0 0 0 / 50%); - position: relative; - width: var(--slide-content-width, auto); +.carousel .swiper-slide p { + font-size: 16px; + line-height: 24px; } -.carousel .carousel-slide-indicators { +.carousel .button-container .button { + margin: 0; display: flex; - justify-content: center; - gap: 0.5rem; + gap: 8px; + align-items: center; } -.carousel .carousel-slide-indicator button { - width: 1rem; - height: 1rem; +.carousel .button-container .button .icon { padding: 0; - border-radius: 1rem; - background-color: rgba(0 0 0 / 25%); } -.carousel .carousel-slide-indicator button:disabled, -.carousel .carousel-slide-indicator button:hover, -.carousel .carousel-slide-indicator button:focus-visible { - background-color: rgba(0 0 0 / 80%); +.carousel-wrapper .swiper-button-next, +.carousel-wrapper .swiper-button-prev { + right: 0; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + background-color: transparent; + color: rgb(0 0 0); + border: 1px solid rgb(0 0 0 / 0%); + width: 2.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + padding: 0; + height: 100%; + top: 0; + margin: 0; } -.carousel .carousel-slide-indicator span, -.carousel .carousel-navigation-buttons span { - border: 0; - clip: rect(0 0 0 0); - clip-path: inset(50%); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - white-space: nowrap; +.carousel-wrapper .swiper-button-next { + right: -40px; } -.carousel .carousel-navigation-buttons { - position: absolute; - top: 50%; - transform: translateY(-50%); - left: 0.5rem; - right: 0.5rem; - display: flex; - align-items: center; - justify-content: space-between; - z-index: 1; +.carousel-wrapper .swiper-button-prev { + left: -40px; } -/* stylelint-disable-next-line no-descending-specificity */ -.carousel .carousel-navigation-buttons button { - border-radius: 8px; - margin: 0; - padding: 0; - width: 2rem; - height: 2rem; - position: relative; - background-color: rgba(0 0 0 / 25%); +.carousel-wrapper .swiper-button-next:hover, +.carousel-wrapper .swiper-button-prev:hover { + background-color: rgb(0 0 0 / 8%); } -.carousel .carousel-navigation-buttons button:hover, -.carousel .carousel-navigation-buttons button:focus-visible { - background-color: rgba(0 0 0 / 80%); +.carousel-wrapper .carousel .swiper-pagination { + top: unset; + left: 50%; + bottom: -28px; + transform: translateX(-50%); + position: relative; + width: 160px; + height: 0.5rem; + display: flex; + -webkit-box-align: center; + align-items: center; + background-color: rgb(0 0 0 / 12%); + border-radius: 6px; } -.carousel .carousel-navigation-buttons button::after { - display: block; - content: ""; - border: 3px white solid; - border-bottom: 0; - border-left: 0; - height: 0.75rem; - width: 0.75rem; - position: absolute; - top: 50%; - left: calc(50% + 3px); - transform: translate(-50%, -50%) rotate(-135deg); -} - -.carousel .carousel-navigation-buttons button.slide-next::after { - transform: translate(-50%, -50%) rotate(45deg); - left: calc(50% - 3px); -} - -@media (width >= 600px) { - .carousel .carousel-navigation-buttons { - left: 1rem; - right: 1rem; - } - - .carousel .carousel-navigation-buttons button { - width: 3rem; - height: 3rem; - } - - .carousel .carousel-navigation-buttons button::after { - width: 1rem; - height: 1rem; - } - - .carousel .carousel-slide .carousel-slide-content { - --slide-content-width: 50%; - - margin: 2.5rem 5rem; - } - - .carousel .carousel-slide .carousel-slide-content[data-align="justify"] { - --slide-content-width: auto; - } +.carousel-wrapper .carousel .swiper-pagination-progressbar-fill { + transform: translate3d(0, 0, 0) scaleX(0.2) scaleY(1); + transition-duration: 300ms; + min-width: 6px; + height: 6px; + background: rgb(0 0 0); + border: 0; + border-radius: 6px; + cursor: pointer; + appearance: none; } diff --git a/blocks/carousel/carousel.js b/blocks/carousel/carousel.js index 2b1ae16..2d15fcb 100755 --- a/blocks/carousel/carousel.js +++ b/blocks/carousel/carousel.js @@ -1,150 +1,46 @@ -import { fetchPlaceholders } from '../../scripts/aem.js'; +import { loadCSS, loadScript } from '../../scripts/aem.js'; + +const buildSlider = () => { + // eslint-disable-next-line no-undef, no-new + new Swiper('.swiper-carousel', { + slidesPerView: 2, + spaceBetween: 30, + pagination: { + el: '.swiper-pagination', + type: 'progressbar', + }, + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev', + }, + }); +}; -function updateActiveSlide(slide) { - const block = slide.closest('.carousel'); - const slideIndex = parseInt(slide.dataset.slideIndex, 10); - block.dataset.activeSlide = slideIndex; - - const slides = block.querySelectorAll('.carousel-slide'); - - slides.forEach((aSlide, idx) => { - aSlide.setAttribute('aria-hidden', idx !== slideIndex); - aSlide.querySelectorAll('a').forEach((link) => { - if (idx !== slideIndex) { - link.setAttribute('tabindex', '-1'); - } else { - link.removeAttribute('tabindex'); - } - }); - }); - - const indicators = block.querySelectorAll('.carousel-slide-indicator'); - indicators.forEach((indicator, idx) => { - if (idx !== slideIndex) { - indicator.querySelector('button').removeAttribute('disabled'); - } else { - indicator.querySelector('button').setAttribute('disabled', 'true'); - } - }); -} - -function showSlide(block, slideIndex = 0) { - const slides = block.querySelectorAll('.carousel-slide'); - let realSlideIndex = slideIndex < 0 ? slides.length - 1 : slideIndex; - if (slideIndex >= slides.length) realSlideIndex = 0; - const activeSlide = slides[realSlideIndex]; - - activeSlide.querySelectorAll('a').forEach((link) => link.removeAttribute('tabindex')); - block.querySelector('.carousel-slides').scrollTo({ - top: 0, - left: activeSlide.offsetLeft, - behavior: 'smooth', - }); -} - -function bindEvents(block) { - const slideIndicators = block.querySelector('.carousel-slide-indicators'); - if (!slideIndicators) return; - - slideIndicators.querySelectorAll('button').forEach((button) => { - button.addEventListener('click', (e) => { - const slideIndicator = e.currentTarget.parentElement; - showSlide(block, parseInt(slideIndicator.dataset.targetSlide, 10)); - }); - }); - - block.querySelector('.slide-prev').addEventListener('click', () => { - showSlide(block, parseInt(block.dataset.activeSlide, 10) - 1); - }); - block.querySelector('.slide-next').addEventListener('click', () => { - showSlide(block, parseInt(block.dataset.activeSlide, 10) + 1); - }); - - const slideObserver = new IntersectionObserver((entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) updateActiveSlide(entry.target); - }); - }, { threshold: 0.5 }); - block.querySelectorAll('.carousel-slide').forEach((slide) => { - slideObserver.observe(slide); - }); -} - -function createSlide(row, slideIndex, carouselId) { - const slide = document.createElement('li'); - slide.dataset.slideIndex = slideIndex; - slide.setAttribute('id', `carousel-${carouselId}-slide-${slideIndex}`); - slide.classList.add('carousel-slide'); - - row.querySelectorAll(':scope > div').forEach((column, colIdx) => { - column.classList.add(`carousel-slide-${colIdx === 0 ? 'image' : 'content'}`); - slide.append(column); - }); - - const labeledBy = slide.querySelector('h1, h2, h3, h4, h5, h6'); - if (labeledBy) { - slide.setAttribute('aria-labelledby', labeledBy.getAttribute('id')); - } - - return slide; -} - -let carouselId = 0; export default async function decorate(block) { - carouselId += 1; - block.setAttribute('id', `carousel-${carouselId}`); - const rows = block.querySelectorAll(':scope > div'); - const isSingleSlide = rows.length < 2; - - const placeholders = await fetchPlaceholders(); - - block.setAttribute('role', 'region'); - block.setAttribute('aria-roledescription', placeholders.carousel || 'Carousel'); - - const container = document.createElement('div'); - container.classList.add('carousel-slides-container'); + block.classList.add('swiper-carousel'); - const slidesWrapper = document.createElement('ul'); - slidesWrapper.classList.add('carousel-slides'); - block.prepend(slidesWrapper); + const wrapperEl = document.createElement('div'); + wrapperEl.classList.add('swiper-wrapper'); + wrapperEl.append(...block.querySelectorAll(':scope > div')); + block.append(wrapperEl); - let slideIndicators; - if (!isSingleSlide) { - const slideIndicatorsNav = document.createElement('nav'); - slideIndicatorsNav.setAttribute('aria-label', placeholders.carouselSlideControls || 'Carousel Slide Controls'); - slideIndicators = document.createElement('ol'); - slideIndicators.classList.add('carousel-slide-indicators'); - slideIndicatorsNav.append(slideIndicators); - block.append(slideIndicatorsNav); + const slides = [...wrapperEl.querySelectorAll(':scope > div')]; - const slideNavButtons = document.createElement('div'); - slideNavButtons.classList.add('carousel-navigation-buttons'); - slideNavButtons.innerHTML = ` - - - `; - - container.append(slideNavButtons); - } - - rows.forEach((row, idx) => { - const slide = createSlide(row, idx, carouselId); - slidesWrapper.append(slide); - - if (slideIndicators) { - const indicator = document.createElement('li'); - indicator.classList.add('carousel-slide-indicator'); - indicator.dataset.targetSlide = idx; - indicator.innerHTML = ``; - slideIndicators.append(indicator); - } - row.remove(); + slides.forEach((slide) => { + slide.classList.add('swiper-slide'); }); - container.append(slidesWrapper); - block.prepend(container); + const nextButton = document.createElement('button'); + nextButton.classList.add('swiper-button-next'); + const prevButton = document.createElement('button'); + prevButton.classList.add('swiper-button-prev'); + const pagination = document.createElement('div'); + pagination.classList.add('swiper-pagination'); + + block.parentElement.prepend(prevButton); + block.parentElement.append(nextButton); + block.append(pagination); - if (!isSingleSlide) { - bindEvents(block); - } + loadScript('https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js').then(() => buildSlider(block)); + loadCSS('https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css'); } diff --git a/blocks/header/header.css b/blocks/header/header.css index 4fd1c6e..438a96f 100644 --- a/blocks/header/header.css +++ b/blocks/header/header.css @@ -4,6 +4,7 @@ header .nav-wrapper { width: 100%; z-index: 2; position: fixed; + box-shadow: rgb(0 0 0 / 20%) 0 3px 12px 0; } header .nav-wrapper::before {