= ({
image,
imageAlt,
title,
subtitle,
buttonLabel,
href,
onClick,
disabled = false,
className = '',
fullBleed = false,
backgroundColor,
}) => {
// Track hover state for button animation
const [isHovered, setIsHovered] = useState(false);
const handleMouseEnter = useCallback(() => {
if (!disabled) {
setIsHovered(true);
}
}, [disabled]);
const handleMouseLeave = useCallback(() => {
if (!disabled) {
setIsHovered(false);
}
}, [disabled]);
// Build class names using BEM with bds namespace
const classNames = clsx(
'bds-card-image',
disabled && 'bds-card-image--disabled',
isHovered && 'bds-card-image--hovered',
fullBleed && 'bds-card-image--full-bleed',
className
);
// Handle card click for linked cards
const handleCardClick = useCallback(
(e: React.MouseEvent) => {
// If clicking the button directly, don't navigate via card
if ((e.target as HTMLElement).closest('.bds-btn')) {
return;
}
if (href && !disabled) {
window.location.href = href;
}
},
[href, disabled]
);
// Handle button click
const handleButtonClick = useCallback(() => {
if (href) {
window.location.href = href;
} else if (onClick) {
onClick();
}
}, [href, onClick]);
// Build inline style for image container background color
const imageContainerStyle = backgroundColor
? { '--bds-card-image-bg': backgroundColor } as React.CSSProperties
: undefined;
// Common content structure
const content = (
<>
{/* Image container with customizable background */}
{/* Content area: title, subtitle, and button */}
>
);
// Render as clickable div (card itself handles navigation)
return (
{
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
window.location.href = href;
}
}
: undefined
}
aria-disabled={disabled}
>
{content}
);
};
export default CardImage;