Files
gridpilot.gg/apps/website/ui/BenefitCard.tsx
2026-01-15 17:12:24 +01:00

110 lines
3.0 KiB
TypeScript

import { motion, useReducedMotion } from 'framer-motion';
import { LucideIcon } from 'lucide-react';
import { useEffect, useState } from 'react';
import { Box } from './Box';
import { Heading } from './Heading';
import { Icon } from './Icon';
import { Surface } from './Surface';
import { Text } from './Text';
interface BenefitCardProps {
icon: LucideIcon;
title: string;
description: string;
stats?: {
value: string;
label: string;
};
variant?: 'default' | 'highlight';
delay?: number;
}
export function BenefitCard({
icon,
title,
description,
stats,
variant = 'default',
delay = 0,
}: BenefitCardProps) {
const shouldReduceMotion = useReducedMotion();
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
const isHighlight = variant === 'highlight';
const cardContent = (
<Surface
variant="muted"
rounded="xl"
border={true}
padding={6}
className={`relative h-full transition-all duration-300 group ${isHighlight ? 'border-primary-blue/30' : 'border-charcoal-outline hover:border-charcoal-outline/80'}`}
style={isHighlight ? { background: 'linear-gradient(to bottom right, rgba(25, 140, 255, 0.1), rgba(25, 140, 255, 0.05))' } : {}}
>
{/* Icon */}
<Box
width="12"
height="12"
rounded="xl"
display="flex"
center
mb={4}
bg={isHighlight ? 'bg-primary-blue/20' : 'bg-iron-gray'}
border={!isHighlight}
borderColor="border-charcoal-outline"
>
<Icon icon={icon} size={6} className={isHighlight ? 'text-primary-blue' : 'text-gray-400'} />
</Box>
{/* Content */}
<Heading level={3} mb={2}>{title}</Heading>
<Text size="sm" color="text-gray-400" block style={{ lineHeight: 1.625 }}>{description}</Text>
{/* Stats */}
{stats && (
<Box mt={4} pt={4} borderTop={true} borderColor="border-charcoal-outline/50">
<Box display="flex" alignItems="baseline" gap={2}>
<Text size="2xl" weight="bold" color={isHighlight ? 'text-primary-blue' : 'text-white'}>
{stats.value}
</Text>
<Text size="sm" color="text-gray-500">{stats.label}</Text>
</Box>
</Box>
)}
{/* Highlight Glow Effect */}
{isHighlight && (
<Box
position="absolute"
inset="0"
rounded="xl"
className="bg-gradient-to-br from-primary-blue/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
/>
)}
</Surface>
);
if (!isMounted || shouldReduceMotion) {
return <Box fullHeight>{cardContent}</Box>;
}
return (
<Box
as={motion.div}
fullHeight
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay }}
whileHover={{ y: -4, transition: { duration: 0.2 } }}
>
{cardContent}
</Box>
);
}