perf/a11y: replace raw img tags, fix alt text, aria-hidden, avif/webp
This commit is contained in:
parent
15c3194eb6
commit
534a5644bf
@ -77,6 +77,8 @@ const nextConfig = {
|
|||||||
cpus: 1,
|
cpus: 1,
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
|
formats: ['image/avif', 'image/webp'],
|
||||||
|
minimumCacheTTL: 3600,
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
...[NEXT_PUBLIC_SERVER_URL].map((item) => {
|
...[NEXT_PUBLIC_SERVER_URL].map((item) => {
|
||||||
const url = new URL(item)
|
const url = new URL(item)
|
||||||
@ -85,6 +87,8 @@ const nextConfig = {
|
|||||||
protocol: url.protocol.replace(':', ''),
|
protocol: url.protocol.replace(':', ''),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
{ hostname: 'img.youtube.com', protocol: 'https' },
|
||||||
|
{ hostname: 'i.vimeocdn.com', protocol: 'https' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
webpack: (webpackConfig) => {
|
webpack: (webpackConfig) => {
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export const FDHeroBlockComponent: React.FC<FDHeroBlockProps> = ({
|
|||||||
priority
|
priority
|
||||||
className="absolute inset-0 w-full h-full object-cover"
|
className="absolute inset-0 w-full h-full object-cover"
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
fallbackAlt=""
|
fallbackAlt={heading || ''}
|
||||||
/>
|
/>
|
||||||
<div className={`absolute inset-0 ${overlayClass}`} aria-hidden="true" />
|
<div className={`absolute inset-0 ${overlayClass}`} aria-hidden="true" />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -83,6 +83,7 @@ export const FDLinkCardsBlockComponent: React.FC<Props> = ({
|
|||||||
className="w-12 h-12 md:w-16 md:h-16 object-contain"
|
className="w-12 h-12 md:w-16 md:h-16 object-contain"
|
||||||
sizes="64px"
|
sizes="64px"
|
||||||
fallbackAlt=""
|
fallbackAlt=""
|
||||||
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<h2 className={`font-joey-heavy text-fd-h1 max-w-[700px] ${hClr}`}>
|
<h2 className={`font-joey-heavy text-fd-h1 max-w-[700px] ${hClr}`}>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import type { FDLocationsGridBlock as Props, Media } from '@/payload-types'
|
import type { FDLocationsGridBlock as Props, Media } from '@/payload-types'
|
||||||
|
import { FDImage } from '@/components/FDImage'
|
||||||
import { fdCardRadius, fdContainer} from '@/utilities/fdTheme'
|
import { fdCardRadius, fdContainer} from '@/utilities/fdTheme'
|
||||||
|
|
||||||
export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
||||||
@ -65,10 +66,13 @@ export const FDLocationsGridBlockComponent: React.FC<Props> = ({
|
|||||||
const inner = (
|
const inner = (
|
||||||
<>
|
<>
|
||||||
{media?.url && (
|
{media?.url && (
|
||||||
<img
|
<FDImage
|
||||||
src={media.url}
|
media={media}
|
||||||
alt={(media as any).alt || card.locationName}
|
size="large"
|
||||||
className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
fill
|
||||||
|
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
||||||
|
sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 33vw"
|
||||||
|
fallbackAlt={card.locationName || ''}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent transition-opacity duration-300 group-hover:opacity-0" />
|
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent transition-opacity duration-300 group-hover:opacity-0" />
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import type { FDPartnersLogosBlock as FDPartnersLogosBlockProps, Media } from '@/payload-types'
|
import type { FDPartnersLogosBlock as FDPartnersLogosBlockProps, Media } from '@/payload-types'
|
||||||
|
import { FDImage } from '@/components/FDImage'
|
||||||
import { fdContainer } from '@/utilities/fdTheme'
|
import { fdContainer } from '@/utilities/fdTheme'
|
||||||
|
|
||||||
export const FDPartnersLogosBlockComponent: React.FC<FDPartnersLogosBlockProps> = ({
|
export const FDPartnersLogosBlockComponent: React.FC<FDPartnersLogosBlockProps> = ({
|
||||||
@ -39,10 +40,12 @@ export const FDPartnersLogosBlockComponent: React.FC<FDPartnersLogosBlockProps>
|
|||||||
if (!media?.url) return null
|
if (!media?.url) return null
|
||||||
|
|
||||||
const logoEl = (
|
const logoEl = (
|
||||||
<img
|
<FDImage
|
||||||
src={media.url}
|
media={media}
|
||||||
alt={item.alt || ''}
|
size="medium"
|
||||||
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}`}
|
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}`}
|
||||||
|
sizes="220px"
|
||||||
|
fallbackAlt={item.alt || ''}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,8 @@ interface FDImageProps {
|
|||||||
sizes?: string
|
sizes?: string
|
||||||
/** Fallback alt text if media has none */
|
/** Fallback alt text if media has none */
|
||||||
fallbackAlt?: string
|
fallbackAlt?: string
|
||||||
|
/** Hide from assistive tech (decorative images) */
|
||||||
|
'aria-hidden'?: boolean | 'true' | 'false'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FDImage: React.FC<FDImageProps> = ({
|
export const FDImage: React.FC<FDImageProps> = ({
|
||||||
@ -28,6 +30,7 @@ export const FDImage: React.FC<FDImageProps> = ({
|
|||||||
priority = false,
|
priority = false,
|
||||||
sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px',
|
sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px',
|
||||||
fallbackAlt = '',
|
fallbackAlt = '',
|
||||||
|
'aria-hidden': ariaHidden,
|
||||||
}) => {
|
}) => {
|
||||||
// If media is not a populated object, bail
|
// If media is not a populated object, bail
|
||||||
if (!media || typeof media === 'string' || typeof media === 'number') return null
|
if (!media || typeof media === 'string' || typeof media === 'number') return null
|
||||||
@ -47,7 +50,7 @@ export const FDImage: React.FC<FDImageProps> = ({
|
|||||||
if (isSvg) {
|
if (isSvg) {
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img src={src} alt={alt} className={className} loading="lazy" />
|
<img src={src} alt={alt} className={className} loading="lazy" aria-hidden={ariaHidden} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +63,7 @@ export const FDImage: React.FC<FDImageProps> = ({
|
|||||||
className={className}
|
className={className}
|
||||||
priority={priority}
|
priority={priority}
|
||||||
sizes={sizes}
|
sizes={sizes}
|
||||||
|
aria-hidden={ariaHidden}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -78,6 +82,7 @@ export const FDImage: React.FC<FDImageProps> = ({
|
|||||||
className={className}
|
className={className}
|
||||||
priority={priority}
|
priority={priority}
|
||||||
sizes={sizes}
|
sizes={sizes}
|
||||||
|
aria-hidden={ariaHidden}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user