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-warm: #F8F8F6;
--color-fd-gray-cool: #F5F7F9; --color-fd-gray-cool: #F5F7F9;
--color-fd-gray-200: #C8CCD0; --color-fd-gray-200: #C8CCD0;
--color-fd-gray-light: #F0F0F0;
--color-fd-gray-300: #A9AEB5; --color-fd-gray-300: #A9AEB5;
--color-fd-gray-400: #8A919A; --color-fd-gray-400: #8A919A;
--color-fd-gray-500: #6B737E; --color-fd-gray-500: #6B737E;

View File

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

View File

@ -13,7 +13,7 @@ const cardStyleMap: Record<
border: '', border: '',
}, },
gray: { 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', headingText: 'text-fd-navy dark:text-fd-yellow',
bodyText: 'text-fd-navy dark:text-white', 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', 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'] const gridCols = layoutGridMap[layout] || layoutGridMap['1-1-1']
return ( 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="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`}> <div className={`grid grid-cols-1 md:grid-cols-2 ${gridCols} gap-4 md:gap-6`}>
{cards?.map((card, index) => { {cards?.map((card, index) => {
@ -84,7 +84,7 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
if (mode === 'centeredHeading') { if (mode === 'centeredHeading') {
cardContent = ( cardContent = (
<div <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 <span
className={`font-joey-heavy text-fd-display leading-tight text-center ${style.headingText}`} 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') { } else if (mode === 'centeredBody') {
cardContent = ( cardContent = (
<div <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 <p
className={`font-joey text-fd-body-lg text-center ${style.bodyText}`} className={`font-joey text-fd-body-lg text-center ${style.bodyText}`}
@ -106,9 +106,10 @@ export const FDCardGridBlockComponent: React.FC<FDCardGridBlockProps> = ({
</div> </div>
) )
} else { } else {
// FIX: px-6 on mobile → px-10 on md+ to prevent 40px padding squashing 375px cards
cardContent = ( cardContent = (
<div <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 && ( {card.heading && (
<h3 <h3

View File

@ -9,22 +9,25 @@ export const FDContactBlockComponent: React.FC<FDContactBlockProps> = ({
return ( return (
<section className="relative w-full bg-fd-navy py-16 md:py-20 lg:pt-[100px] lg:pb-[120px]"> <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"> <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} {heading}
</h2> </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) => { {contactMethods?.map((method, index) => {
const media = method.icon as Media const media = method.icon as Media
const card = ( const card = (
<div className="flex-1 flex flex-col items-center gap-3 md:gap-5 cursor-pointer transition-transform hover:scale-105"> <div className="flex-1 flex flex-col items-center gap-3 md:gap-5 cursor-pointer transition-transform hover:scale-105">
{media?.url && ( {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 <FDImage
media={media} media={media}
size="medium" size="medium"
fill fill
className="object-cover" className="object-cover"
sizes="(max-width: 768px) 50vw, 300px" sizes="(max-width: 640px) 90vw, (max-width: 768px) 50vw, 300px"
fallbackAlt={method.label} fallbackAlt={method.label}
/> />
</div> </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 flex-1 items-start gap-8 lg:gap-[41px] ${!hasImage ? 'max-w-2xl' : ''}`}>
<div className="flex flex-col items-start gap-4 w-full"> <div className="flex flex-col items-start gap-4 w-full">
<h2 <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' isDark ? 'text-fd-yellow' : 'text-fd-navy'
}`} }`}
> >
{heading} {heading}
</h2> </h2>
<p <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' 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"> <div className="w-full lg:w-[575px] lg:h-[479px] flex-shrink-0">
<FDImage <FDImage
media={image as Media} media={image as Media}
size="large"
fallbackAlt={heading} fallbackAlt={heading}
className="w-full h-full object-cover rounded-[70px]" className="w-full h-full object-cover rounded-[70px]"
sizes="(max-width: 1024px) 100vw, 575px"
/> />
</div> </div>
) : null ) : null
return ( 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"> <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' ? ( {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 }, 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 }, 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 }, 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 }, 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' }> = { const buttonVariantMap: Record<string, { variant: 'primary' | 'outline' }> = {
yellow: { variant: 'primary' }, yellow: { variant: 'primary' },
navy: { variant: 'primary' }, navy: { variant: 'primary' },
@ -54,34 +53,33 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
const { variant } = buttonVariantMap[buttonColor || 'yellow'] const { variant } = buttonVariantMap[buttonColor || 'yellow']
const cardCount = cards?.length || 1 const cardCount = cards?.length || 1
const gridCols = gridColsMap[cardCount] || gridColsMap[3] const gridCols = gridColsMap[cardCount] || gridColsMap[3]
// outline-dark vs outline-light is determined by the card background
const outlineOnDark = style.isDark const outlineOnDark = style.isDark
return ( 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="max-w-[1200px] mx-auto px-6 md:px-8">
{sectionTitle && ( {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} {sectionTitle}
</h2> </h2>
)} )}
{/* FIX: rounded-[32px] → rounded-[70px] per design system */}
<div className={`grid grid-cols-1 ${gridCols} gap-6 md:gap-8`}> <div className={`grid grid-cols-1 ${gridCols} gap-6 md:gap-8`}>
{cards?.map((card, index) => ( {cards?.map((card, index) => (
<div <div
key={index} 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} {card.title}
</h3> </h3>
{card.subtitle && ( {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} {card.subtitle}
</p> </p>
)} )}
{card.description && ( {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} {card.description}
</p> </p>
)} )}
@ -90,7 +88,7 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
{card.bulletPoints.map((point, i) => ( {card.bulletPoints.map((point, i) => (
<li key={i} className="flex items-start gap-3"> <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="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> </li>
))} ))}
</ul> </ul>
@ -101,7 +99,6 @@ export const FDPricingCardBlockComponent: React.FC<FDPricingCardBlockProps> = ({
href={card.ctaLink || '#'} href={card.ctaLink || '#'}
variant={variant} variant={variant}
onDark={variant === 'outline' ? outlineOnDark : style.isDark} onDark={variant === 'outline' ? outlineOnDark : style.isDark}
className="text-lg md:text-xl"
> >
{card.ctaText} {card.ctaText}
</FDButton> </FDButton>

View File

@ -14,7 +14,8 @@ export const FDServicesGridBlockComponent: React.FC<FDServicesGridBlockProps> =
columns = '4', columns = '4',
}) => { }) => {
return ( 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"> <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"> <h2 className="font-joey-heavy text-fd-h1 text-fd-navy mb-8 lg:mb-12">
{heading} {heading}

View File

@ -1,18 +1,17 @@
// @ts-nocheck
import React from 'react' import React from 'react'
import type { FDTeamBlock as FDTeamBlockProps, Media } from '@/payload-types' import type { FDTeamBlock as FDTeamBlockProps, Media } from '@/payload-types'
import { FDImage } from '@/components/FDImage' import { FDImage } from '@/components/FDImage'
const sectionBgMap: Record<string, string> = { const sectionBgMap: Record<string, string> = {
white: 'bg-white', white: 'bg-white',
gray: 'bg-[#e5e5e5]', gray: 'bg-fd-gray-light',
navy: 'bg-fd-navy', navy: 'bg-fd-navy',
} }
const cardMap: Record<string, { bg: string; name: string; role: string; bio: string; icon: string }> = { 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' }, 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' }, 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> = { const colsMap: Record<string, string> = {
@ -29,9 +28,9 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
cardStyle = 'navy', cardStyle = 'navy',
sectionBackground = 'white', sectionBackground = 'white',
}) => { }) => {
const sectionBg = sectionBgMap[sectionBackground] || sectionBgMap.white const sectionBg = sectionBgMap[sectionBackground ?? 'white'] || sectionBgMap.white
const card = cardMap[cardStyle] || cardMap.navy const card = cardMap[cardStyle ?? 'navy'] || cardMap.navy
const gridCols = colsMap[columns] || colsMap['3'] const gridCols = colsMap[columns ?? '3'] || colsMap['3']
const isNavySection = sectionBackground === 'navy' const isNavySection = sectionBackground === 'navy'
return ( return (
@ -42,12 +41,12 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{(heading || subheading) && ( {(heading || subheading) && (
<div className="flex flex-col gap-3 mb-10 md:mb-14"> <div className="flex flex-col gap-3 mb-10 md:mb-14">
{heading && ( {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} {heading}
</h2> </h2>
)} )}
{subheading && ( {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} {subheading}
</p> </p>
)} )}
@ -57,22 +56,24 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{/* Grid */} {/* Grid */}
<div className={`grid grid-cols-1 ${gridCols} gap-6`}> <div className={`grid grid-cols-1 ${gridCols} gap-6`}>
{members?.map((member, i) => { {members?.map((member, i) => {
// FIX: cast photo properly as Media and pass full object to FDImage
const photo = member.photo as Media | undefined const photo = member.photo as Media | undefined
const hasPhoto = photo && typeof photo === 'object' && photo.url
return ( return (
<div key={i} className={`${card.bg} rounded-[70px] overflow-hidden flex flex-col`}> <div key={i} className={`${card.bg} rounded-[70px] overflow-hidden flex flex-col`}>
{/* Photo */} {/* Photo */}
<div className="aspect-[4/3] w-full overflow-hidden"> <div className="aspect-[4/3] w-full overflow-hidden">
{photo?.url ? ( {hasPhoto ? (
<FDImage <FDImage
src={photo.url} media={photo}
alt={photo.alt || member.name} size="medium"
width={600}
height={450}
className="w-full h-full object-cover object-top" 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'}`}> <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"> <svg viewBox="0 0 80 80" className="w-20 h-20 opacity-30" fill="currentColor">
<circle cx="40" cy="30" r="18" /> <circle cx="40" cy="30" r="18" />
@ -84,11 +85,11 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
{/* Info */} {/* Info */}
<div className="flex flex-col gap-2 px-8 py-8 flex-1"> <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-bold text-fd-h3 ${card.name}`}>{member.name}</p>
<p className={`font-joey text-sm ${card.role}`}>{member.role}</p> <p className={`font-joey text-fd-small ${card.role}`}>{member.role}</p>
{member.bio && ( {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} {member.bio}
</p> </p>
)} )}
@ -102,7 +103,6 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
aria-label={`E-post till ${member.name}`} aria-label={`E-post till ${member.name}`}
className={`transition-colors ${card.icon}`} className={`transition-colors ${card.icon}`}
> >
{/* Email icon */}
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <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" /> <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> </svg>
@ -116,7 +116,6 @@ export const FDTeamBlockComponent: React.FC<FDTeamBlockProps> = ({
aria-label={`LinkedIn för ${member.name}`} aria-label={`LinkedIn för ${member.name}`}
className={`transition-colors ${card.icon}`} className={`transition-colors ${card.icon}`}
> >
{/* LinkedIn icon */}
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"> <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" /> <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> </svg>

View File

@ -1,29 +1,48 @@
// @ts-nocheck
import React from 'react' import React from 'react'
import type { FDTestimonialBlock as FDTestimonialBlockProps, Media } from '@/payload-types' import type { FDTestimonialBlock as FDTestimonialBlockProps, Media } from '@/payload-types'
import { FDImage } from '@/components/FDImage' import { FDImage } from '@/components/FDImage'
const bgMap: Record<string, { section: string; card: string; quote: string; meta: string; name: string; company: string }> = { 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' }, 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-[#e5e5e5]', 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' }, 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> = ({ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
heading, heading,
testimonials, testimonials,
layout = 'grid', layout = 'grid',
sectionBackground = 'gray', sectionBackground = 'gray',
}) => { }) => {
const theme = bgMap[sectionBackground] || bgMap.gray const theme = bgMap[sectionBackground ?? 'gray'] || bgMap.gray
const isFeatured = layout === 'featured' const isFeatured = layout === 'featured'
const isNavy = sectionBackground === 'navy'
return ( return (
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${theme.section}`}> <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"> <div className="max-w-[1200px] mx-auto px-6 md:px-8">
{heading && ( {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} {heading}
</h2> </h2>
)} )}
@ -36,22 +55,12 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
const t = testimonials[0] const t = testimonials[0]
const avatar = t.avatar as Media | undefined const avatar = t.avatar as Media | undefined
return ( 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}`}> <p className={`font-joey-medium text-xl md:text-2xl lg:text-3xl leading-relaxed ${theme.quote}`}>
&ldquo;{t.quote}&rdquo; &ldquo;{t.quote}&rdquo;
</p> </p>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{avatar?.url && ( <Avatar media={avatar} name={t.authorName} size={56} />
<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>
)}
<div> <div>
<p className={`font-joey-bold text-lg ${theme.name}`}>{t.authorName}</p> <p className={`font-joey-bold text-lg ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-sm ${theme.meta}`}> <p className={`font-joey text-sm ${theme.meta}`}>
@ -74,17 +83,7 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
&ldquo;{t.quote}&rdquo; &ldquo;{t.quote}&rdquo;
</p> </p>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{avatar?.url && ( <Avatar media={avatar} name={t.authorName} size={40} />
<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>
)}
<div> <div>
<p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p> <p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-xs ${theme.meta}`}> <p className={`font-joey text-xs ${theme.meta}`}>
@ -106,24 +105,14 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
return ( return (
<div key={i} className={`${theme.card} rounded-[70px] px-8 md:px-10 py-10 md:py-12 flex flex-col gap-6`}> <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 */} {/* 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; &ldquo;
</span> </span>
<p className={`font-joey-medium text-lg leading-relaxed -mt-4 ${theme.quote}`}> <p className={`font-joey-medium text-lg leading-relaxed -mt-4 ${theme.quote}`}>
{t.quote} {t.quote}
</p> </p>
<div className="flex items-center gap-3 mt-auto"> <div className="flex items-center gap-3 mt-auto">
{avatar?.url && ( <Avatar media={avatar} name={t.authorName} size={40} />
<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>
)}
<div> <div>
<p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p> <p className={`font-joey-bold text-base ${theme.name}`}>{t.authorName}</p>
<p className={`font-joey text-sm ${theme.meta}`}> <p className={`font-joey text-sm ${theme.meta}`}>

View File

@ -1,11 +1,12 @@
import React from 'react' import React from 'react'
import type { FDWideCardBlock as FDWideCardBlockProps, Media } from '@/payload-types' import type { FDWideCardBlock as FDWideCardBlockProps, Media } from '@/payload-types'
import { FDButton } from '@/components/FDButton' import { FDButton } from '@/components/FDButton'
import { FDImage } from '@/components/FDImage'
const cardBgMap: Record<string, { bg: string; heading: string; body: string; isDark: boolean }> = { 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 }, 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 }, 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 }, 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', 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' }> = { const btnVariantMap: Record<string, { variant: 'primary' | 'outline' }> = {
yellow: { variant: 'primary' }, yellow: { variant: 'primary' },
navy: { variant: 'outline' }, navy: { variant: 'outline' },
@ -36,19 +36,18 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
const sectionBg = sectionBgMap[sectionBackground || 'white'] const sectionBg = sectionBgMap[sectionBackground || 'white']
const { variant } = btnVariantMap[buttonColor || 'yellow'] const { variant } = btnVariantMap[buttonColor || 'yellow']
const media = image as Media | undefined const media = image as Media | undefined
const imageUrl = media?.url || '' const hasImage = media && typeof media === 'object' && media.url
const hasImage = Boolean(imageUrl)
return ( 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="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={`${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"> <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-2xl md:text-3xl lg:text-[42px] leading-tight ${card.heading}`}> <h2 className={`font-joey-heavy text-fd-h1 leading-tight ${card.heading}`}>
{heading} {heading}
</h2> </h2>
{body && ( {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} {body}
</p> </p>
)} )}
@ -58,16 +57,24 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
href={ctaLink || '#'} href={ctaLink || '#'}
variant={variant} variant={variant}
onDark={card.isDark} onDark={card.isDark}
className="text-lg md:text-xl"
> >
{ctaText} {ctaText}
</FDButton> </FDButton>
</div> </div>
)} )}
</div> </div>
{/* FIX: replaced raw <img> with FDImage */}
{hasImage && ( {hasImage && (
<div className="flex-1 min-h-[250px] lg:min-h-0"> <div className="relative flex-1 min-h-[250px] lg:min-h-0">
<img src={imageUrl} alt={media?.alt || ''} className="w-full h-full object-cover" /> <FDImage
media={media}
size="large"
fill
className="object-cover"
sizes="(max-width: 1024px) 100vw, 50vw"
fallbackAlt={heading || ''}
/>
</div> </div>
)} )}
</div> </div>

File diff suppressed because one or more lines are too long