This repository has been archived on 2025-10-29. You can view files and clone it, but cannot push or open issues or pull requests.
Files
M6C9.de/src/components/Carousel.pug

84 lines
4.2 KiB
Plaintext

mixin Carousel(items, options)
// Set options with fallbacks for defaults
- const id = options.id || "carousel";
- const color = "black";
- const colorInverted = "white";
- const category = options.category || "portfolio";
- const slideClasses = options.slideClasses || "sm:w-1/2 lg:w-1/3 xl:w-1/4";
// Pass through options even if not used
- const autoScroll = options.autoScroll ?? 4_800;
.carousel-container.relative.w-full.overflow-hidden-y.px-4(class="sm:px-12", id=id)
//- Navigation buttons
button.absolute.z-20.transform(
class=`-translate-y-1/2 top-1/2 -left-6 sm:left-2 bg-white/40 hover:bg-white/80 text-nls-black rounded-full p-1 sm:p-3 shadow-lg transition-all focus:outline-none focus:ring-2 focus:ring-nls-${color}`,
aria-label="Previous slide",
onclick=`carouselPrev('${id}'); umami.track('carousel navigation', { direction: 'prev', category: '${category}', visitDuration: getVisitDuration() })`
)
svg.w-8.h-8.text-gray-800(class="sm:w-8 sm:h-8", fill="none", stroke="currentColor", viewBox="0 0 24 24")
path(stroke-linecap="round", stroke-linejoin="round", stroke-width="2", d="M15 19l-7-7 7-7")
button.absolute.z-20.transform(
class=`top-1/2 -right-6 sm:right-2 -translate-y-1/2 bg-white/40 hover:bg-white/80 text-nls-black rounded-full p-1 sm:p-3 shadow-lg transition-all focus:outline-none focus:ring-2 focus:ring-nls-${color}`,
aria-label="Next slide",
onclick=`carouselNext('${id}'); umami.track('carousel navigation', { direction: 'next', category: '${category}', visitDuration: getVisitDuration() })`
)
svg.w-8.h-8.text-gray-800(class="sm:w-8 sm:h-8", fill="none", stroke="currentColor", viewBox="0 0 24 24")
path(stroke-linecap="round", stroke-linejoin="round", stroke-width="2", d="M9 5l7 7-7 7")
//- Carousel track
.carousel-track.flex.overflow-x-auto.rounded-lg.scroll-smooth.scrollbar-hide(
style="scroll-snap-type: x mandatory",
role="region",
aria-label="Image carousel"
)
each item, index in items
- const href = item?.href || "#";
.carousel-slide.flex-none.w-full.relative(class=slideClasses, style="scroll-snap-align: start")
if item.type === "image"
a.text-left.flex.flex-col.relative.overflow-hidden.rounded-lg.mb-4.py-4(
class="sm:px-6 sm:flex-row sm:gap-6",
onclick=`umami.track('carousel image clicked', { category: '${category}', position: 'carousel', label: '${item.alt || item.caption || "image"}', visitDuration: getVisitDuration() })`,
href=href,
target="_blank",
rel="noopener noreferrer"
)
img.w-full.h-full.object-contain.transition-transform.rounded-lg.duration-300.shadow-lg(
class="sm:w-1/2",
src=item.src,
alt=item.alt,
loading=index < 3 ? "eager" : "lazy"
)
if item.caption
.p-4(class="sm:static sm:self-center sm:w-1/2")
p.text-lg.pt-2.font-bold(class="sm:text-xl")= item.caption
if item.description
p.text-sm.mt-2(class="sm:text-base")= item.description
//- Technology pills
if item.technologies && item.technologies.length > 0
.flex.flex-wrap.gap-2.mt-3
each tech in item.technologies
span.inline-block.px-3.py-1.text-sm.font-medium.rounded-full.shadow-sm(
class=`bg-nls-${color}/10 text-nls-${color} border border-nls-${color}/20 dark:bg-nls-${colorInverted}/20 dark:text-nls-${colorInverted} dark:border-nls-${colorInverted}/30`
)= tech
if item.href
p.text-sm.mt-4.underline(class="sm:text-base hover:text-nls-black dark:hover:text-nls-white")= item.href
else if item.type === "brand"
.aspect-video.flex.items-center.justify-center.mx-2.mb-4(class="sm:mx-12")
img.max-w-full.max-h-full.min-h-64.object-contain.p-4(src=item.logo, alt=item.name, loading=index < 6 ? "eager" : "lazy")
if autoScroll
script.
document.addEventListener("DOMContentLoaded", () => {
setInterval(
() => {
const track = document.querySelector(`##{id} .carousel-track`);
if (track.scrollLeft + track.clientWidth >= track.scrollWidth) {
track.scrollTo({left: 0, behavior: "smooth"});
} else {
carouselNext("#{id}");
}
},
#{autoScroll},
);
});