penguinui is now in the root dir in penguinui-components/
This commit is contained in:
89
penguinui-components/carousel/carousel-on-card.html
Normal file
89
penguinui-components/carousel/carousel-on-card.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<article class="group flex rounded-radius max-w-sm flex-col overflow-hidden border border-outline bg-surface-alt text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark" style="-webkit-mask-image: -webkit-radial-gradient(white, black)">
|
||||
<!-- Carousel -->
|
||||
<div class="h-48 md:h-64 overflow-hidden">
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/nft-1.webp',
|
||||
imgAlt: 'An illustration of a cyberpunk-style ape wearing a hoodie and futuristic headphones.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/nft-2.webp',
|
||||
imgAlt: 'An illustration of a cyberpunk-style ape wearing ajacket.',
|
||||
},
|
||||
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/nft-3.webp',
|
||||
imgAlt: 'An illustration of a cyberpunk-style ape wearing a cape with chains on the neck.',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- previous button -->
|
||||
<button type="button" class="absolute left-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="previous slide" x-on:click="previous()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pr-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- next button -->
|
||||
<button type="button" class="absolute right-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="next slide" x-on:click="next()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pl-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- slides -->
|
||||
<div class="relative h-48 md:h-64 w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-cloak x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.300ms>
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div class="flex flex-col gap-4 p-6">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col md:flex-row gap-4 md:gap-12 justify-between">
|
||||
<!-- Title -->
|
||||
<div class="flex flex-col">
|
||||
<h3 class="text-lg lg:text-xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong" aria-describedby="nftDescription">BioHazardApe #343</h3>
|
||||
</div>
|
||||
<!-- Price -->
|
||||
<span class="w-fit bg-secondary h-fit rounded-radius px-2 py-1 text-xs font-medium text-on-secondary dark:bg-secondary-dark dark:text-on-secondary-dark"><span class="sr-only">price</span>0.45 ETH</span>
|
||||
</div>
|
||||
<p id="nftDescription" class="mb-2 text-pretty text-sm">
|
||||
by <a href="#" class="text-primary dark:text-primary-dark">@apeMakers</a>
|
||||
<br/>
|
||||
<br/>
|
||||
BioHazardApe NFT showcases a captivating collection of digital artworks inspired by the wild essence of apes, each piece a gem in the jungle of digital art.
|
||||
</p>
|
||||
<!-- Button -->
|
||||
<button type="button" class="flex items-center justify-center gap-2 border border-primary whitespace-nowrap bg-primary px-4 py-2 text-center text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 dark:bg-primary-dark dark:border-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark rounded-radius">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-3.5">
|
||||
<path fill-rule="evenodd" d="M5 4a3 3 0 0 1 6 0v1h.643a1.5 1.5 0 0 1 1.492 1.35l.7 7A1.5 1.5 0 0 1 12.342 15H3.657a1.5 1.5 0 0 1-1.492-1.65l.7-7A1.5 1.5 0 0 1 4.357 5H5V4Zm4.5 0v1h-3V4a1.5 1.5 0 0 1 3 0Zm-3 3.75a.75.75 0 0 0-1.5 0v1a3 3 0 1 0 6 0v-1a.75.75 0 0 0-1.5 0v1a1.5 1.5 0 1 1-3 0v-1Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
I Must Have It
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
91
penguinui-components/carousel/carousel-with-autoplay.html
Normal file
91
penguinui-components/carousel/carousel-with-autoplay.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<div x-data="{
|
||||
// Sets the time between each slides in milliseconds
|
||||
autoplayIntervalTime: 4000,
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-1.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and light pink hues on a canvas.',
|
||||
title: 'Front end developers',
|
||||
description: 'The architects of the digital world, constantly battling against their mortal enemy – browser compatibility.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-2.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling red, yellow, and pink hues on a canvas.',
|
||||
title: 'Back end developers',
|
||||
description: 'Because not all superheroes wear capes, some wear headphones and stare at terminal screens',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-3.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and purple hues on a canvas.',
|
||||
title: 'Full stack developers',
|
||||
description: 'Where "burnout" is just a fancy term for "Tuesday".',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
isPaused: false,
|
||||
autoplayInterval: null,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
autoplay() {
|
||||
this.autoplayInterval = setInterval(() => {
|
||||
if (! this.isPaused) {
|
||||
this.next()
|
||||
}
|
||||
}, this.autoplayIntervalTime)
|
||||
},
|
||||
// Updates interval time
|
||||
setAutoplayInterval(newIntervalTime) {
|
||||
clearInterval(this.autoplayInterval)
|
||||
this.autoplayIntervalTime = newIntervalTime
|
||||
this.autoplay()
|
||||
},
|
||||
}" x-init="autoplay" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change min-h-[50svh] to your preferred height size -->
|
||||
<div class="relative min-h-[50svh] w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-cloak x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.1000ms>
|
||||
|
||||
<!-- Title and description -->
|
||||
<div class="lg:px-32 lg:py-14 absolute inset-0 z-10 flex flex-col items-center justify-end gap-2 bg-linear-to-t from-surface-dark/85 to-transparent px-16 py-12 text-center">
|
||||
<h3 class="w-full lg:w-[80%] text-balance text-2xl lg:text-3xl font-bold text-on-surface-dark-strong" x-text="slide.title" x-bind:aria-describedby="'slide' + (index + 1) + 'Description'"></h3>
|
||||
<p class="lg:w-1/2 w-full text-pretty text-sm text-on-surface-dark" x-text="slide.description" x-bind:id="'slide' + (index + 1) + 'Description'"></p>
|
||||
</div>
|
||||
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Pause/Play Button -->
|
||||
<button type="button" class="absolute bottom-5 right-5 z-20 rounded-full text-on-surface-dark opacity-50 transition hover:opacity-80 focus-visible:opacity-80 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-dark active:outline-offset-0" aria-label="pause carousel" x-on:click="(isPaused = !isPaused), setAutoplayInterval(autoplayIntervalTime)" x-bind:aria-pressed="isPaused">
|
||||
<svg x-cloak x-show="isPaused" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-7">
|
||||
<path fill-rule="evenodd" d="M2 10a8 8 0 1 1 16 0 8 8 0 0 1-16 0Zm6.39-2.908a.75.75 0 0 1 .766.027l3.5 2.25a.75.75 0 0 1 0 1.262l-3.5 2.25A.75.75 0 0 1 8 12.25v-4.5a.75.75 0 0 1 .39-.658Z" clip-rule="evenodd">
|
||||
</svg>
|
||||
<svg x-cloak x-show="!isPaused" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-7">
|
||||
<path fill-rule="evenodd" d="M2 10a8 8 0 1 1 16 0 8 8 0 0 1-16 0Zm5-2.25A.75.75 0 0 1 7.75 7h.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1-.75-.75v-4.5Zm4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1-.75-.75v-4.5Z" clip-rule="evenodd">
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 px-1.5 py-1 md:px-2" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition" x-on:click="(currentSlideIndex = index + 1), setAutoplayInterval(autoplayIntervalTime)" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-on-surface-dark' : 'bg-on-surface-dark/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
85
penguinui-components/carousel/carousel-with-cta-button.html
Normal file
85
penguinui-components/carousel/carousel-with-cta-button.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-1.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and light pink hues on a canvas.',
|
||||
title: 'Front end developers',
|
||||
description: 'The architects of the digital world, constantly battling against their mortal enemy – browser compatibility.',
|
||||
ctaUrl: 'https://example.com',
|
||||
ctaText: 'Become a Developer',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-2.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling red, yellow, and pink hues on a canvas.',
|
||||
title: 'Back end developers',
|
||||
description: 'Because not all superheroes wear capes, some wear headphones and stare at terminal screens',
|
||||
ctaUrl: 'https://example.com',
|
||||
ctaText: 'Become a Developer',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-3.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and purple hues on a canvas.',
|
||||
title: 'Full stack developers',
|
||||
description: 'Where "burnout" is just a fancy term for "Tuesday".',
|
||||
ctaUrl: 'https://example.com',
|
||||
ctaText: 'Become a Developer',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- previous button -->
|
||||
<button type="button" class="absolute left-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="previous slide" x-on:click="previous()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pr-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- next button -->
|
||||
<button type="button" class="absolute right-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="next slide" x-on:click="next()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pl-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change min-h-[50svh] to your preferred height size -->
|
||||
<div class="relative min-h-[50svh] w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-cloak x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.1000ms>
|
||||
|
||||
<!-- Title and description -->
|
||||
<div class="lg:px-32 lg:py-14 absolute inset-0 z-10 flex flex-col items-center justify-end gap-2 bg-linear-to-t from-surface-dark/85 to-transparent px-16 py-12 text-center">
|
||||
<h3 class="w-full lg:w-[80%] text-balance text-2xl lg:text-3xl font-bold text-on-surface-dark-strong" x-text="slide.title" x-bind:aria-describedby="'slide' + (index + 1) + 'Description'"></h3>
|
||||
<p class="lg:w-1/2 w-full text-pretty text-sm text-on-surface-dark" x-text="slide.description" x-bind:id="'slide' + (index + 1) + 'Description'"></p>
|
||||
<button type="button" x-cloak x-show="slide.ctaUrl !== null" class="mt-2 whitespace-nowrap rounded-radius border border-on-surface-dark-strong bg-transparent px-4 py-2 text-center text-xs font-medium tracking-wide text-on-surface-dark-strong transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-on-surface-dark-strong active:opacity-100 active:outline-offset-0 md:text-sm" x-text="slide.ctaText"></button>
|
||||
</div>
|
||||
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 px-1.5 py-1 md:px-2" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition" x-on:click="currentSlideIndex = index + 1" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-neutral-300' : 'bg-neutral-300/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,66 @@
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/aspect-slide-1.webp',
|
||||
imgAlt: 'New collection - ride the wave of excitement.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/aspect-slide-2.webp',
|
||||
imgAlt: 'Up to 30% discount, gear up for adventure.',
|
||||
},
|
||||
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/aspect-slide-3.webp',
|
||||
imgAlt: '30% off all surfing essentials, ride like a pro.',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- previous button -->
|
||||
<button type="button" class="absolute left-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="previous slide" x-on:click="previous()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pr-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- next button -->
|
||||
<button type="button" class="absolute right-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="next slide" x-on:click="next()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pl-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change aspect-3/1 to match your images aspect ratio -->
|
||||
<div class="relative aspect-3/1 w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-cloak x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.700ms>
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 bg-surface/75 px-1.5 py-1 md:px-2 dark:bg-surface-dark/75" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition bg-on-surface dark:bg-on-surface-dark" x-on:click="currentSlideIndex = index + 1" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-on-surface dark:bg-on-surface-dark' : 'bg-on-surface/50 dark:bg-on-surface-dark/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
78
penguinui-components/carousel/carousel-with-text.html
Normal file
78
penguinui-components/carousel/carousel-with-text.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-1.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and light pink hues on a canvas.',
|
||||
title: 'Front end developers',
|
||||
description: 'The architects of the digital world, constantly battling against their mortal enemy – browser compatibility.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-2.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling red, yellow, and pink hues on a canvas.',
|
||||
title: 'Back end developers',
|
||||
description: 'Because not all superheroes wear capes, some wear headphones and stare at terminal screens',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-3.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and purple hues on a canvas.',
|
||||
title: 'Full stack developers',
|
||||
description: 'Where "burnout" is just a fancy term for "Tuesday".'
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- previous button -->
|
||||
<button type="button" class="absolute left-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="previous slide" x-on:click="previous()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pr-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- next button -->
|
||||
<button type="button" class="absolute right-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="next slide" x-on:click="next()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pl-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change min-h-[50svh] to your preferred height size -->
|
||||
<div class="relative min-h-[50svh] w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-cloak x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.1000ms>
|
||||
|
||||
<!-- Title and description -->
|
||||
<div class="lg:px-32 lg:py-14 absolute inset-0 z-10 flex flex-col items-center justify-end gap-2 bg-linear-to-t from-surface-dark/85 to-transparent px-16 py-12 text-center">
|
||||
<h3 class="w-full lg:w-[80%] text-balance text-2xl lg:text-3xl font-bold text-on-surface-dark-strong" x-text="slide.title" x-bind:aria-describedby="'slide' + (index + 1) + 'Description'"></h3>
|
||||
<p class="lg:w-1/2 w-full text-pretty text-sm text-on-surface-dark" x-text="slide.description" x-bind:id="'slide' + (index + 1) + 'Description'"></p>
|
||||
</div>
|
||||
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 px-1.5 py-1 md:px-2" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition" x-on:click="currentSlideIndex = index + 1" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-on-surface-dark' : 'bg-on-surface-dark/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
72
penguinui-components/carousel/carousel-with-touch.html
Normal file
72
penguinui-components/carousel/carousel-with-touch.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-1.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and light pink hues on a canvas.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-2.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling red, yellow, and pink hues on a canvas.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-3.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and purple hues on a canvas.',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
touchStartX: null,
|
||||
touchEndX: null,
|
||||
swipeThreshold: 50,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
handleTouchStart(event) {
|
||||
this.touchStartX = event.touches[0].clientX
|
||||
},
|
||||
handleTouchMove(event) {
|
||||
this.touchEndX = event.touches[0].clientX
|
||||
},
|
||||
handleTouchEnd() {
|
||||
if(this.touchEndX){
|
||||
if (this.touchStartX - this.touchEndX > this.swipeThreshold) {
|
||||
this.next()
|
||||
}
|
||||
if (this.touchStartX - this.touchEndX < -this.swipeThreshold) {
|
||||
this.previous()
|
||||
}
|
||||
this.touchStartX = null
|
||||
this.touchEndX = null
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change min-h-[50svh] to your preferred height size -->
|
||||
<div class="relative min-h-[50svh] w-full" x-on:touchstart="handleTouchStart($event)" x-on:touchmove="handleTouchMove($event)" x-on:touchend="handleTouchEnd()">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.700ms>
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 bg-surface/75 px-1.5 py-1 md:px-2 dark:bg-surface-dark/75" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition bg-on-surface dark:bg-on-surface-dark" x-on:click="currentSlideIndex = index + 1" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-on-surface dark:bg-on-surface-dark' : 'bg-on-surface/50 dark:bg-on-surface-dark/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
65
penguinui-components/carousel/default-carousel.html
Normal file
65
penguinui-components/carousel/default-carousel.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<div x-data="{
|
||||
slides: [
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-1.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and light pink hues on a canvas.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-2.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling red, yellow, and pink hues on a canvas.',
|
||||
},
|
||||
{
|
||||
imgSrc: 'https://penguinui.s3.amazonaws.com/component-assets/carousel/default-slide-3.webp',
|
||||
imgAlt: 'Vibrant abstract painting with swirling blue and purple hues on a canvas.',
|
||||
},
|
||||
],
|
||||
currentSlideIndex: 1,
|
||||
previous() {
|
||||
if (this.currentSlideIndex > 1) {
|
||||
this.currentSlideIndex = this.currentSlideIndex - 1
|
||||
} else {
|
||||
// If it's the first slide, go to the last slide
|
||||
this.currentSlideIndex = this.slides.length
|
||||
}
|
||||
},
|
||||
next() {
|
||||
if (this.currentSlideIndex < this.slides.length) {
|
||||
this.currentSlideIndex = this.currentSlideIndex + 1
|
||||
} else {
|
||||
// If it's the last slide, go to the first slide
|
||||
this.currentSlideIndex = 1
|
||||
}
|
||||
},
|
||||
}" class="relative w-full overflow-hidden">
|
||||
|
||||
<!-- previous button -->
|
||||
<button type="button" class="absolute left-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="previous slide" x-on:click="previous()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pr-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- next button -->
|
||||
<button type="button" class="absolute right-5 top-1/2 z-20 flex rounded-full -translate-y-1/2 items-center justify-center bg-surface/40 p-2 text-on-surface transition hover:bg-surface/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:outline-offset-0 dark:bg-surface-dark/40 dark:text-on-surface-dark dark:hover:bg-surface-dark/60 dark:focus-visible:outline-primary-dark" aria-label="next slide" x-on:click="next()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="size-5 md:size-6 pl-0.5" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- slides -->
|
||||
<!-- Change min-h-[50svh] to your preferred height size -->
|
||||
<div class="relative min-h-[50svh] w-full">
|
||||
<template x-for="(slide, index) in slides">
|
||||
<div x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.1000ms>
|
||||
<img class="absolute w-full h-full inset-0 object-cover text-on-surface dark:text-on-surface-dark" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- indicators -->
|
||||
<div class="absolute rounded-radius bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-4 md:gap-3 bg-surface/75 px-1.5 py-1 md:px-2 dark:bg-surface-dark/75" role="group" aria-label="slides" >
|
||||
<template x-for="(slide, index) in slides">
|
||||
<button class="size-2 rounded-full transition bg-on-surface dark:bg-on-surface-dark" x-on:click="currentSlideIndex = index + 1" x-bind:class="[currentSlideIndex === index + 1 ? 'bg-on-surface dark:bg-on-surface-dark' : 'bg-on-surface/50 dark:bg-on-surface-dark/50']" x-bind:aria-label="'slide ' + (index + 1)"></button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user