Add marketing motion reveals to blog and occasions
This commit is contained in:
@@ -17,6 +17,7 @@ import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes';
|
||||
import { useLocale } from '@/hooks/useLocale';
|
||||
import { ArrowRight, Check, Star } from 'lucide-react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
interface Package {
|
||||
id: number;
|
||||
@@ -325,11 +326,26 @@ const Packages: React.FC<PackagesProps> = ({ endcustomerPackages, resellerPackag
|
||||
const { t } = useTranslation('marketing');
|
||||
const { t: tCommon } = useTranslation('common');
|
||||
const { flash } = usePage<{ flash?: { error?: string } }>().props;
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const {
|
||||
variant: packagesHeroVariant,
|
||||
trackClick: trackPackagesHeroClick,
|
||||
} = useCtaExperiment('packages_hero_cta');
|
||||
|
||||
const viewportOnce = { once: true, amount: 0.25 };
|
||||
const revealUp = {
|
||||
hidden: { opacity: 0, y: shouldReduceMotion ? 0 : 18 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
|
||||
},
|
||||
};
|
||||
const stagger = {
|
||||
hidden: {},
|
||||
visible: { transition: { staggerChildren: 0.12 } },
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (flash?.error) {
|
||||
toast.error(flash.error);
|
||||
@@ -960,8 +976,13 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
return (
|
||||
<MarketingLayout title={t('packages.title')}>
|
||||
<section className="bg-aurora-enhanced text-gray-900 dark:text-gray-100 px-4 py-12 md:py-16">
|
||||
<div className="container mx-auto text-center space-y-6 md:space-y-8">
|
||||
<div className="space-y-3 md:space-y-4">
|
||||
<motion.div
|
||||
className="container mx-auto text-center space-y-6 md:space-y-8"
|
||||
variants={stagger}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
<motion.div className="space-y-3 md:space-y-4" variants={revealUp}>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-gray-600 dark:text-gray-300">
|
||||
{t('packages.for_endcustomers')} · {t('packages.for_resellers')}
|
||||
</p>
|
||||
@@ -971,8 +992,8 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
<p className="mx-auto max-w-2xl font-sans-marketing text-base text-gray-700 dark:text-gray-200 md:text-xl">
|
||||
{t('packages.hero_description')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center justify-center gap-3">
|
||||
</motion.div>
|
||||
<motion.div className="flex flex-wrap items-center justify-center gap-3" variants={revealUp}>
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
@@ -1011,11 +1032,11 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
{t('packages.gift_cta', 'Paket verschenken')}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
</motion.div>
|
||||
<motion.p className="text-sm text-gray-600 dark:text-gray-300" variants={revealUp}>
|
||||
{t('packages.hero_secondary')}
|
||||
</p>
|
||||
</div>
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
<section id="packages-showcase" className="px-4 py-16 md:py-20">
|
||||
@@ -1045,14 +1066,16 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
>
|
||||
{orderedEndcustomerPackages.map((pkg) => (
|
||||
<div key={pkg.id} className="snap-start basis-[72vw] shrink-0 sm:basis-[60vw]">
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="endcustomer"
|
||||
highlight={pkg.id === highlightEndcustomerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
<motion.div variants={revealUp} initial="hidden" whileInView="visible" viewport={viewportOnce}>
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="endcustomer"
|
||||
highlight={pkg.id === highlightEndcustomerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -1060,18 +1083,27 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
</div>
|
||||
<div className="hidden gap-6 md:grid md:grid-cols-2 xl:grid-cols-3">
|
||||
{orderedEndcustomerPackages.map((pkg) => (
|
||||
<PackageCard
|
||||
<motion.div
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
variant="endcustomer"
|
||||
highlight={pkg.id === highlightEndcustomerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
variants={revealUp}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={viewportOnce}
|
||||
>
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="endcustomer"
|
||||
highlight={pkg.id === highlightEndcustomerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'endcustomer')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
<PackageComparison packages={orderedEndcustomerPackages} variant="endcustomer" />
|
||||
<motion.div variants={revealUp} initial="hidden" whileInView="visible" viewport={viewportOnce}>
|
||||
<PackageComparison packages={orderedEndcustomerPackages} variant="endcustomer" />
|
||||
</motion.div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="reseller" className="space-y-8">
|
||||
@@ -1086,14 +1118,16 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
>
|
||||
{orderedResellerPackages.map((pkg) => (
|
||||
<div key={pkg.id} className="snap-start basis-[72vw] shrink-0 sm:basis-[60vw]">
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="reseller"
|
||||
highlight={pkg.id === highlightResellerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
<motion.div variants={revealUp} initial="hidden" whileInView="visible" viewport={viewportOnce}>
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="reseller"
|
||||
highlight={pkg.id === highlightResellerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -1101,18 +1135,27 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
</div>
|
||||
<div className="hidden gap-6 md:grid md:grid-cols-2 xl:grid-cols-3">
|
||||
{orderedResellerPackages.map((pkg) => (
|
||||
<PackageCard
|
||||
<motion.div
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
variant="reseller"
|
||||
highlight={pkg.id === highlightResellerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
variants={revealUp}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={viewportOnce}
|
||||
>
|
||||
<PackageCard
|
||||
pkg={pkg}
|
||||
variant="reseller"
|
||||
highlight={pkg.id === highlightResellerId}
|
||||
onSelect={(selected) => handleCardClick(selected, 'reseller')}
|
||||
className="h-full"
|
||||
compact
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
<PackageComparison packages={orderedResellerPackages} variant="reseller" serviceTierNames={serviceTierNames} />
|
||||
<motion.div variants={revealUp} initial="hidden" whileInView="visible" viewport={viewportOnce}>
|
||||
<PackageComparison packages={orderedResellerPackages} variant="reseller" serviceTierNames={serviceTierNames} />
|
||||
</motion.div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -1131,14 +1174,16 @@ const PackageDetailGrid: React.FC<PackageDetailGridProps> = ({
|
||||
{ title: t('packages.faq_reseller'), body: t('packages.faq_reseller_desc') },
|
||||
{ title: t('packages.faq_payment'), body: t('packages.faq_payment_desc') },
|
||||
].map((item) => (
|
||||
<Card key={item.title} className="h-full border border-gray-200/70 dark:border-gray-800/70">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl font-display">{item.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{item.body}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<motion.div key={item.title} variants={revealUp} initial="hidden" whileInView="visible" viewport={viewportOnce}>
|
||||
<Card className="h-full border border-gray-200/70 dark:border-gray-800/70">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl font-display">{item.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{item.body}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user