first commitv1
This commit is contained in:
111
src/components/Testimonials.jsx
Normal file
111
src/components/Testimonials.jsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Star } from 'lucide-react'
|
||||
|
||||
const reviews = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Thomas M.',
|
||||
role: 'Entrepreneur',
|
||||
text: 'Le forfait signature vaut chaque euro. Ambiance feutrée, coupe millimétrée — je ne vais plus ailleurs.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Alexandre R.',
|
||||
role: 'Avocat',
|
||||
text: 'Barbe sculptée comme je voulais depuis des années. L’équipe est à l’écoute et très pro.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Julien L.',
|
||||
role: 'Architecte',
|
||||
text: 'Détails, timing, produits : tout est au niveau d’un grand palace. RDV facile à prendre.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Karim D.',
|
||||
role: 'Consultant',
|
||||
text: 'Premier camouflage gris : naturel à souhait. On m’a expliqué l’entretien sans jargon, top.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Nicolas P.',
|
||||
role: 'Chef de projet',
|
||||
text: 'Le rasage traditionnel est une claque. Serviette chaude, zéro irritation. Je recommande les yeux fermés.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Hugo B.',
|
||||
role: 'Photographe',
|
||||
text: 'J’apprécie le calme du lieu et le fait qu’on ne soit jamais bousculé. La coupe tient bien trois semaines.',
|
||||
rating: 5,
|
||||
},
|
||||
]
|
||||
|
||||
function Stars({ count }) {
|
||||
return (
|
||||
<div className="flex gap-0.5" aria-label={`${count} sur 5 étoiles`}>
|
||||
{Array.from({ length: count }, (_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className="h-4 w-4 fill-barber-gold text-barber-gold"
|
||||
aria-hidden
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Testimonials() {
|
||||
return (
|
||||
<section className="border-t border-white/5 bg-barber-stone py-20 sm:py-28">
|
||||
<div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-80px' }}
|
||||
transition={{ duration: 0.55 }}
|
||||
className="mb-12 text-center"
|
||||
>
|
||||
<p className="mb-3 text-xs font-medium uppercase tracking-[0.3em] text-barber-gold">
|
||||
Témoignages
|
||||
</p>
|
||||
<h2 className="font-serif text-3xl font-medium text-barber-cream sm:text-4xl md:text-5xl">
|
||||
Avis clients
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-sm text-barber-cream/60 sm:text-base">
|
||||
Une communauté fidèle de professionnels et de passionnés de style — merci à tous pour la confiance
|
||||
accordée au fil des années.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<ul className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{reviews.map((r, i) => (
|
||||
<motion.li
|
||||
key={r.id}
|
||||
initial={{ opacity: 0, y: 24 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-40px' }}
|
||||
transition={{ delay: (i % 3) * 0.06, duration: 0.5 }}
|
||||
>
|
||||
<blockquote className="flex h-full flex-col rounded-lg border border-white/10 bg-barber-bg p-6">
|
||||
<Stars count={r.rating} />
|
||||
<p className="mt-4 flex-1 text-sm leading-relaxed text-barber-cream/80">
|
||||
“{r.text}”
|
||||
</p>
|
||||
<footer className="mt-6 border-t border-white/10 pt-4">
|
||||
<cite className="not-italic text-sm font-medium text-barber-gold">{r.name}</cite>
|
||||
<p className="mt-0.5 text-xs text-barber-cream/45">{r.role}</p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user