Add marketing motion reveals to blog and occasions
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-21 15:22:39 +01:00
parent 941931934f
commit 50cc4e76df
16 changed files with 1869 additions and 781 deletions

View File

@@ -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>