Polish pass: micro-interactions on admin and storefront pages
Admin design system: - AdminCard: hover shadow elevation - AdminTable: enhanced empty state, row hover - AdminBadge: tracking-wide typography - AdminButton: active scale press feedback - AdminFormElements: input transitions - AdminFilterBar: search input polish - PageHeader: icon hover scale, chevron separator - AdminToggle: glow effect, thumb shadow - AdminEmptyState: icon hover, spacing - AdminPagination: active solid bg, ellipsis Storefronts (tuxedo + indian-river-direct): - Button hover lifts (-translate-y-0.5) - Shadow elevations on hover - Button press feedback - Consistent timing (duration-200) - Product card image zoom - FAQ empty state fix
This commit is contained in:
@@ -151,7 +151,7 @@ export default function IndianRiverFAQPage() {
|
||||
<LayoutContainer>
|
||||
<div className="max-w-3xl mx-auto">
|
||||
{/* Page header */}
|
||||
<div className="text-center mb-14 p-10 rounded-3xl bg-white border-2 border-stone-200 shadow-xl">
|
||||
<div className="text-center mb-14 p-10 rounded-3xl bg-white border-2 border-stone-200 shadow-xl transition-all duration-300 hover:shadow-2xl hover:border-blue-100">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-blue-600 mb-4">Questions</p>
|
||||
<h1 className="text-5xl md:text-6xl font-black tracking-tight text-stone-950 leading-tight">
|
||||
FAQ
|
||||
|
||||
@@ -354,13 +354,13 @@ export default function IndianRiverDirectPage() {
|
||||
) : (
|
||||
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{products.map((product) => (
|
||||
<div key={product.id} className="rounded-2xl bg-white border-2 border-stone-200 overflow-hidden shadow-xl hover:shadow-2xl hover:-translate-y-2 hover:border-blue-200 transition-all duration-300">
|
||||
<div key={product.id} className="rounded-2xl bg-white border-2 border-stone-200 overflow-hidden shadow-xl hover:shadow-2xl hover:-translate-y-2 hover:border-blue-200 transition-all duration-300 group">
|
||||
{/* Product Image */}
|
||||
<div className="aspect-[4/3] bg-gradient-to-br from-blue-600 to-blue-700 flex items-center justify-center relative">
|
||||
<div className="aspect-[4/3] bg-gradient-to-br from-blue-600 to-blue-700 flex items-center justify-center relative overflow-hidden">
|
||||
{product.image_url ? (
|
||||
<Image src={product.image_url} alt={product.name} fill className="object-cover" sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" />
|
||||
<Image src={product.image_url} alt={product.name} fill className="object-cover transition-transform duration-500 group-hover:scale-105" sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" />
|
||||
) : (
|
||||
<div className="text-6xl opacity-30">🍑</div>
|
||||
<div className="text-6xl opacity-30 transition-transform duration-500 group-hover:scale-110">🍑</div>
|
||||
)}
|
||||
{/* Badges */}
|
||||
<div className="absolute top-3 left-3 flex flex-wrap gap-2">
|
||||
@@ -393,7 +393,7 @@ export default function IndianRiverDirectPage() {
|
||||
{product.price_tba ? "Price TBA" : `$${product.price.toFixed(2)}`}
|
||||
</span>
|
||||
{product.price > 0 && (
|
||||
<button className="rounded-xl bg-blue-600 hover:bg-blue-500 px-5 py-2.5 text-sm font-bold text-white transition-all shadow-lg hover:shadow-xl">
|
||||
<button className="rounded-xl bg-blue-600 hover:bg-blue-500 active:bg-blue-700 px-5 py-2.5 text-sm font-bold text-white transition-all duration-200 shadow-lg hover:shadow-xl hover:-translate-y-0.5 active:translate-y-0">
|
||||
{product.preorder ? "Pre-Order" : "Add to Cart"}
|
||||
</button>
|
||||
)}
|
||||
@@ -483,10 +483,10 @@ export default function IndianRiverDirectPage() {
|
||||
<h2 className="text-2xl md:text-3xl font-black text-white">Ready to order?</h2>
|
||||
<p className="mt-2 text-blue-100">Find a stop near you or browse our seasonal products.</p>
|
||||
<div className="mt-8 flex gap-4 justify-center flex-wrap">
|
||||
<Link href="/indian-river-direct#stops" className="rounded-2xl bg-white px-8 py-4 font-bold text-blue-600 hover:bg-blue-50 active:bg-blue-100 hover:-translate-y-0.5 hover:shadow-lg shadow-sm transition-all text-sm tracking-wider">
|
||||
<Link href="/indian-river-direct#stops" className="rounded-2xl bg-white px-8 py-4 font-bold text-blue-600 hover:bg-blue-50 active:bg-blue-100 hover:-translate-y-0.5 hover:shadow-xl shadow-sm transition-all duration-200 text-sm tracking-wider">
|
||||
Find a Stop
|
||||
</Link>
|
||||
<Link href="/indian-river-direct/about" className="rounded-2xl bg-blue-700/50 backdrop-blur-sm border border-white/30 px-8 py-4 font-bold text-white hover:bg-blue-600/80 hover:-translate-y-0.5 hover:shadow-lg shadow-sm transition-all text-sm tracking-wider">
|
||||
<Link href="/indian-river-direct/about" className="rounded-2xl bg-blue-700/50 backdrop-blur-sm border border-white/30 px-8 py-4 font-bold text-white hover:bg-blue-600/80 hover:-translate-y-0.5 hover:shadow-xl shadow-sm transition-all duration-200 text-sm tracking-wider">
|
||||
Our Story
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -90,46 +90,46 @@ function FAQAccordion({ items, searchQuery }: { items: FAQItem[]; searchQuery: s
|
||||
);
|
||||
}, [items, searchQuery]);
|
||||
|
||||
if (filtered.length === 0) {
|
||||
return (
|
||||
<div className="rounded-2xl bg-white p-6 sm:p-8 text-center shadow-sm ring-1 ring-stone-200/60">
|
||||
<p className="text-stone-500 text-sm">No results for "{searchQuery}"</p>
|
||||
<p className="mt-1 text-stone-400 text-sm">Try a different term or browse all categories below.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{filtered.map((item) => {
|
||||
const isOpen = open === item.question;
|
||||
return (
|
||||
<div
|
||||
key={item.question}
|
||||
className="rounded-2xl bg-white shadow-sm ring-1 ring-stone-200/60 overflow-hidden transition-all duration-300 hover:ring-stone-300"
|
||||
>
|
||||
<button
|
||||
onClick={() => setOpen(isOpen ? null : item.question)}
|
||||
className="flex w-full items-center justify-between px-4 sm:px-6 py-4 sm:py-5 text-left group"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<span className="font-semibold text-stone-950 pr-3 sm:pr-4 text-sm sm:text-[15px] leading-snug group-hover:text-emerald-700 transition-colors">
|
||||
{item.question}
|
||||
</span>
|
||||
<span className={`flex-shrink-0 w-6 h-6 sm:w-7 sm:h-7 rounded-full flex items-center justify-center transition-all duration-300 ${isOpen ? "bg-emerald-600 text-white rotate-180" : "bg-stone-100 text-stone-400 group-hover:bg-stone-200"}`}>
|
||||
<svg className="w-3 h-3 sm:w-3.5 sm:h-3.5 transition-transform duration-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-out ${isOpen ? "max-h-48 opacity-100" : "max-h-0 opacity-0"}`}>
|
||||
<div className="px-4 sm:px-6 pb-5 sm:pb-6 pt-1 border-t border-stone-100">
|
||||
<p className="text-sm text-stone-500 leading-relaxed">{item.answer}</p>
|
||||
{filtered.length === 0 ? (
|
||||
<div className="rounded-2xl bg-white p-6 sm:p-8 text-center shadow-sm ring-1 ring-stone-200/60 transition-all duration-300 hover:shadow-lg hover:ring-stone-300">
|
||||
<p className="text-stone-500 text-sm">No results for "{searchQuery}"</p>
|
||||
<p className="mt-1 text-stone-400 text-sm">Try a different term or browse all categories below.</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{filtered.map((item) => {
|
||||
const isOpen = open === item.question;
|
||||
return (
|
||||
<div
|
||||
key={item.question}
|
||||
className="rounded-2xl bg-white shadow-sm ring-1 ring-stone-200/60 overflow-hidden transition-all duration-300 hover:ring-stone-300"
|
||||
>
|
||||
<button
|
||||
onClick={() => setOpen(isOpen ? null : item.question)}
|
||||
className="flex w-full items-center justify-between px-4 sm:px-6 py-4 sm:py-5 text-left group"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<span className="font-semibold text-stone-950 pr-3 sm:pr-4 text-sm sm:text-[15px] leading-snug group-hover:text-emerald-700 transition-colors">
|
||||
{item.question}
|
||||
</span>
|
||||
<span className={`flex-shrink-0 w-6 h-6 sm:w-7 sm:h-7 rounded-full flex items-center justify-center transition-all duration-300 ${isOpen ? "bg-emerald-600 text-white rotate-180" : "bg-stone-100 text-stone-400 group-hover:bg-stone-200"}`}>
|
||||
<svg className="w-3 h-3 sm:w-3.5 sm:h-3.5 transition-transform duration-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-out ${isOpen ? "max-h-48 opacity-100" : "max-h-0 opacity-0"}`}>
|
||||
<div className="px-4 sm:px-6 pb-5 sm:pb-6 pt-1 border-t border-stone-100">
|
||||
<p className="text-sm text-stone-500 leading-relaxed">{item.answer}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ export default function TuxedoFAQPage() {
|
||||
placeholder="Search questions..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full rounded-2xl border border-stone-200 bg-white pl-10 sm:pl-11 pr-5 py-3.5 sm:py-4 text-sm sm:text-base text-stone-900 placeholder-stone-400 outline-none focus:border-stone-900 focus:ring-1 focus:ring-stone-900 transition-colors shadow-sm"
|
||||
className="w-full rounded-2xl border border-stone-200 bg-white pl-10 sm:pl-11 pr-5 py-3.5 sm:py-4 text-sm sm:text-base text-stone-900 placeholder-stone-400 outline-none focus:border-stone-900 focus:ring-1 focus:ring-stone-900/20 transition-all duration-200 shadow-sm hover:shadow-md focus:shadow-lg"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
|
||||
@@ -519,7 +519,7 @@ export default function TuxedoPage() {
|
||||
<div className="mx-auto max-w-6xl px-6">
|
||||
<Link
|
||||
href="/wholesale/login"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-emerald-700 px-5 py-2 text-sm font-semibold text-white hover:bg-emerald-600 transition-colors"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-emerald-700 px-5 py-2 text-sm font-semibold text-white hover:bg-emerald-600 active:bg-emerald-800 transition-all duration-200 hover:-translate-y-0.5"
|
||||
>
|
||||
Wholesale Portal →
|
||||
</Link>
|
||||
|
||||
@@ -134,7 +134,7 @@ export default function StopPage() {
|
||||
</div>
|
||||
|
||||
{/* Stop info */}
|
||||
<div className="rounded-3xl bg-white p-8 mb-14 shadow-sm ring-1 ring-stone-200/60">
|
||||
<div className="rounded-3xl bg-white p-8 mb-14 shadow-sm ring-1 ring-stone-200/60 transition-all duration-300 hover:shadow-xl hover:ring-stone-300/60">
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<div className="flex items-start gap-4 py-4 px-5 rounded-2xl bg-stone-50">
|
||||
<div className={`flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center ${isBlue ? "bg-blue-50" : "bg-emerald-50"}`}>
|
||||
|
||||
Reference in New Issue
Block a user