/
index.html
173 lines (164 loc) · 10.6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Fancy Testimonials Slider</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
inter: ['Inter', 'sans-serif'],
},
},
},
};
</script>
</head>
<body class="relative font-inter antialiased">
<main class="relative min-h-screen flex flex-col justify-center bg-slate-50 overflow-hidden">
<div class="w-full max-w-6xl mx-auto px-4 md:px-6 py-24">
<div class="flex justify-center">
<!-- Fancy testimonial slider component -->
<div class="w-full max-w-3xl mx-auto text-center" x-data="slider">
<!-- Testimonial image -->
<div class="relative h-32">
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-[480px] h-[480px] pointer-events-none before:absolute before:inset-0 before:bg-gradient-to-b before:from-indigo-500/25 before:via-indigo-500/5 before:via-25% before:to-indigo-500/0 before:to-75% before:rounded-full before:-z-10">
<div class="h-32 [mask-image:_linear-gradient(0deg,transparent,theme(colors.white)_20%,theme(colors.white))]">
<!-- Alpine.js template for testimonial images: https://github.com/alpinejs/alpine#x-for -->
<template x-for="(testimonial, index) in testimonials" :key="index">
<div
x-show="active === index"
class="absolute inset-0 -z-10"
x-transition:enter="transition ease-[cubic-bezier(0.68,-0.3,0.32,1)] duration-700 order-first"
x-transition:enter-start="opacity-0 -rotate-[60deg]"
x-transition:enter-end="opacity-100 rotate-0"
x-transition:leave="transition ease-[cubic-bezier(0.68,-0.3,0.32,1)] duration-700"
x-transition:leave-start="opacity-100 rotate-0"
x-transition:leave-end="opacity-0 rotate-[60deg]"
>
<img class="relative top-11 left-1/2 -translate-x-1/2 rounded-full" :src="testimonial.img" width="56" height="56" :alt="testimonial.name">
</div>
</template>
</div>
</div>
</div>
<!-- Text -->
<div class="mb-9">
<div class="relative flex flex-col transition-all duration-150 delay-300 ease-in-out" x-ref="testimonials">
<!-- Alpine.js template for testimonials: https://github.com/alpinejs/alpine#x-for -->
<template x-for="(testimonial, index) in testimonials" :key="index">
<div
x-show="active === index"
x-transition:enter="transition ease-in-out duration-500 delay-200 order-first"
x-transition:enter-start="opacity-0 -translate-x-4"
x-transition:enter-end="opacity-100 translate-x-0"
x-transition:leave="transition ease-out duration-300 delay-300 absolute"
x-transition:leave-start="opacity-100 translate-x-0"
x-transition:leave-end="opacity-0 translate-x-4"
>
<div class="text-2xl font-bold text-slate-900 before:content-['\201C'] after:content-['\201D']" x-text="testimonial.quote"></div>
</div>
</template>
</div>
</div>
<!-- Buttons -->
<div class="flex flex-wrap justify-center -m-1.5">
<!-- Alpine.js template for buttons: https://github.com/alpinejs/alpine#x-for -->
<template x-for="(testimonial, index) in testimonials" :key="index">
<button
class="inline-flex justify-center whitespace-nowrap rounded-full px-3 py-1.5 m-1.5 text-xs shadow-sm focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150"
:class="active === index ? 'bg-indigo-500 text-white shadow-indigo-950/10' : 'bg-white hover:bg-indigo-100 text-slate-900'"
@click="active = index; stopAutorotate();"
>
<span x-text="testimonial.name"></span> <span :class="active === index ? 'text-indigo-200' : 'text-slate-300'">-</span> <span x-text="testimonial.role"></span>
</button>
</template>
</div>
</div>
<!-- Slider data and functionality: https://github.com/alpinejs/alpine -->
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('slider', () => ({
active: 0,
autorotate: true,
autorotateTiming: 7000,
testimonials: [
{
img: 'testimonial-01.jpg',
quote: "The ability to capture responses is a game-changer. If a user gets tired of the sign up and leaves, that data is still persisted. Additionally, it's great to select between formats.",
name: 'Jessie J',
role: 'Acme LTD'
},
{
img: 'testimonial-02.jpg',
quote: "Having the power to capture user feedback is revolutionary. Even if a participant abandons the sign-up process midway, their valuable input remains intact.",
name: 'Nick V',
role: 'Malika Inc.'
},
{
img: 'testimonial-03.jpg',
quote: "The functionality to capture responses is a true game-changer. Even if a user becomes fatigued during sign-up and abandons the process, their information remains stored.",
name: 'Amelia W',
role: 'Panda AI'
},
],
init() {
if (this.autorotate) {
this.autorotateInterval = setInterval(() => {
this.active = this.active + 1 === this.testimonials.length ? 0 : this.active + 1
}, this.autorotateTiming)
}
this.$watch('active', callback => this.heightFix())
},
stopAutorotate() {
clearInterval(this.autorotateInterval)
this.autorotateInterval = null
},
heightFix() {
this.$nextTick(() => {
this.$refs.testimonials.style.height = this.$refs.testimonials.children[this.active + 1].offsetHeight + 'px'
})
}
}))
})
</script>
<!-- End: Fancy testimonial slider component -->
</div>
</div>
</main>
<!-- Page footer -->
<footer class="absolute left-6 right-6 md:left-12 md:right-auto bottom-4 md:bottom-8 text-center md:text-left">
<a class="text-xs text-slate-500 hover:underline" href="https://cruip.com">©Cruip - Tailwind CSS templates</a>
</footer>
<!-- Banner with links -->
<div class="fixed bottom-0 right-0 w-full md:bottom-6 md:right-12 md:w-auto z-50" :class="bannerOpen ? '' : 'hidden'" x-data="{ bannerOpen: true }">
<div class="bg-slate-800 text-sm p-3 md:rounded shadow flex justify-between">
<div class="text-slate-500 inline-flex">
<a class="font-medium hover:underline text-slate-300" href="https://cruip.com/how-to-build-a-fancy-testimonial-slider-with-tailwind-css-and-alpine-js/">
Read Tutorial
</a>
<span class="italic px-1.5">or</span>
<a class="font-medium hover:underline text-indigo-500 flex items-center" href="https://github.com/cruip/cruip-tutorials/blob/main/fancy-testimonials-slider/index.html" target="_blank" rel="noreferrer">
<span>Download</span>
<svg class="fill-indigo-400 ml-1" xmlns="http://www.w3.org/2000/svg" width="9" height="9">
<path d="m1.649 8.514-.91-.915 5.514-5.523H2.027l.01-1.258h6.388v6.394H7.158l.01-4.226z" />
</svg>
</a>
</div>
<button class="text-slate-500 hover:text-slate-400 pl-2 ml-3 border-l border-slate-700" @click="bannerOpen = false">
<span class="sr-only">Close</span>
<svg class="w-4 h-4 shrink-0 fill-current" viewBox="0 0 16 16">
<path d="M12.72 3.293a1 1 0 00-1.415 0L8.012 6.586 4.72 3.293a1 1 0 00-1.414 1.414L6.598 8l-3.293 3.293a1 1 0 101.414 1.414l3.293-3.293 3.293 3.293a1 1 0 001.414-1.414L9.426 8l3.293-3.293a1 1 0 000-1.414z" />
</svg>
</button>
</div>
</div>
</body>
</html>