diff --git a/src/blocks/InlineMedia/Component.tsx b/src/blocks/InlineMedia/Component.tsx index 88d32bcb..1fd13ecb 100644 --- a/src/blocks/InlineMedia/Component.tsx +++ b/src/blocks/InlineMedia/Component.tsx @@ -1,8 +1,11 @@ import type { InlineMediaBlock } from '@/payload-types' import { Media } from '@/components/Media' +import { cssVariables } from '@/cssVariables' import { cn } from '@/utilities/ui' +const { breakpoints } = cssVariables + type Props = Omit const widthClasses = { @@ -19,6 +22,15 @@ const verticalAlignClasses = { baseline: 'align-baseline', } +// Maps percentage width to a sizes attribute, using container-relative pixel +// estimates so the browser picks an appropriately sized image in narrow containers. +const sizesForWidth: Record = { + '25': `(max-width: ${breakpoints.sm}px) 25vw, 192px`, // ~25% of a ~768px container + '50': `(max-width: ${breakpoints.sm}px) 50vw, 384px`, // ~50% of a ~768px container + '75': `(max-width: ${breakpoints.sm}px) 75vw, 576px`, // ~75% of a ~768px container + '100': `(max-width: ${breakpoints.sm}px) 100vw, 768px`, // full container width +} + type WidthSize = keyof typeof widthClasses function isWidthSize(size: string): size is WidthSize { @@ -50,8 +62,7 @@ export const InlineMediaComponent = ({ } else if (isWidthSize(resolvedSize)) { sizeClass = widthClasses[resolvedSize] imgSizeClass = 'w-full h-auto' - // Approximate sizes hint for responsive images - sizes = `${resolvedSize}vw` + sizes = sizesForWidth[resolvedSize] ?? '100vw' } else if (isFixedHeight) { imgSizeClass = 'h-full w-auto' sizes = '96px' diff --git a/src/blocks/Media/Component.tsx b/src/blocks/Media/Component.tsx index fe16d260..a9916421 100644 --- a/src/blocks/Media/Component.tsx +++ b/src/blocks/Media/Component.tsx @@ -36,14 +36,16 @@ export const MediaBlockComponent = (props: Props) => { const bgColorClass = `bg-${backgroundColor}` const textColor = getTextColorFromBgColor(backgroundColor) + // Container query breakpoints (@sm, @md, @lg) so sizing tracks the parent + // container width rather than the viewport, e.g. when embedded in a post layout. const getImageSizeClasses = () => { switch (imageSize) { case 'small': - return 'max-w-xs md:max-w-sm lg:max-w-md' + return 'max-w-xs @sm:max-w-sm @lg:max-w-md' case 'medium': - return 'max-w-sm md:max-w-lg lg:max-w-2xl' + return 'max-w-sm @sm:max-w-lg @lg:max-w-2xl' case 'large': - return 'max-w-md md:max-w-2xl lg:max-w-4xl' + return 'max-w-md @sm:max-w-2xl @lg:max-w-4xl' case 'full': return 'max-w-full' case 'original': @@ -52,16 +54,16 @@ export const MediaBlockComponent = (props: Props) => { } } - // sizes prop hints to browser what image width to request based on imageSize setting - // Uses breakpoints from cssVariables for consistency with other image components + // Hints the image width to request, using conservative estimates based on the + // container size the block actually occupies rather than the full viewport. const getSizesForImageSize = () => { switch (imageSize) { case 'small': - return `(max-width: ${breakpoints.md}px) 100vw, 384px` // max-w-sm = 24rem = 384px + return `(max-width: ${breakpoints.sm}px) 100vw, 384px` // small caps at max-w-md = 28rem = 448px case 'medium': - return `(max-width: ${breakpoints.md}px) 100vw, 672px` // max-w-2xl = 42rem = 672px + return `(max-width: ${breakpoints.sm}px) 100vw, 672px` // medium caps at max-w-2xl = 42rem = 672px case 'large': - return `(max-width: ${breakpoints.md}px) 100vw, 896px` // max-w-4xl = 56rem = 896px + return `(max-width: ${breakpoints.sm}px) 100vw, 896px` // large caps at max-w-4xl = 56rem = 896px case 'full': return '100vw' case 'original': @@ -75,6 +77,7 @@ export const MediaBlockComponent = (props: Props) => {