feat: mobile typography overhaul + layout fixes
Typography: - Raise all fd-* token minimums in globals.css for larger mobile text (fd-h1: 28→40px, fd-h2: 22→32px, fd-h3: 18→22px, body-lg: 15→18px, body: 14→16px) - Strip all text-[x] sm:text-fd-* overrides from 25 block components — tokens now handle full range, no block-level hacks - Change RichText enableGutter default to false — fixes rich text indenting in FAQ, TextBlock, posts, and form confirmations Layout fixes (mobile): - FDContactBlock: 3-column grid on all screen sizes - FDHeroBlock: full-width buttons on mobile - FDServicesGridBlock: px-3 padding on description text under images - FDWideCardBlock: image now padded inside card, object-contain, no longer bleeds edge-to-edge - FDCtaSideImageBlock: consistent mobile order (heading → image → body → button) regardless of imagePosition setting; extra mt-4 between body and button on mobile - FDTestimonialBlock: quote text sizes migrated to fd-h3/fd-body-lg tokens
This commit is contained in:
parent
82d2e48ee5
commit
89f6ab505d
@ -379,37 +379,37 @@ html:not([data-theme]) {
|
|||||||
Usage: text-fd-display, text-fd-h1, text-fd-body, etc. */
|
Usage: text-fd-display, text-fd-h1, text-fd-body, etc. */
|
||||||
|
|
||||||
/* Level 1 — Display / Hero H1 */
|
/* Level 1 — Display / Hero H1 */
|
||||||
--text-fd-display: clamp(2.25rem, 5vw + 0.5rem, 4.875rem); /* 36px → 78px */
|
--text-fd-display: clamp(2.75rem, 5vw + 0.5rem, 4.875rem); /* 44px → 78px */
|
||||||
--text-fd-display--line-height: 1.05;
|
--text-fd-display--line-height: 1.05;
|
||||||
--text-fd-display--letter-spacing: -0.02em;
|
--text-fd-display--letter-spacing: -0.02em;
|
||||||
|
|
||||||
/* Level 2 — Section H1 */
|
/* Level 2 — Section H1 */
|
||||||
--text-fd-h1: clamp(1.75rem, 3.5vw + 0.25rem, 3.125rem); /* 28px → 50px */
|
--text-fd-h1: clamp(2.5rem, 3.5vw + 0.25rem, 3.125rem); /* 40px → 50px */
|
||||||
--text-fd-h1--line-height: 1.15;
|
--text-fd-h1--line-height: 1.15;
|
||||||
--text-fd-h1--letter-spacing: -0.01em;
|
--text-fd-h1--letter-spacing: -0.01em;
|
||||||
|
|
||||||
/* Level 3 — Block titles / H2 */
|
/* Level 3 — Block titles / H2 */
|
||||||
--text-fd-h2: clamp(1.375rem, 2.5vw + 0.25rem, 2.25rem); /* 22px → 36px */
|
--text-fd-h2: clamp(2rem, 2.5vw + 0.25rem, 2.25rem); /* 32px → 36px */
|
||||||
--text-fd-h2--line-height: 1.2;
|
--text-fd-h2--line-height: 1.2;
|
||||||
|
|
||||||
/* Level 4 — Card titles / H3 */
|
/* Level 4 — Card titles / H3 */
|
||||||
--text-fd-h3: clamp(1.125rem, 2vw + 0.125rem, 1.75rem); /* 18px → 28px */
|
--text-fd-h3: clamp(1.375rem, 2vw + 0.125rem, 1.75rem); /* 22px → 28px */
|
||||||
--text-fd-h3--line-height: 1.3;
|
--text-fd-h3--line-height: 1.3;
|
||||||
|
|
||||||
/* Level 5 — Subheadings / H4 */
|
/* Level 5 — Subheadings / H4 */
|
||||||
--text-fd-h4: clamp(1rem, 1.5vw + 0.125rem, 1.375rem); /* 16px → 22px */
|
--text-fd-h4: clamp(1.125rem, 1.5vw + 0.125rem, 1.375rem); /* 18px → 22px */
|
||||||
--text-fd-h4--line-height: 1.4;
|
--text-fd-h4--line-height: 1.4;
|
||||||
|
|
||||||
/* Level 6 — Lead / large body */
|
/* Level 6 — Lead / large body */
|
||||||
--text-fd-body-lg: clamp(0.9375rem, 1.2vw + 0.125rem, 1.25rem); /* 15px → 20px */
|
--text-fd-body-lg: clamp(1.125rem, 1.2vw + 0.125rem, 1.25rem); /* 18px → 20px */
|
||||||
--text-fd-body-lg--line-height: 1.6;
|
--text-fd-body-lg--line-height: 1.6;
|
||||||
|
|
||||||
/* Level 7 — Body text */
|
/* Level 7 — Body text */
|
||||||
--text-fd-body: clamp(0.875rem, 1vw + 0.125rem, 1.0625rem); /* 14px → 17px */
|
--text-fd-body: clamp(1rem, 1vw + 0.125rem, 1.0625rem); /* 16px → 17px */
|
||||||
--text-fd-body--line-height: 1.7;
|
--text-fd-body--line-height: 1.7;
|
||||||
|
|
||||||
/* Level 8 — Small / captions */
|
/* Level 8 — Small / captions */
|
||||||
--text-fd-small: clamp(0.8125rem, 0.9vw + 0.0625rem, 0.9375rem); /* 13px → 15px */
|
--text-fd-small: clamp(0.875rem, 0.9vw + 0.0625rem, 0.9375rem); /* 14px → 15px */
|
||||||
--text-fd-small--line-height: 1.6;
|
--text-fd-small--line-height: 1.6;
|
||||||
|
|
||||||
/* Level 9 — Extra small / fine print */
|
/* Level 9 — Extra small / fine print */
|
||||||
|
|||||||
@ -17,13 +17,13 @@ export const FDContactBlockComponent: React.FC<FDContactBlockProps> = ({
|
|||||||
{heading}
|
{heading}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-stretch gap-4 md:gap-7 w-full max-w-[656px]">
|
<div className="grid grid-cols-3 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-[140px] sm:h-[120px] md:h-[160px] lg:h-[200px] overflow-hidden ${imageRadius}`}>
|
<div className={`relative w-full h-[100px] sm:h-[120px] md:h-[160px] lg:h-[200px] overflow-hidden ${imageRadius}`}>
|
||||||
<FDImage
|
<FDImage
|
||||||
media={media}
|
media={media}
|
||||||
size="medium"
|
size="medium"
|
||||||
|
|||||||
@ -60,9 +60,9 @@ export const FDCtaBannerBlockComponent: React.FC<FDCtaBannerBlockProps> = ({
|
|||||||
return (
|
return (
|
||||||
<section id={anchorId || undefined} className={`w-full ${sizing.py} ${theme.section}`}>
|
<section id={anchorId || undefined} className={`w-full ${sizing.py} ${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">
|
||||||
<div className={`flex flex-col gap-6 md:gap-8 ${isCenter ? 'items-center text-center' : 'items-start text-left'} max-w-[800px] ${isCenter ? 'mx-auto' : ''}`}>
|
<div className={`flex flex-col gap-6 md:gap-8 items-center text-center min-[820px]:${isCenter ? 'items-center text-center' : 'items-start text-left'} max-w-[800px] ${isCenter ? 'mx-auto' : ''}`}>
|
||||||
|
|
||||||
<div className={`flex flex-col gap-3 ${isCenter ? 'items-center' : 'items-start'}`}>
|
<div className={`flex flex-col gap-3 items-center min-[820px]:${isCenter ? 'items-center' : 'items-start'}`}>
|
||||||
<h2 className={`font-joey-heavy leading-tight ${sizing.heading} ${theme.heading}`}>
|
<h2 className={`font-joey-heavy leading-tight ${sizing.heading} ${theme.heading}`}>
|
||||||
{heading}
|
{heading}
|
||||||
</h2>
|
</h2>
|
||||||
@ -74,11 +74,11 @@ export const FDCtaBannerBlockComponent: React.FC<FDCtaBannerBlockProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(ctaText || secondaryCtaText) && (
|
{(ctaText || secondaryCtaText) && (
|
||||||
<div className={`flex flex-col sm:flex-row gap-4 ${isCenter ? 'justify-center' : ''}`}>
|
<div className={`flex flex-col sm:flex-row gap-4 w-full sm:w-auto ${isCenter ? 'justify-center' : ''}`}>
|
||||||
{ctaText && (
|
{ctaText && (
|
||||||
<a
|
<a
|
||||||
href={ctaLink || '#'}
|
href={ctaLink || '#'}
|
||||||
className={`inline-flex items-center justify-center px-8 py-3 rounded-full font-joey-bold text-fd-btn transition-colors ${theme.primaryBtn}`}
|
className={`inline-flex items-center justify-center px-8 py-3 rounded-full font-joey-bold text-fd-btn transition-colors w-full sm:w-auto ${theme.primaryBtn}`}
|
||||||
>
|
>
|
||||||
{ctaText}
|
{ctaText}
|
||||||
</a>
|
</a>
|
||||||
@ -86,7 +86,7 @@ export const FDCtaBannerBlockComponent: React.FC<FDCtaBannerBlockProps> = ({
|
|||||||
{secondaryCtaText && secondaryCtaLink && (
|
{secondaryCtaText && secondaryCtaLink && (
|
||||||
<a
|
<a
|
||||||
href={secondaryCtaLink}
|
href={secondaryCtaLink}
|
||||||
className={`inline-flex items-center justify-center px-8 py-3 rounded-full font-joey-bold text-fd-btn transition-colors ${theme.secondaryBtn}`}
|
className={`inline-flex items-center justify-center px-8 py-3 rounded-full font-joey-bold text-fd-btn transition-colors w-full sm:w-auto ${theme.secondaryBtn}`}
|
||||||
>
|
>
|
||||||
{secondaryCtaText}
|
{secondaryCtaText}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -47,46 +47,52 @@ export const FDCtaSideImageBlockComponent: React.FC<FDCtaSideImageBlockProps> =
|
|||||||
? 'text-white'
|
? 'text-white'
|
||||||
: 'text-fd-navy dark:text-white'
|
: 'text-fd-navy dark:text-white'
|
||||||
|
|
||||||
const textContent = (
|
// Mobile order is always: heading → image → body → button
|
||||||
<div className={`flex flex-col flex-1 items-start gap-8 min-[820px]:gap-[41px] ${!hasImage ? 'max-w-2xl' : ''}`}>
|
// Desktop order respects imagePosition (left/right)
|
||||||
<div className="flex flex-col items-start gap-4 w-full">
|
// We achieve this by splitting heading from body+button and using CSS order
|
||||||
<h2 className={`w-full font-joey-heavy text-fd-h1 leading-tight ${headingClass}`}>
|
|
||||||
{heading}
|
|
||||||
</h2>
|
|
||||||
<p className={`w-full font-joey text-fd-body-lg leading-relaxed ${bodyClass}`}>
|
|
||||||
{body}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{ctaText && (
|
const desktopTextOrder = imagePosition === 'left' ? 'min-[820px]:order-2' : 'min-[820px]:order-1'
|
||||||
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark}>
|
const desktopImageOrder = imagePosition === 'left' ? 'min-[820px]:order-1' : 'min-[820px]:order-2'
|
||||||
{ctaText}
|
|
||||||
</FDButton>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
const imageContent = hasImage ? (
|
|
||||||
<div className={`relative w-full min-[820px]:w-[45%] lg:w-[575px] lg:h-[479px] flex-shrink-0 overflow-hidden ${imageRadius}`}>
|
|
||||||
<FDImage
|
|
||||||
media={image as Media}
|
|
||||||
size="large"
|
|
||||||
fallbackAlt={heading}
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
sizes="(max-width: 1024px) 100vw, 575px"
|
|
||||||
/>
|
|
||||||
{overlay && <div className={`absolute inset-0 ${overlay}`} />}
|
|
||||||
</div>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
||||||
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col min-[820px]:flex-row items-center gap-10 min-[820px]:gap-16">
|
<div className="max-w-[1200px] mx-auto px-6 md:px-8 flex flex-col min-[820px]:flex-row min-[820px]:items-center gap-6 min-[820px]:gap-16">
|
||||||
{imagePosition === 'left' ? (
|
|
||||||
<>{imageContent}{textContent}</>
|
{/* Heading — always first on mobile */}
|
||||||
) : (
|
<h2 className={`w-full font-joey-heavy text-fd-h1 leading-tight order-1 min-[820px]:hidden ${headingClass}`}>
|
||||||
<>{textContent}{imageContent}</>
|
{heading}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Image — always second on mobile, position-aware on desktop */}
|
||||||
|
{hasImage && (
|
||||||
|
<div className={`relative w-full min-[820px]:w-[45%] lg:w-[575px] lg:h-[479px] flex-shrink-0 overflow-hidden order-2 ${desktopImageOrder} ${imageRadius}`}>
|
||||||
|
<FDImage
|
||||||
|
media={image as Media}
|
||||||
|
size="large"
|
||||||
|
fallbackAlt={heading}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 575px"
|
||||||
|
/>
|
||||||
|
{overlay && <div className={`absolute inset-0 ${overlay}`} />}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Text block — heading hidden on mobile (shown above), body+button always third */}
|
||||||
|
<div className={`flex flex-col flex-1 items-start gap-4 min-[820px]:gap-[41px] order-3 ${desktopTextOrder} ${!hasImage ? 'max-w-2xl' : ''}`}>
|
||||||
|
{/* Heading shown only on desktop inside the text column */}
|
||||||
|
<h2 className={`hidden min-[820px]:block w-full font-joey-heavy text-fd-h1 leading-tight ${headingClass}`}>
|
||||||
|
{heading}
|
||||||
|
</h2>
|
||||||
|
<p className={`w-full font-joey text-fd-body-lg leading-relaxed ${bodyClass}`}>
|
||||||
|
{body}
|
||||||
|
</p>
|
||||||
|
{ctaText && (
|
||||||
|
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark} className="w-full sm:w-auto justify-center mt-4 min-[820px]:mt-0">
|
||||||
|
{ctaText}
|
||||||
|
</FDButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -242,11 +242,7 @@ export const FDDataTableBlockComponent: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && !error && tableData && tableData.rows.length > 0 && (
|
{!loading && !error && tableData && tableData.rows.length > 0 && null}
|
||||||
<p className={`mt-3 font-joey text-fd-xs opacity-40 ${bodyClass}`}>
|
|
||||||
{tableData.rows.length} rader · {tableData.headers.length} kolumner
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -54,13 +54,16 @@ export const FDFaqBlockComponent: React.FC<FDFaqBlockProps> = ({
|
|||||||
<div key={index} className={`border-b ${borderColor}`}>
|
<div key={index} className={`border-b ${borderColor}`}>
|
||||||
<button
|
<button
|
||||||
id={triggerId}
|
id={triggerId}
|
||||||
className={`flex w-full items-center gap-3 md:gap-4 py-4 md:py-5 text-left transition-colors ${textColor}`}
|
className={`flex w-full items-center justify-between gap-3 md:gap-4 py-4 md:py-5 text-left transition-colors ${textColor}`}
|
||||||
onClick={() => setOpenIndex(isOpen ? null : index)}
|
onClick={() => setOpenIndex(isOpen ? null : index)}
|
||||||
aria-expanded={isOpen}
|
aria-expanded={isOpen}
|
||||||
aria-controls={panelId}
|
aria-controls={panelId}
|
||||||
>
|
>
|
||||||
|
<span className="font-joey text-fd-h3">
|
||||||
|
{item.question}
|
||||||
|
</span>
|
||||||
<svg
|
<svg
|
||||||
className={`w-4 h-4 md:w-5 md:h-5 flex-shrink-0 transition-transform duration-200 ${
|
className={`w-5 h-5 flex-shrink-0 transition-transform duration-200 ${
|
||||||
isOpen ? 'rotate-45' : ''
|
isOpen ? 'rotate-45' : ''
|
||||||
}`}
|
}`}
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -72,9 +75,6 @@ export const FDFaqBlockComponent: React.FC<FDFaqBlockProps> = ({
|
|||||||
<line x1="10" y1="2" x2="10" y2="18" />
|
<line x1="10" y1="2" x2="10" y2="18" />
|
||||||
<line x1="2" y1="10" x2="18" y2="10" />
|
<line x1="2" y1="10" x2="18" y2="10" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="font-joey text-fd-h3">
|
|
||||||
{item.question}
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
id={panelId}
|
id={panelId}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const FDFeatureAnnouncementBlockComponent: React.FC<FDFeatureAnnouncement
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className={`w-full max-w-[1112px] font-joey text-xl md:text-2xl lg:text-4xl text-center leading-relaxed ${bodyColor}`}
|
className={`w-full max-w-[1112px] font-joey text-xl md:text-fd-body-lg text-center leading-relaxed ${bodyColor}`}
|
||||||
>
|
>
|
||||||
{body}
|
{body}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -75,14 +75,14 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
|||||||
{body}
|
{body}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4">
|
<div className="flex flex-col items-stretch sm:flex-row sm:items-center gap-4 w-full sm:w-auto">
|
||||||
{ctaText && (
|
{ctaText && (
|
||||||
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark}>
|
<FDButton href={ctaLink || '#'} variant="primary" onDark={isDark} className="w-full sm:w-auto justify-center">
|
||||||
{ctaText}
|
{ctaText}
|
||||||
</FDButton>
|
</FDButton>
|
||||||
)}
|
)}
|
||||||
{secondaryCtaText && (
|
{secondaryCtaText && (
|
||||||
<FDButton href={secondaryCtaLink || '#'} variant="outline" onDark={secondaryOnDark}>
|
<FDButton href={secondaryCtaLink || '#'} variant="outline" onDark={secondaryOnDark} className="w-full sm:w-auto justify-center">
|
||||||
{secondaryCtaText}
|
{secondaryCtaText}
|
||||||
</FDButton>
|
</FDButton>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -38,7 +38,6 @@ export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
|||||||
{(heading || description || ctaText) && (
|
{(heading || description || ctaText) && (
|
||||||
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6 mb-12 md:mb-16">
|
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6 mb-12 md:mb-16">
|
||||||
{heading && (
|
{heading && (
|
||||||
/* Priority #6: Was font-joey-medium — now font-joey-heavy for consistency with all section headings */
|
|
||||||
<h2 className={`font-joey-heavy text-fd-h1 ${titleClass} lg:max-w-[380px]`}>
|
<h2 className={`font-joey-heavy text-fd-h1 ${titleClass} lg:max-w-[380px]`}>
|
||||||
{heading}
|
{heading}
|
||||||
</h2>
|
</h2>
|
||||||
@ -49,7 +48,7 @@ export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
{ctaText && (
|
{ctaText && (
|
||||||
<a href={ctaLink || '#'} className="self-start fd-btn-primary">
|
<a href={ctaLink || '#'} className="self-start fd-btn-primary">
|
||||||
{ctaText} →
|
{ctaText}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -60,7 +59,7 @@ export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
|||||||
{(cards ?? []).map((card, i) => {
|
{(cards ?? []).map((card, i) => {
|
||||||
const media = card.image as Media | undefined
|
const media = card.image as Media | undefined
|
||||||
const isLink = Boolean(card.link)
|
const isLink = Boolean(card.link)
|
||||||
const className = `group relative overflow-hidden rounded-[32px] md:rounded-[50px] lg:rounded-[70px] aspect-[4/3] block ${isLink ? 'cursor-pointer' : ''}`
|
const className = `group relative overflow-hidden rounded-[32px] md:rounded-[50px] lg:rounded-[70px] aspect-[16/9] sm:aspect-[4/3] block ${isLink ? 'cursor-pointer' : ''}`
|
||||||
|
|
||||||
const inner = (
|
const inner = (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -26,32 +26,34 @@ export const FDPartnersLogosBlockComponent: React.FC<FDPartnersLogosBlockProps>
|
|||||||
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
<section className={`w-full py-16 md:py-20 lg:py-[99px] ${bgClass}`}>
|
||||||
<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 && (
|
||||||
/* Priority #6: Was font-joey-medium — now font-joey-heavy for section heading consistency */
|
|
||||||
<h2 className={`font-joey-heavy text-fd-h2 text-center mb-10 md:mb-14 ${titleClass}`}>
|
<h2 className={`font-joey-heavy text-fd-h2 text-center mb-10 md:mb-14 ${titleClass}`}>
|
||||||
{heading}
|
{heading}
|
||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-wrap items-center justify-center gap-10 md:gap-14 lg:gap-16">
|
{/* Mobile: horizontal scroll. Desktop: wrap grid */}
|
||||||
{(logos ?? []).map((item, i) => {
|
<div className="overflow-x-auto pb-2 sm:overflow-visible sm:pb-0 -mx-6 px-6 sm:mx-0 sm:px-0">
|
||||||
const media = item.image as Media | undefined
|
<div className="flex sm:flex-wrap items-center justify-start sm:justify-center gap-8 sm:gap-10 md:gap-14 lg:gap-16 min-w-max sm:min-w-0">
|
||||||
if (!media?.url) return null
|
{(logos ?? []).map((item, i) => {
|
||||||
|
const media = item.image as Media | undefined
|
||||||
|
if (!media?.url) return null
|
||||||
|
|
||||||
const logoEl = (
|
const logoEl = (
|
||||||
<img
|
<img
|
||||||
src={media.url}
|
src={media.url}
|
||||||
alt={item.alt || ''}
|
alt={item.alt || ''}
|
||||||
className={`h-12 md:h-16 lg:h-[72px] w-auto max-w-[180px] md:max-w-[220px] object-contain ${imgFilter}`}
|
className={`h-14 sm:h-12 md:h-16 lg:h-[72px] w-auto max-w-[160px] sm:max-w-[180px] md:max-w-[220px] object-contain ${imgFilter}`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
return item.link ? (
|
return item.link ? (
|
||||||
<a key={i} href={item.link} target="_blank" rel="noopener noreferrer" className="flex items-center justify-center">
|
<a key={i} href={item.link} target="_blank" rel="noopener noreferrer" className="flex items-center justify-center flex-shrink-0">
|
||||||
{logoEl}
|
{logoEl}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
<div key={i} className="flex items-center justify-center">{logoEl}</div>
|
<div key={i} className="flex items-center justify-center flex-shrink-0">{logoEl}</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -131,6 +131,7 @@ 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="w-full sm:w-auto justify-center"
|
||||||
>
|
>
|
||||||
{card.ctaText}
|
{card.ctaText}
|
||||||
</FDButton>
|
</FDButton>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ function ResourceRow({
|
|||||||
style={{ appearance: 'textfield' }}
|
style={{ appearance: 'textfield' }}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
/>
|
/>
|
||||||
<span className="font-joey text-fd-body w-8 text-fd-navy/40 dark:text-white/40">{unit}</span>
|
<span className="hidden sm:inline font-joey text-fd-body w-8 text-fd-navy/40 dark:text-white/40">{unit}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onChange(Math.min(max, value + step))}
|
onClick={() => onChange(Math.min(max, value + step))}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export const FDServicesGridBlockComponent: React.FC<FDServicesGridBlockProps> =
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="font-joey text-fd-navy dark:text-white/80 text-fd-body">
|
<p className="font-joey text-fd-navy dark:text-white/80 text-fd-body px-3">
|
||||||
{service.description}
|
{service.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -82,7 +82,7 @@ export const FDStatisticsBlockComponent: React.FC<Props> = ({
|
|||||||
}`}
|
}`}
|
||||||
style={prefersReducedMotion ? undefined : { transitionDelay: `${i * 120}ms` }}
|
style={prefersReducedMotion ? undefined : { transitionDelay: `${i * 120}ms` }}
|
||||||
>
|
>
|
||||||
<span className={`font-joey-heavy text-5xl md:text-7xl lg:text-[96px] leading-none ${getNumberClass()}`}>
|
<span className={`font-joey-heavy text-[3.5rem] sm:text-5xl md:text-7xl lg:text-[96px] leading-none ${getNumberClass()}`}>
|
||||||
{stat.number}
|
{stat.number}
|
||||||
</span>
|
</span>
|
||||||
<span className={`font-joey text-fd-body max-w-[180px] ${labelClass}`}>
|
<span className={`font-joey text-fd-body max-w-[180px] ${labelClass}`}>
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
|
|||||||
const avatar = t.avatar as Media | undefined
|
const avatar = t.avatar as Media | undefined
|
||||||
return (
|
return (
|
||||||
<div className={`${theme.card} ${cardRadius} px-8 md:px-16 py-10 md:py-16 flex flex-col gap-8`}>
|
<div className={`${theme.card} ${cardRadius} 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-fd-h3 leading-relaxed ${theme.quote}`}>
|
||||||
“{t.quote}”
|
“{t.quote}”
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
@ -102,7 +102,7 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
|
|||||||
const avatar = t.avatar as Media | undefined
|
const avatar = t.avatar as Media | undefined
|
||||||
return (
|
return (
|
||||||
<div key={i} className={`${theme.card} ${cardRadius} px-8 md:px-12 py-10 md:py-12 flex flex-col gap-6`}>
|
<div key={i} className={`${theme.card} ${cardRadius} px-8 md:px-12 py-10 md:py-12 flex flex-col gap-6`}>
|
||||||
<p className={`font-joey-medium text-lg md:text-xl leading-relaxed ${theme.quote}`}>
|
<p className={`font-joey-medium text-fd-body-lg leading-relaxed ${theme.quote}`}>
|
||||||
“{t.quote}”
|
“{t.quote}”
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@ -129,7 +129,7 @@ export const FDTestimonialBlockComponent: React.FC<FDTestimonialBlockProps> = ({
|
|||||||
<span className={`font-joey-heavy text-5xl leading-none ${theme.accent} opacity-30`}>
|
<span className={`font-joey-heavy text-5xl leading-none ${theme.accent} opacity-30`}>
|
||||||
“
|
“
|
||||||
</span>
|
</span>
|
||||||
<p className={`font-joey-medium text-lg leading-relaxed -mt-4 ${theme.quote}`}>
|
<p className={`font-joey-medium text-fd-body-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">
|
||||||
|
|||||||
@ -54,7 +54,7 @@ function ResourceRow({
|
|||||||
className="w-20 text-center font-joey-medium text-fd-body-lg rounded-full px-2 py-1.5 border-2 bg-fd-surface-alt border-fd-navy/15 text-fd-navy dark:bg-white/10 dark:border-white/20 dark:text-white [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
className="w-20 text-center font-joey-medium text-fd-body-lg rounded-full px-2 py-1.5 border-2 bg-fd-surface-alt border-fd-navy/15 text-fd-navy dark:bg-white/10 dark:border-white/20 dark:text-white [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
||||||
style={{ appearance: 'textfield' }}
|
style={{ appearance: 'textfield' }}
|
||||||
/>
|
/>
|
||||||
<span className="font-joey text-fd-body w-8 text-fd-navy/40 dark:text-white/40">{unit}</span>
|
<span className="hidden sm:inline font-joey text-fd-body w-8 text-fd-navy/40 dark:text-white/40">{unit}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onChange(Math.min(max, value + step))}
|
onClick={() => onChange(Math.min(max, value + step))}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
|||||||
return (
|
return (
|
||||||
<section id={anchorId || undefined} className={`w-full py-16 md:py-20 lg:py-[99px] ${sectionBg}`}>
|
<section id={anchorId || undefined} 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} ${cardRadius} overflow-hidden flex flex-col min-[820px]:flex-row`}>
|
<div className={`${card.bg} ${cardRadius} flex flex-col min-[820px]:flex-row`}>
|
||||||
<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">
|
<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}`}>
|
<h2 className={`font-joey-heavy text-fd-h1 leading-tight ${card.heading}`}>
|
||||||
{heading}
|
{heading}
|
||||||
@ -56,7 +56,7 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
|||||||
)}
|
)}
|
||||||
{ctaText && (
|
{ctaText && (
|
||||||
<div>
|
<div>
|
||||||
<FDButton href={ctaLink || '#'} variant={variant} onDark={card.isDark}>
|
<FDButton href={ctaLink || '#'} variant={variant} onDark={card.isDark} className="w-full sm:w-auto justify-center">
|
||||||
{ctaText}
|
{ctaText}
|
||||||
</FDButton>
|
</FDButton>
|
||||||
</div>
|
</div>
|
||||||
@ -64,13 +64,12 @@ export const FDWideCardBlockComponent: React.FC<FDWideCardBlockProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasImage && (
|
{hasImage && (
|
||||||
<div className="relative flex-1 min-h-[250px] lg:min-h-0">
|
<div className="flex items-center justify-center w-full min-[820px]:w-[45%] lg:w-[480px] flex-shrink-0 p-6 md:p-10 lg:p-12">
|
||||||
<FDImage
|
<FDImage
|
||||||
media={media}
|
media={media}
|
||||||
size="large"
|
size="large"
|
||||||
fill
|
className="w-full h-auto max-h-[320px] object-contain drop-shadow-xl"
|
||||||
className="object-cover"
|
sizes="(max-width: 820px) 80vw, 400px"
|
||||||
sizes="(max-width: 1024px) 100vw, 50vw"
|
|
||||||
fallbackAlt={heading || ''}
|
fallbackAlt={heading || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -60,7 +60,7 @@ type Props = {
|
|||||||
} & React.HTMLAttributes<HTMLDivElement>
|
} & React.HTMLAttributes<HTMLDivElement>
|
||||||
|
|
||||||
export default function RichText(props: Props) {
|
export default function RichText(props: Props) {
|
||||||
const { className, enableProse = true, enableGutter = true, ...rest } = props
|
const { className, enableProse = true, enableGutter = false, ...rest } = props
|
||||||
return (
|
return (
|
||||||
<ConvertRichText
|
<ConvertRichText
|
||||||
converters={jsxConverters}
|
converters={jsxConverters}
|
||||||
|
|||||||
@ -1590,6 +1590,9 @@ export interface User {
|
|||||||
role: 'admin' | 'editor';
|
role: 'admin' | 'editor';
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
enableAPIKey?: boolean | null;
|
||||||
|
apiKey?: string | null;
|
||||||
|
apiKeyIndex?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
resetPasswordToken?: string | null;
|
resetPasswordToken?: string | null;
|
||||||
resetPasswordExpiration?: string | null;
|
resetPasswordExpiration?: string | null;
|
||||||
@ -2728,6 +2731,9 @@ export interface UsersSelect<T extends boolean = true> {
|
|||||||
role?: T;
|
role?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
|
enableAPIKey?: T;
|
||||||
|
apiKey?: T;
|
||||||
|
apiKeyIndex?: T;
|
||||||
email?: T;
|
email?: T;
|
||||||
resetPasswordToken?: T;
|
resetPasswordToken?: T;
|
||||||
resetPasswordExpiration?: T;
|
resetPasswordExpiration?: T;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user