feat: add FDButton component, fix button hover on navy, make CTA image optional
This commit is contained in:
parent
1ab4e41c00
commit
80be2c4098
@ -2,19 +2,7 @@ import React from 'react'
|
||||
import type { FDCtaSideImageBlock as FDCtaSideImageBlockProps } from '@/payload-types'
|
||||
import type { Media } from '@/payload-types'
|
||||
import { FDImage } from '@/components/FDImage'
|
||||
|
||||
const overlayColorMap: Record<string, string> = {
|
||||
navy: 'bg-fd-navy',
|
||||
yellow: 'bg-fd-yellow',
|
||||
black: 'bg-black',
|
||||
}
|
||||
|
||||
const overlayOpacityMap: Record<string, string> = {
|
||||
'20': 'opacity-20',
|
||||
'30': 'opacity-30',
|
||||
'50': 'opacity-50',
|
||||
'70': 'opacity-70',
|
||||
}
|
||||
import { FDButton } from '@/components/FDButton'
|
||||
|
||||
export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> = ({
|
||||
heading,
|
||||
@ -24,69 +12,61 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
||||
image,
|
||||
imagePosition = 'right',
|
||||
theme = 'dark',
|
||||
customBackgroundColor,
|
||||
customTextLight = true,
|
||||
imageOverlay = 'none',
|
||||
imageOverlayOpacity = '30',
|
||||
}) => {
|
||||
const isCustom = theme === 'custom'
|
||||
const isDark = isCustom ? customTextLight : theme === 'dark'
|
||||
const media = image as Media
|
||||
|
||||
let sectionBg: string
|
||||
let sectionStyle: React.CSSProperties = {}
|
||||
if (isCustom && customBackgroundColor) {
|
||||
sectionStyle = { backgroundColor: customBackgroundColor }
|
||||
sectionBg = ''
|
||||
} else {
|
||||
sectionBg = isDark ? 'bg-fd-navy' : 'bg-white'
|
||||
}
|
||||
|
||||
const hasOverlay = imageOverlay && imageOverlay !== 'none'
|
||||
const overlayColor = overlayColorMap[imageOverlay || ''] || ''
|
||||
const overlayOpacity = overlayOpacityMap[imageOverlayOpacity || '30'] || 'opacity-30'
|
||||
const isDark = theme === 'dark'
|
||||
const hasImage = !!image
|
||||
|
||||
const textContent = (
|
||||
<div className="flex flex-col flex-1 items-start gap-8 lg:gap-[41px]">
|
||||
<div className={`flex flex-col flex-1 items-start gap-8 lg:gap-[41px] ${!hasImage ? 'max-w-2xl' : ''}`}>
|
||||
<div className="flex flex-col items-start gap-4 w-full">
|
||||
<h2 className={`w-full font-joey-heavy text-fd-h1 ${isDark ? 'text-fd-yellow' : 'text-fd-navy'}`}>
|
||||
<h2
|
||||
className={`w-full font-joey-heavy text-3xl md:text-4xl lg:text-5xl leading-tight lg:leading-[57.6px] ${
|
||||
isDark ? 'text-fd-yellow' : 'text-fd-navy'
|
||||
}`}
|
||||
>
|
||||
{heading}
|
||||
</h2>
|
||||
<p className={`w-full font-joey text-fd-body-lg ${isDark ? 'text-white' : 'text-fd-navy'}`}>
|
||||
<p
|
||||
className={`w-full font-joey text-lg md:text-xl lg:text-2xl leading-relaxed lg:leading-[38px] ${
|
||||
isDark ? 'text-white' : 'text-fd-navy'
|
||||
}`}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{ctaText && (
|
||||
<a
|
||||
href={ctaLink || '#'}
|
||||
className={isDark ? 'fd-btn-primary' : 'fd-btn-secondary'}
|
||||
>
|
||||
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark}>
|
||||
{ctaText}
|
||||
</a>
|
||||
</FDButton>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
const imageContent = media?.url ? (
|
||||
<div className="relative w-full lg:w-[575px] h-[350px] lg:h-[479px] overflow-hidden rounded-[70px]">
|
||||
const imageContent = hasImage ? (
|
||||
<div className="w-full lg:w-[575px] lg:h-[479px] flex-shrink-0">
|
||||
<FDImage
|
||||
media={media}
|
||||
size="large"
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 1024px) 100vw, 575px"
|
||||
fallbackAlt={heading || ''}
|
||||
media={image as Media}
|
||||
fallbackAlt={heading}
|
||||
className="w-full h-full object-cover rounded-[70px]"
|
||||
/>
|
||||
{hasOverlay && (
|
||||
<div className={`absolute inset-0 ${overlayColor} ${overlayOpacity}`} aria-hidden="true" />
|
||||
)}
|
||||
</div>
|
||||
) : null
|
||||
|
||||
return (
|
||||
<section className={`w-full py-16 lg:py-[79px] ${sectionBg}`} style={sectionStyle}>
|
||||
<section className={`w-full py-16 lg:py-[79px] ${isDark ? 'bg-fd-navy' : 'bg-white'}`}>
|
||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
|
||||
{imagePosition === 'left' ? <>{imageContent}{textContent}</> : <>{textContent}{imageContent}</>}
|
||||
{imagePosition === 'left' ? (
|
||||
<>
|
||||
{imageContent}
|
||||
{textContent}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{textContent}
|
||||
{imageContent}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import type { FDFeatureAnnouncementBlock as FDFeatureAnnouncementBlockProps } from '@/payload-types'
|
||||
import { FDButton } from '@/components/FDButton'
|
||||
|
||||
export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncementBlockProps> = ({
|
||||
heading,
|
||||
@ -8,31 +9,31 @@ export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncement
|
||||
ctaLink = '#',
|
||||
theme = 'gray',
|
||||
}) => {
|
||||
const bgClass =
|
||||
theme === 'dark'
|
||||
? 'bg-fd-navy'
|
||||
: theme === 'gray'
|
||||
? 'bg-fd-gray'
|
||||
: 'bg-white'
|
||||
const isDark = theme === 'dark'
|
||||
|
||||
const headingColor = theme === 'dark' ? 'text-fd-yellow' : 'text-fd-navy'
|
||||
const bodyColor = theme === 'dark' ? 'text-white' : 'text-fd-navy'
|
||||
const bgClass = isDark ? 'bg-fd-navy' : theme === 'gray' ? 'bg-fd-gray' : 'bg-white'
|
||||
const headingColor = isDark ? 'text-fd-yellow' : 'text-fd-navy'
|
||||
const bodyColor = isDark ? 'text-white' : 'text-fd-navy'
|
||||
|
||||
return (
|
||||
<section className={`w-full py-20 md:py-28 lg:py-[173px] ${bgClass}`}>
|
||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8">
|
||||
<h2 className={`w-full max-w-[696px] font-joey-bold text-fd-h1 text-center ${headingColor}`}>
|
||||
<h2
|
||||
className={`w-full max-w-[696px] font-joey-bold text-3xl md:text-4xl lg:text-[54px] text-center leading-tight lg:leading-[64.8px] ${headingColor}`}
|
||||
>
|
||||
{heading}
|
||||
</h2>
|
||||
|
||||
<p className={`w-full max-w-[1112px] font-joey text-fd-h2 text-center ${bodyColor}`}>
|
||||
<p
|
||||
className={`w-full max-w-[1112px] font-joey text-xl md:text-2xl lg:text-4xl text-center leading-relaxed lg:leading-[44px] ${bodyColor}`}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
|
||||
{ctaText && (
|
||||
<a href={ctaLink || '#'} className="fd-btn-primary">
|
||||
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark}>
|
||||
{ctaText}
|
||||
</a>
|
||||
</FDButton>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import type { FDHeroBlock as FDHeroBlockProps, Media } from '@/payload-types'
|
||||
import { FDButton } from '@/components/FDButton'
|
||||
|
||||
export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
heading,
|
||||
@ -35,8 +36,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
const overlayClass =
|
||||
overlayOpacity === '30' ? 'bg-black/30' : overlayOpacity === '70' ? 'bg-black/70' : 'bg-black/50'
|
||||
|
||||
const secondaryBtnClass =
|
||||
isDark && textColor !== 'navy' ? 'fd-btn-secondary-dark' : 'fd-btn-secondary'
|
||||
const secondaryOnDark = textColor === 'navy' ? false : isDark
|
||||
|
||||
return (
|
||||
<section
|
||||
@ -44,39 +44,34 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
>
|
||||
{hasBgImage && (
|
||||
<>
|
||||
<img
|
||||
src={bgImageUrl}
|
||||
alt=""
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<img src={bgImageUrl} alt="" className="absolute inset-0 w-full h-full object-cover" aria-hidden="true" />
|
||||
<div className={`absolute inset-0 ${overlayClass}`} aria-hidden="true" />
|
||||
</>
|
||||
)}
|
||||
<div className="relative max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-start gap-6 md:gap-8">
|
||||
<h1 className={`w-full max-w-[884px] font-joey-heavy text-fd-display ${headingColor}`}>
|
||||
<h1 className={`w-full max-w-[884px] font-joey-heavy text-3xl md:text-5xl lg:text-[78px] leading-tight lg:leading-none ${headingColor}`}>
|
||||
{heading}
|
||||
</h1>
|
||||
{subheading && (
|
||||
<h2 className={`w-full max-w-[884px] font-joey-medium text-fd-h1 ${textBodyColor}`}>
|
||||
<h2 className={`w-full max-w-[884px] font-joey-medium text-2xl md:text-4xl lg:text-[50px] leading-tight ${textBodyColor}`}>
|
||||
{subheading}
|
||||
</h2>
|
||||
)}
|
||||
{body && (
|
||||
<p className={`w-full max-w-[597px] font-joey text-fd-body-lg ${textBodyColor}`}>
|
||||
<p className={`w-full max-w-[597px] font-joey text-lg md:text-xl lg:text-2xl lg:leading-snug ${textBodyColor}`}>
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4">
|
||||
{ctaText && (
|
||||
<a href={ctaLink || '#'} className="fd-btn-primary">
|
||||
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark}>
|
||||
{ctaText}
|
||||
</a>
|
||||
</FDButton>
|
||||
)}
|
||||
{secondaryCtaText && (
|
||||
<a href={secondaryCtaLink || '#'} className={secondaryBtnClass}>
|
||||
<FDButton href={secondaryCtaLink || '#'} variant="outline" onDark={secondaryOnDark}>
|
||||
{secondaryCtaText}
|
||||
</a>
|
||||
</FDButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
import React from 'react'
|
||||
import type { FDHeroBlock as FDHeroBlockProps, Media } from '@/payload-types'
|
||||
import { FDImage } from '@/components/FDImage'
|
||||
|
||||
export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
||||
heading,
|
||||
subheading,
|
||||
body,
|
||||
ctaText,
|
||||
ctaLink = '#',
|
||||
secondaryCtaText,
|
||||
secondaryCtaLink = '#',
|
||||
backgroundImage,
|
||||
overlayOpacity = '50',
|
||||
textColor = 'auto',
|
||||
theme = 'light',
|
||||
}) => {
|
||||
const media = backgroundImage as Media | undefined
|
||||
const hasBgImage = Boolean(media?.url)
|
||||
const isDark = hasBgImage || theme === 'dark'
|
||||
|
||||
let headingColor: string
|
||||
let textBodyColor: string
|
||||
if (textColor === 'white') {
|
||||
headingColor = 'text-white'
|
||||
textBodyColor = 'text-white'
|
||||
} else if (textColor === 'navy') {
|
||||
headingColor = 'text-fd-navy'
|
||||
textBodyColor = 'text-fd-navy'
|
||||
} else {
|
||||
headingColor = isDark ? 'text-fd-yellow' : 'text-fd-navy'
|
||||
textBodyColor = isDark ? 'text-white' : 'text-fd-navy'
|
||||
}
|
||||
|
||||
const overlayClass = overlayOpacity === '30' ? 'bg-black/30' : overlayOpacity === '70' ? 'bg-black/70' : 'bg-black/50'
|
||||
|
||||
return (
|
||||
<section className={`relative w-full py-16 md:py-20 lg:py-[99px] ${hasBgImage ? '' : isDark ? 'bg-fd-navy' : 'bg-white'} overflow-hidden`}>
|
||||
{hasBgImage && (
|
||||
<>
|
||||
<FDImage
|
||||
media={backgroundImage as Media}
|
||||
size="hero"
|
||||
fill
|
||||
priority
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
sizes="100vw"
|
||||
fallbackAlt=""
|
||||
/>
|
||||
<div className={`absolute inset-0 ${overlayClass}`} aria-hidden="true" />
|
||||
</>
|
||||
)}
|
||||
<div className="relative max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-start gap-6 md:gap-8">
|
||||
<h1 className={`w-full max-w-[884px] font-joey-heavy text-3xl md:text-5xl lg:text-[78px] leading-tight lg:leading-none ${headingColor}`}>{heading}</h1>
|
||||
{subheading && <h2 className={`w-full max-w-[884px] font-joey-medium text-2xl md:text-4xl lg:text-[50px] leading-tight ${textBodyColor}`}>{subheading}</h2>}
|
||||
{body && <p className={`w-full max-w-[597px] font-joey text-lg md:text-xl lg:text-2xl lg:leading-snug ${textBodyColor}`}>{body}</p>}
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4">
|
||||
{ctaText && <a href={ctaLink || '#'} className="inline-flex items-center justify-center px-8 py-2.5 bg-fd-yellow hover:bg-fd-yellow/90 rounded-full font-joey-bold text-fd-navy text-lg md:text-2xl leading-[38px] transition-colors">{ctaText}</a>}
|
||||
{secondaryCtaText && <a href={secondaryCtaLink || '#'} className={`inline-flex items-center justify-center px-8 py-2.5 rounded-full font-joey-bold text-lg md:text-2xl leading-[38px] border-2 transition-colors ${textColor === 'navy' ? 'border-fd-navy text-fd-navy hover:bg-fd-navy/5' : isDark ? 'border-white text-white hover:bg-white/10' : 'border-fd-navy text-fd-navy hover:bg-fd-navy/5'}`}>{secondaryCtaText}</a>}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -1,33 +1,37 @@
|
||||
import React from 'react'
|
||||
import type { FDPricingCardBlock as FDPricingCardBlockProps } from '@/payload-types'
|
||||
import { FDButton } from '@/components/FDButton'
|
||||
|
||||
const sectionBgMap: Record<string, string> = {
|
||||
white: 'bg-white',
|
||||
navy: 'bg-fd-navy',
|
||||
gray: 'bg-fd-gray-light',
|
||||
white: 'bg-white',
|
||||
navy: 'bg-fd-navy',
|
||||
gray: 'bg-fd-gray-light',
|
||||
yellow: 'bg-fd-yellow',
|
||||
}
|
||||
|
||||
const titleColorMap: Record<string, string> = {
|
||||
navy: 'text-fd-navy',
|
||||
white: 'text-white',
|
||||
navy: 'text-fd-navy',
|
||||
white: 'text-white',
|
||||
yellow: 'text-fd-yellow',
|
||||
}
|
||||
|
||||
const cardStyleMap: Record<string, { bg: string; border: string; title: string; subtitle: string; body: string; bullet: string }> = {
|
||||
outlined: { bg: 'bg-white', border: 'border-[6px] border-[#d1d5db]', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy' },
|
||||
navy: { bg: 'bg-fd-navy', border: '', title: 'text-fd-yellow', subtitle: 'text-white', body: 'text-white/80', bullet: 'text-white' },
|
||||
gray: { bg: 'bg-[#e5e5e5]', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy' },
|
||||
yellow: { bg: 'bg-fd-yellow', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy' },
|
||||
white: { bg: 'bg-white shadow-lg', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy' },
|
||||
const cardStyleMap: Record<string, {
|
||||
bg: string; border: string; title: string
|
||||
subtitle: string; body: string; bullet: string; isDark: boolean
|
||||
}> = {
|
||||
outlined: { bg: 'bg-white', border: 'border-[2px] border-[#d1d5db]', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy', isDark: false },
|
||||
navy: { bg: 'bg-fd-navy', border: '', title: 'text-fd-yellow', subtitle: 'text-white', body: 'text-white/80', bullet: 'text-white', isDark: true },
|
||||
gray: { bg: 'bg-[#e5e5e5]', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy', isDark: false },
|
||||
yellow: { bg: 'bg-fd-yellow', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy', isDark: false },
|
||||
white: { bg: 'bg-white shadow-lg', border: '', title: 'text-fd-navy', subtitle: 'text-fd-navy', body: 'text-fd-navy/80', bullet: 'text-fd-navy', isDark: false },
|
||||
}
|
||||
|
||||
// Map to fd-btn classes; the 'a' tag just uses the class directly
|
||||
const buttonStyleMap: Record<string, string> = {
|
||||
yellow: 'fd-btn-primary',
|
||||
navy: 'fd-btn-navy',
|
||||
outlinedNavy: 'fd-btn-secondary',
|
||||
outlinedWhite: 'fd-btn-secondary-dark',
|
||||
// Maps CMS buttonColor to FDButton variant — outline used where primary yellow wouldn't be visible
|
||||
const buttonVariantMap: Record<string, { variant: 'primary' | 'outline' }> = {
|
||||
yellow: { variant: 'primary' },
|
||||
navy: { variant: 'primary' },
|
||||
outlinedNavy: { variant: 'outline' },
|
||||
outlinedWhite: { variant: 'outline' },
|
||||
}
|
||||
|
||||
const gridColsMap: Record<number, string> = {
|
||||
@ -47,15 +51,18 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||
const sectionTitleColor = titleColorMap[titleColor || 'navy']
|
||||
const style = cardStyleMap[cardStyle || 'outlined']
|
||||
const btnClass = buttonStyleMap[buttonColor || 'yellow'] || 'fd-btn-primary'
|
||||
const { variant } = buttonVariantMap[buttonColor || 'yellow']
|
||||
const cardCount = cards?.length || 1
|
||||
const gridCols = gridColsMap[cardCount] || gridColsMap[3]
|
||||
|
||||
// outline-dark vs outline-light is determined by the card background
|
||||
const outlineOnDark = style.isDark
|
||||
|
||||
return (
|
||||
<section className={`w-full py-12 md:py-16 lg:py-20 ${sectionBg}`}>
|
||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||
{sectionTitle && (
|
||||
<h2 className={`font-joey-heavy text-fd-h1 text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
|
||||
<h2 className={`font-joey-heavy text-3xl md:text-4xl lg:text-[54px] leading-tight text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
|
||||
{sectionTitle}
|
||||
</h2>
|
||||
)}
|
||||
@ -63,28 +70,41 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
|
||||
{cards?.map((card, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${style.bg} ${style.border} rounded-[32px] md:rounded-[50px] lg:rounded-[70px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-5`}
|
||||
className={`${style.bg} ${style.border} rounded-[32px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-5`}
|
||||
>
|
||||
<h3 className={`font-joey-heavy text-fd-h2 ${style.title}`}>{card.title}</h3>
|
||||
<h3 className={`font-joey-heavy text-2xl md:text-3xl lg:text-4xl leading-tight ${style.title}`}>
|
||||
{card.title}
|
||||
</h3>
|
||||
{card.subtitle && (
|
||||
<p className={`font-joey-bold text-fd-h3 ${style.subtitle}`}>{card.subtitle}</p>
|
||||
<p className={`font-joey-bold text-xl md:text-2xl leading-tight ${style.subtitle}`}>
|
||||
{card.subtitle}
|
||||
</p>
|
||||
)}
|
||||
{card.description && (
|
||||
<p className={`font-joey text-fd-body ${style.body}`}>{card.description}</p>
|
||||
<p className={`font-joey text-base md:text-lg leading-relaxed ${style.body}`}>
|
||||
{card.description}
|
||||
</p>
|
||||
)}
|
||||
{card.bulletPoints && card.bulletPoints.length > 0 && (
|
||||
<ul className={`flex flex-col gap-1 ${style.bullet}`}>
|
||||
<ul className={`flex flex-col gap-2 ${style.bullet}`}>
|
||||
{card.bulletPoints.map((point, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<span className="mt-2 w-2 h-2 rounded-full bg-current flex-shrink-0" />
|
||||
<span className="font-joey text-fd-body">{point.text}</span>
|
||||
<span className="font-joey text-base md:text-lg">{point.text}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{card.ctaText && (
|
||||
<div className="mt-auto pt-4">
|
||||
<a href={card.ctaLink || '#'} className={btnClass}>{card.ctaText}</a>
|
||||
<FDButton
|
||||
href={card.ctaLink || '#'}
|
||||
variant={variant}
|
||||
onDark={variant === 'outline' ? outlineOnDark : style.isDark}
|
||||
className="text-lg md:text-xl"
|
||||
>
|
||||
{card.ctaText}
|
||||
</FDButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,19 +1,12 @@
|
||||
import React from 'react'
|
||||
import type { FDWideCardBlock as FDWideCardBlockProps, Media } from '@/payload-types'
|
||||
import { FDImage } from '@/components/FDImage'
|
||||
import { FDButton } from '@/components/FDButton'
|
||||
|
||||
const cardBgMap: Record<string, { bg: string; heading: string; body: string }> = {
|
||||
navy: { bg: 'bg-fd-navy', heading: 'text-white', body: 'text-white/80' },
|
||||
yellow: { bg: 'bg-fd-yellow', heading: 'text-fd-navy', body: 'text-fd-navy/80' },
|
||||
gray: { bg: 'bg-[#e5e5e5]', heading: 'text-fd-navy', body: 'text-fd-navy/80' },
|
||||
white: { bg: 'bg-white shadow-xl', heading: 'text-fd-navy', body: 'text-fd-navy/80' },
|
||||
}
|
||||
|
||||
// Map to fd-btn classes
|
||||
const btnMap: Record<string, string> = {
|
||||
yellow: 'fd-btn-primary',
|
||||
navy: 'fd-btn-navy',
|
||||
white: 'fd-btn-white',
|
||||
const cardBgMap: Record<string, { bg: string; heading: string; body: string; isDark: boolean }> = {
|
||||
navy: { bg: 'bg-fd-navy', heading: 'text-white', body: 'text-white/80', isDark: true },
|
||||
yellow: { bg: 'bg-fd-yellow', heading: 'text-fd-navy', body: 'text-fd-navy/80', isDark: false },
|
||||
gray: { bg: 'bg-[#e5e5e5]', heading: 'text-fd-navy', body: 'text-fd-navy/80', isDark: false },
|
||||
white: { bg: 'bg-white shadow-xl', heading: 'text-fd-navy', body: 'text-fd-navy/80', isDark: false },
|
||||
}
|
||||
|
||||
const sectionBgMap: Record<string, string> = {
|
||||
@ -22,6 +15,13 @@ const sectionBgMap: Record<string, string> = {
|
||||
navy: 'bg-fd-navy',
|
||||
}
|
||||
|
||||
// Button variant per CMS color choice — outline used for navy cards with navy button (would be invisible)
|
||||
const btnVariantMap: Record<string, { variant: 'primary' | 'outline' }> = {
|
||||
yellow: { variant: 'primary' },
|
||||
navy: { variant: 'outline' },
|
||||
white: { variant: 'primary' },
|
||||
}
|
||||
|
||||
export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
||||
heading,
|
||||
body,
|
||||
@ -33,36 +33,41 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
||||
sectionBackground = 'white',
|
||||
}) => {
|
||||
const card = cardBgMap[cardBackground || 'navy']
|
||||
const btnClass = btnMap[buttonColor || 'yellow'] || 'fd-btn-primary'
|
||||
const sectionBg = sectionBgMap[sectionBackground || 'white']
|
||||
const { variant } = btnVariantMap[buttonColor || 'yellow']
|
||||
const media = image as Media | undefined
|
||||
const hasImage = Boolean(media?.url)
|
||||
const imageUrl = media?.url || ''
|
||||
const hasImage = Boolean(imageUrl)
|
||||
|
||||
return (
|
||||
<section className={`w-full py-12 md:py-16 lg:py-20 ${sectionBg}`}>
|
||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
|
||||
<div className={`${card.bg} rounded-[70px] overflow-hidden flex flex-col lg:flex-row`}>
|
||||
<div className="flex-1 flex flex-col justify-center gap-5 md:gap-6 px-10 md:px-14 lg:px-16 py-12 md:py-16">
|
||||
<h2 className={`font-joey-heavy text-fd-h2 ${card.heading}`}>{heading}</h2>
|
||||
<h2 className={`font-joey-heavy text-2xl md:text-3xl lg:text-[42px] leading-tight ${card.heading}`}>
|
||||
{heading}
|
||||
</h2>
|
||||
{body && (
|
||||
<p className={`font-joey text-fd-body ${card.body}`}>{body}</p>
|
||||
<p className={`font-joey text-base md:text-lg lg:text-xl leading-relaxed ${card.body}`}>
|
||||
{body}
|
||||
</p>
|
||||
)}
|
||||
{ctaText && (
|
||||
<div>
|
||||
<a href={ctaLink || '#'} className={btnClass}>{ctaText}</a>
|
||||
<FDButton
|
||||
href={ctaLink || '#'}
|
||||
variant={variant}
|
||||
onDark={card.isDark}
|
||||
className="text-lg md:text-xl"
|
||||
>
|
||||
{ctaText}
|
||||
</FDButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{hasImage && (
|
||||
<div className="relative flex-1 min-h-[250px] lg:min-h-0">
|
||||
<FDImage
|
||||
media={media!}
|
||||
size="large"
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 1024px) 100vw, 600px"
|
||||
fallbackAlt={heading || ''}
|
||||
/>
|
||||
<div className="flex-1 min-h-[250px] lg:min-h-0">
|
||||
<img src={imageUrl} alt={media?.alt || ''} className="w-full h-full object-cover" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
43
src/components/FDButton.tsx
Normal file
43
src/components/FDButton.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
|
||||
type FDButtonProps = {
|
||||
href: string
|
||||
children: React.ReactNode
|
||||
variant?: 'primary' | 'outline'
|
||||
onDark?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* FDButton — shared button component for all FD blocks.
|
||||
*
|
||||
* variant="primary" onDark={true} → yellow bg, hover white (use on navy backgrounds)
|
||||
* variant="primary" onDark={false} → yellow bg, hover yellow/80 (use on light backgrounds)
|
||||
* variant="outline" onDark={true} → white border + text, hover white/10
|
||||
* variant="outline" onDark={false} → navy border + text, hover navy/5
|
||||
*/
|
||||
export const FDButton: React.FC<FDButtonProps> = ({
|
||||
href,
|
||||
children,
|
||||
variant = 'primary',
|
||||
onDark = false,
|
||||
className = '',
|
||||
}) => {
|
||||
const base =
|
||||
'inline-flex items-center justify-center px-8 py-2.5 rounded-full font-joey-bold text-lg md:text-2xl leading-[38px] transition-colors'
|
||||
|
||||
const styles = {
|
||||
'primary-dark': 'bg-fd-yellow text-fd-navy hover:bg-white hover:text-fd-navy',
|
||||
'primary-light': 'bg-fd-yellow text-fd-navy hover:bg-fd-yellow/80',
|
||||
'outline-dark': 'border-2 border-white text-white hover:bg-white/10',
|
||||
'outline-light': 'border-2 border-fd-navy text-fd-navy hover:bg-fd-navy/5',
|
||||
}
|
||||
|
||||
const key = `${variant}-${onDark ? 'dark' : 'light'}` as keyof typeof styles
|
||||
|
||||
return (
|
||||
<a href={href} className={`${base} ${styles[key]} ${className}`}>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user