fix: mobile audit — gray token, FDImage props, border radius, padding

This commit is contained in:
Jeffrey 2026-02-20 10:11:02 +01:00
parent 5b64cbf24c
commit 097615569b
11 changed files with 119 additions and 113 deletions

View File

@ -340,6 +340,7 @@ html:not([data-theme]) {
--color-fd-gray-warm: #F8F8F6;
--color-fd-gray-cool: #F5F7F9;
--color-fd-gray-200: #C8CCD0;
--color-fd-gray-light: #F0F0F0;
--color-fd-gray-300: #A9AEB5;
--color-fd-gray-400: #8A919A;
--color-fd-gray-500: #6B737E;

View File

@ -1,5 +1,7 @@
import React from 'react'
import type { FDAlternateHeroBlock as Props, Media } from '@/payload-types'
import { FDImage } from '@/components/FDImage'
import { FDButton } from '@/components/FDButton'
export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
heading,
@ -13,8 +15,9 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
sectionBackground = 'white',
}) => {
const media = image as Media | undefined
const hasImage = media && typeof media === 'object' && media.url
const isDark = sectionBackground === 'navy'
const bgClass = isDark ? 'bg-fd-navy' : sectionBackground === 'gray' ? 'bg-fd-surface-alt' : 'bg-white'
const bgClass = isDark ? 'bg-fd-navy' : sectionBackground === 'gray' ? 'bg-fd-gray-light' : 'bg-white'
const titleClass = isDark ? 'text-fd-yellow' : 'text-fd-navy'
const bodyClass = isDark ? 'text-white' : 'text-fd-navy'
@ -33,31 +36,33 @@ export const FDAlternateHeroBlockComponent: React.FC<Props> = ({
{(primaryCtaText || secondaryCtaText) && (
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mt-2">
{primaryCtaText && (
<a href={primaryCtaLink || '#'} className="fd-btn-primary">
<FDButton href={primaryCtaLink || '#'} variant="primary" onDark={isDark}>
{primaryCtaText}
</a>
</FDButton>
)}
{secondaryCtaText && (
<a
href={secondaryCtaLink || '#'}
className={isDark ? 'fd-btn-secondary-dark' : 'fd-btn-secondary'}
>
<FDButton href={secondaryCtaLink || '#'} variant="outline" onDark={isDark}>
{secondaryCtaText}
</a>
</FDButton>
)}
</div>
)}
</div>
{/* Full-width image — no container, no border radius */}
{media?.url && (
{/* FIX: Full-width image using FDImage with fill, replacing raw <img> */}
{hasImage && (
<div className="w-full">
<img
src={media.url}
alt={(media as any).alt || heading}
className="w-full object-cover block"
style={{ maxHeight: '620px', objectPosition: 'center top' }}
/>
<div className="relative w-full" style={{ maxHeight: '620px', height: '45vw', minHeight: '220px' }}>
<FDImage
media={media}
size="hero"
fill
priority
className="object-cover object-center"
sizes="100vw"
fallbackAlt={heading || ''}
/>
</div>
{imageCaption && (
<div className={`text-center py-3 font-joey text-fd-small opacity-60 ${bodyClass} ${bgClass}`}>
{imageCaption}

View File

@ -13,7 +13,7 @@ const cardStyleMap: Record<
border: '',
},
gray: {
bg: 'bg-[#e5e5e5] dark:bg-white/10',
bg: 'bg-fd-gray-light dark:bg-white/10',
headingText: 'text-fd-navy dark:text-fd-yellow',
bodyText: 'text-fd-navy dark:text-white',
linkText: 'text-fd-navy hover:text-fd-navy/70 dark:text-fd-yellow dark:hover:text-fd-yellow/80',
@ -73,7 +73,7 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
const gridCols = layoutGridMap[layout] || layoutGridMap['1-1-1']
return (
<section className={`w-full py-12 md:py-16 lg:py-20 ${sectionBg}`}>
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
<div className={`grid grid-cols-1 md:grid-cols-2 ${gridCols} gap-4 md:gap-6`}>
{cards?.map((card, index) => {
@ -84,7 +84,7 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
if (mode === 'centeredHeading') {
cardContent = (
<div
className={`${style.bg} ${style.border} rounded-[70px] px-10 md:px-14 py-14 md:py-20 flex items-center justify-center min-h-[280px] md:min-h-[360px]`}
className={`${style.bg} ${style.border} rounded-[70px] px-8 md:px-14 py-14 md:py-20 flex items-center justify-center min-h-[280px] md:min-h-[360px]`}
>
<span
className={`font-joey-heavy text-fd-display leading-tight text-center ${style.headingText}`}
@ -96,7 +96,7 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
} else if (mode === 'centeredBody') {
cardContent = (
<div
className={`${style.bg} ${style.border} rounded-[70px] px-10 md:px-14 py-14 md:py-20 flex items-center justify-center min-h-[280px] md:min-h-[360px]`}
className={`${style.bg} ${style.border} rounded-[70px] px-8 md:px-14 py-14 md:py-20 flex items-center justify-center min-h-[280px] md:min-h-[360px]`}
>
<p
className={`font-joey text-fd-body-lg text-center ${style.bodyText}`}
@ -106,9 +106,10 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
</div>
)
} else {
// FIX: px-6 on mobile → px-10 on md+ to prevent 40px padding squashing 375px cards
cardContent = (
<div
className={`${style.bg} ${style.border} rounded-[70px] px-10 md:px-14 py-10 md:py-14 flex flex-col gap-1 h-full`}
className={`${style.bg} ${style.border} rounded-[70px] px-6 md:px-10 lg:px-14 py-8 md:py-12 lg:py-14 flex flex-col gap-1 h-full`}
>
{card.heading && (
<h3

View File

@ -9,22 +9,25 @@ export const FDContactBlockComponent: React.FC<FDContactBlockProps> = ({
return (
<section className="relative w-full bg-fd-navy py-16 md:py-20 lg:pt-[100px] lg:pb-[120px]">
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col items-center gap-8 lg:gap-10">
<h2 className="w-full font-joey-heavy text-fd-navy text-fd-h1 text-fd-yellow text-center">
{/* FIX: removed conflicting text-fd-navy class, kept text-fd-yellow */}
<h2 className="w-full font-joey-heavy text-fd-h1 text-fd-yellow text-center">
{heading}
</h2>
<div className="flex flex-row items-stretch gap-4 md:gap-7 w-full max-w-[656px]">
{/* FIX: flex-col on mobile → sm:flex-row so cards don't squash at 375px */}
<div className="flex flex-col sm:flex-row items-stretch gap-4 md:gap-7 w-full max-w-[656px]">
{contactMethods?.map((method, index) => {
const media = method.icon as Media
const card = (
<div className="flex-1 flex flex-col items-center gap-3 md:gap-5 cursor-pointer transition-transform hover:scale-105">
{media?.url && (
<div className="relative w-full h-[120px] md:h-[160px] lg:h-[200px] overflow-hidden rounded-[70px]">
<div className="relative w-full h-[140px] sm:h-[120px] md:h-[160px] lg:h-[200px] overflow-hidden rounded-[70px]">
<FDImage
media={media}
size="medium"
fill
className="object-cover"
sizes="(max-width: 768px) 50vw, 300px"
sizes="(max-width: 640px) 90vw, (max-width: 768px) 50vw, 300px"
fallbackAlt={method.label}
/>
</div>

View File

@ -20,14 +20,14 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
<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-3xl md:text-4xl lg:text-5xl leading-tight lg:leading-[57.6px] ${
className={`w-full font-joey-heavy text-fd-h1 leading-tight ${
isDark ? 'text-fd-yellow' : 'text-fd-navy'
}`}
>
{heading}
</h2>
<p
className={`w-full font-joey text-lg md:text-xl lg:text-2xl leading-relaxed lg:leading-[38px] ${
className={`w-full font-joey text-fd-body-lg leading-relaxed ${
isDark ? 'text-white' : 'text-fd-navy'
}`}
>
@ -47,14 +47,17 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
<div className="w-full lg:w-[575px] lg:h-[479px] flex-shrink-0">
<FDImage
media={image as Media}
size="large"
fallbackAlt={heading}
className="w-full h-full object-cover rounded-[70px]"
sizes="(max-width: 1024px) 100vw, 575px"
/>
</div>
) : null
return (
<section className={`w-full py-16 lg:py-[79px] ${isDark ? 'bg-fd-navy' : 'bg-white'}`}>
// FIX: added md:py-20 so tablet gets correct spacing
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${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' ? (
<>

View File

@ -21,12 +21,11 @@ const cardStyleMap: Record<string, {
}> = {
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 },
gray: { bg: 'bg-fd-gray-light', 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 },
}
// 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' },
@ -54,34 +53,33 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
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}`}>
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
{sectionTitle && (
<h2 className={`font-joey-heavy text-3xl md:text-4xl lg:text-[54px] leading-tight text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
<h2 className={`font-joey-heavy text-fd-h1 text-center mb-10 md:mb-14 ${sectionTitleColor}`}>
{sectionTitle}
</h2>
)}
{/* FIX: rounded-[32px] → rounded-[70px] per design system */}
<div className={`grid grid-cols-1 ${gridCols} gap-6 md:gap-8`}>
{cards?.map((card, index) => (
<div
key={index}
className={`${style.bg} ${style.border} rounded-[32px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-5`}
className={`${style.bg} ${style.border} rounded-[70px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-5`}
>
<h3 className={`font-joey-heavy text-2xl md:text-3xl lg:text-4xl leading-tight ${style.title}`}>
<h3 className={`font-joey-heavy text-fd-h2 leading-tight ${style.title}`}>
{card.title}
</h3>
{card.subtitle && (
<p className={`font-joey-bold text-xl md:text-2xl leading-tight ${style.subtitle}`}>
<p className={`font-joey-bold text-fd-h3 leading-tight ${style.subtitle}`}>
{card.subtitle}
</p>
)}
{card.description && (
<p className={`font-joey text-base md:text-lg leading-relaxed ${style.body}`}>
<p className={`font-joey text-fd-body-lg leading-relaxed ${style.body}`}>
{card.description}
</p>
)}
@ -90,7 +88,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
{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-base md:text-lg">{point.text}</span>
<span className="font-joey text-fd-body-lg">{point.text}</span>
</li>
))}
</ul>
@ -101,7 +99,6 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
href={card.ctaLink || '#'}
variant={variant}
onDark={variant === 'outline' ? outlineOnDark : style.isDark}
className="text-lg md:text-xl"
>
{card.ctaText}
</FDButton>

View File

@ -14,7 +14,8 @@ export const FDServicesGridBlockComponent: React.FC<FDServicesGridBlockProps> =
columns = '4',
}) => {
return (
<section className="relative w-full bg-white py-16 md:py-20 lg:py-24">
// FIX: lg:py-24 → lg:py-[99px] (standard section padding)
<section className="relative w-full bg-white py-16 md:py-20 lg:py-[99px]">
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
<h2 className="font-joey-heavy text-fd-h1 text-fd-navy mb-8 lg:mb-12">
{heading}

View File

@ -1,18 +1,17 @@
// @ts-nocheck
import React from 'react'
import type { FDTeamBlock as FDTeamBlockProps, Media } from '@/payload-types'
import { FDImage } from '@/components/FDImage'
const sectionBgMap: Record<string, string> = {
white: 'bg-white',
gray: 'bg-[#e5e5e5]',
gray: 'bg-fd-gray-light',
navy: 'bg-fd-navy',
}
const cardMap: Record<string, { bg: string; name: string; role: string; bio: string; icon: string }> = {
navy: { bg: 'bg-fd-navy', name: 'text-fd-yellow', role: 'text-white/70', bio: 'text-white/60', icon: 'text-white/40 hover:text-fd-yellow' },
white: { bg: 'bg-white', name: 'text-fd-navy', role: 'text-fd-navy/60', bio: 'text-fd-navy/60', icon: 'text-fd-navy/40 hover:text-fd-navy' },
gray: { bg: 'bg-[#e5e5e5]', name: 'text-fd-navy', role: 'text-fd-navy/60', bio: 'text-fd-navy/60', icon: 'text-fd-navy/40 hover:text-fd-navy' },
gray: { bg: 'bg-fd-gray-light', name: 'text-fd-navy', role: 'text-fd-navy/60', bio: 'text-fd-navy/60', icon: 'text-fd-navy/40 hover:text-fd-navy' },
}
const colsMap: Record<string, string> = {
@ -29,9 +28,9 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
cardStyle = 'navy',
sectionBackground = 'white',
}) => {
const sectionBg = sectionBgMap[sectionBackground] || sectionBgMap.white
const card = cardMap[cardStyle] || cardMap.navy
const gridCols = colsMap[columns] || colsMap['3']
const sectionBg = sectionBgMap[sectionBackground ?? 'white'] || sectionBgMap.white
const card = cardMap[cardStyle ?? 'navy'] || cardMap.navy
const gridCols = colsMap[columns ?? '3'] || colsMap['3']
const isNavySection = sectionBackground === 'navy'
return (
@ -42,12 +41,12 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{(heading || subheading) && (
<div className="flex flex-col gap-3 mb-10 md:mb-14">
{heading && (
<h2 className={`font-joey-heavy text-3xl md:text-4xl lg:text-5xl leading-tight ${isNavySection ? 'text-fd-yellow' : 'text-fd-navy'}`}>
<h2 className={`font-joey-heavy text-fd-h1 ${isNavySection ? 'text-fd-yellow' : 'text-fd-navy'}`}>
{heading}
</h2>
)}
{subheading && (
<p className={`font-joey text-lg md:text-xl ${isNavySection ? 'text-white/70' : 'text-fd-navy/60'}`}>
<p className={`font-joey text-fd-body-lg ${isNavySection ? 'text-white/70' : 'text-fd-navy/60'}`}>
{subheading}
</p>
)}
@ -57,22 +56,24 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{/* Grid */}
<div className={`grid grid-cols-1 ${gridCols} gap-6`}>
{members?.map((member, i) => {
// FIX: cast photo properly as Media and pass full object to FDImage
const photo = member.photo as Media | undefined
const hasPhoto = photo && typeof photo === 'object' && photo.url
return (
<div key={i} className={`${card.bg} rounded-[70px] overflow-hidden flex flex-col`}>
{/* Photo */}
<div className="aspect-[4/3] w-full overflow-hidden">
{photo?.url ? (
{hasPhoto ? (
<FDImage
src={photo.url}
alt={photo.alt || member.name}
width={600}
height={450}
media={photo}
size="medium"
className="w-full h-full object-cover object-top"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
fallbackAlt={member.name}
/>
) : (
// Placeholder when no photo
<div className={`w-full h-full flex items-center justify-center ${cardStyle === 'navy' ? 'bg-fd-navy/50' : 'bg-fd-navy/10'}`}>
<svg viewBox="0 0 80 80" className="w-20 h-20 opacity-30" fill="currentColor">
<circle cx="40" cy="30" r="18" />
@ -84,11 +85,11 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{/* Info */}
<div className="flex flex-col gap-2 px-8 py-8 flex-1">
<p className={`font-joey-bold text-xl ${card.name}`}>{member.name}</p>
<p className={`font-joey text-sm ${card.role}`}>{member.role}</p>
<p className={`font-joey-bold text-fd-h3 ${card.name}`}>{member.name}</p>
<p className={`font-joey text-fd-small ${card.role}`}>{member.role}</p>
{member.bio && (
<p className={`font-joey text-sm leading-relaxed mt-2 ${card.bio}`}>
<p className={`font-joey text-fd-small leading-relaxed mt-2 ${card.bio}`}>
{member.bio}
</p>
)}
@ -102,7 +103,6 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
aria-label={`E-post till ${member.name}`}
className={`transition-colors ${card.icon}`}
>
{/* Email icon */}
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
@ -116,7 +116,6 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
aria-label={`LinkedIn för ${member.name}`}
className={`transition-colors ${card.icon}`}
>
{/* LinkedIn icon */}
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
</svg>

View File

@ -1,12 +1,30 @@
// @ts-nocheck
import React from 'react'
import type { FDTestimonialBlock as FDTestimonialBlockProps, Media } from '@/payload-types'
import { FDImage } from '@/components/FDImage'
const bgMap: Record<string, { section: string; card: string; quote: string; meta: string; name: string; company: string }> = {
gray: { section: 'bg-[#e5e5e5]', card: 'bg-white', quote: 'text-fd-navy', meta: 'text-fd-navy/60', name: 'text-fd-navy', company: 'text-fd-navy/50' },
white: { section: 'bg-white', card: 'bg-[#e5e5e5]', quote: 'text-fd-navy', meta: 'text-fd-navy/60', name: 'text-fd-navy', company: 'text-fd-navy/50' },
navy: { section: 'bg-fd-navy', card: 'bg-white/10', quote: 'text-white', meta: 'text-white/60', name: 'text-white', company: 'text-white/50' },
gray: { section: 'bg-fd-gray-light', card: 'bg-white', quote: 'text-fd-navy', meta: 'text-fd-navy/60', name: 'text-fd-navy', company: 'text-fd-navy/50' },
white: { section: 'bg-white', card: 'bg-fd-gray-light', quote: 'text-fd-navy', meta: 'text-fd-navy/60', name: 'text-fd-navy', company: 'text-fd-navy/50' },
navy: { section: 'bg-fd-navy', card: 'bg-white/10', quote: 'text-white', meta: 'text-white/60', name: 'text-white', company: 'text-white/50' },
}
/** Avatar helper — uses FDImage with media object correctly */
const Avatar: React.FC<{ media: Media | undefined; name: string; size: number }> = ({ media, name, size }) => {
if (!media?.url) return null
return (
<div
className="rounded-full overflow-hidden flex-shrink-0"
style={{ width: size, height: size }}
>
<FDImage
media={media}
size="thumbnail"
className="w-full h-full object-cover"
sizes={`${size}px`}
fallbackAlt={name}
/>
</div>
)
}
export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
@ -15,15 +33,16 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
layout = 'grid',
sectionBackground = 'gray',
}) => {
const theme = bgMap[sectionBackground] || bgMap.gray
const theme = bgMap[sectionBackground ?? 'gray'] || bgMap.gray
const isFeatured = layout === 'featured'
const isNavy = sectionBackground === 'navy'
return (
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${theme.section}`}>
<div className="max-w-[1200px] mx-auto px-6 md:px-8">
{heading && (
<h2 className={`font-joey-heavy text-3xl md:text-4xl lg:text-5xl leading-tight mb-10 md:mb-14 ${sectionBackground === 'navy' ? 'text-fd-yellow' : 'text-fd-navy'}`}>
<h2 className={`font-joey-heavy text-fd-h1 mb-10 md:mb-14 ${isNavy ? 'text-fd-yellow' : 'text-fd-navy'}`}>
{heading}
</h2>
)}
@ -36,22 +55,12 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
const t = testimonials[0]
const avatar = t.avatar as Media | undefined
return (
<div className={`${theme.card} rounded-[70px] px-10 md:px-16 py-12 md:py-16 flex flex-col gap-8`}>
<div className={`${theme.card} rounded-[70px] px-8 md:px-16 py-10 md:py-16 flex flex-col gap-8`}>
<p className={`font-joey-medium text-xl md:text-2xl lg:text-3xl leading-relaxed ${theme.quote}`}>
&ldquo;{t.quote}&rdquo;
</p>
<div className="flex items-center gap-4">
{avatar?.url && (
<div className="w-14 h-14 rounded-full overflow-hidden flex-shrink-0">
<FDImage
src={avatar.url}
alt={avatar.alt || t.authorName}
width={56}
height={56}
className="w-full h-full object-cover"
/>
</div>
)}
<Avatar media={avatar} name={t.authorName} size={56} />
<div>
<p className={`font-joey-bold text-lg ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-sm ${theme.meta}`}>
@ -74,17 +83,7 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
&ldquo;{t.quote}&rdquo;
</p>
<div className="flex items-center gap-3">
{avatar?.url && (
<div className="w-10 h-10 rounded-full overflow-hidden flex-shrink-0">
<FDImage
src={avatar.url}
alt={avatar.alt || t.authorName}
width={40}
height={40}
className="w-full h-full object-cover"
/>
</div>
)}
<Avatar media={avatar} name={t.authorName} size={40} />
<div>
<p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-xs ${theme.meta}`}>
@ -106,24 +105,14 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
return (
<div key={i} className={`${theme.card} rounded-[70px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-6`}>
{/* Quote mark */}
<span className={`font-joey-heavy text-5xl leading-none ${sectionBackground === 'navy' ? 'text-fd-yellow' : 'text-fd-navy'} opacity-30`}>
<span className={`font-joey-heavy text-5xl leading-none ${isNavy ? 'text-fd-yellow' : 'text-fd-navy'} opacity-30`}>
&ldquo;
</span>
<p className={`font-joey-medium text-lg leading-relaxed -mt-4 ${theme.quote}`}>
{t.quote}
</p>
<div className="flex items-center gap-3 mt-auto">
{avatar?.url && (
<div className="w-10 h-10 rounded-full overflow-hidden flex-shrink-0">
<FDImage
src={avatar.url}
alt={avatar.alt || t.authorName}
width={40}
height={40}
className="w-full h-full object-cover"
/>
</div>
)}
<Avatar media={avatar} name={t.authorName} size={40} />
<div>
<p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-sm ${theme.meta}`}>

View File

@ -1,11 +1,12 @@
import React from 'react'
import type { FDWideCardBlock as FDWideCardBlockProps, Media } from '@/payload-types'
import { FDButton } from '@/components/FDButton'
import { FDImage } from '@/components/FDImage'
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 },
gray: { bg: 'bg-fd-gray-light', 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 },
}
@ -15,7 +16,6 @@ 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' },
@ -36,19 +36,18 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
const sectionBg = sectionBgMap[sectionBackground || 'white']
const { variant } = btnVariantMap[buttonColor || 'yellow']
const media = image as Media | undefined
const imageUrl = media?.url || ''
const hasImage = Boolean(imageUrl)
const hasImage = media && typeof media === 'object' && media.url
return (
<section className={`w-full py-12 md:py-16 lg:py-20 ${sectionBg}`}>
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${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-2xl md:text-3xl lg:text-[42px] leading-tight ${card.heading}`}>
<div className="flex-1 flex flex-col justify-center gap-5 md:gap-6 px-8 md:px-14 lg:px-16 py-12 md:py-16">
<h2 className={`font-joey-heavy text-fd-h1 leading-tight ${card.heading}`}>
{heading}
</h2>
{body && (
<p className={`font-joey text-base md:text-lg lg:text-xl leading-relaxed ${card.body}`}>
<p className={`font-joey text-fd-body-lg leading-relaxed ${card.body}`}>
{body}
</p>
)}
@ -58,16 +57,24 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
href={ctaLink || '#'}
variant={variant}
onDark={card.isDark}
className="text-lg md:text-xl"
>
{ctaText}
</FDButton>
</div>
)}
</div>
{/* FIX: replaced raw <img> with FDImage */}
{hasImage && (
<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 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, 50vw"
fallbackAlt={heading || ''}
/>
</div>
)}
</div>

File diff suppressed because one or more lines are too long