I've been trying to style a NextJS image with rounded corners whilst also growing to fit it's containing div (Blue on image, difficult to see but there) and maintaining aspect ratio (unknown until runtime). All that is broken with what I currently have, is the image not getting the border-radius
but the box surrounding it does (Black on image). I cannot find a way to get the border radius to work without hard coding the image size, as that must be dynamic. The only other vector to consider is that this is all contained inside another fixed
positioned div (Red on image) that contains the whole popup.
I have tried the below from sugguestions in other threads and it almost works, the only issue I have found is that the image does not recieve the rounded corners due to it's box being larger than the content and thus rounding that box and not the image.
{/* Card that shows on click */}
<div className='fixed z-10 w-full h-full left-0 top-0 invisible bg-black/[.6]' id={'hidden-card-' + title} onClick={hideEnlargement}>
<div className='w-[80%] h-[80%] translate-x-[calc(50vw-50%)] translate-y-[calc(50vh-60%)] rounded-2xl'>
<Image
src={img}
alt={alt}
quality={100}
className="rounded-2xl bg-black m-auto"
fill={true}
style={{"objectFit" : "contain"}}
/>
</div>
</div>
A couple of tweaks will get this to work:
w-full h-full left-0 top-0
with inset-0
for brevity. The inset-0 class is the same as setting top, right, bottom, left all to 0.h-3/4
and w-3/4
which is 75% width and height.<div
className="fixed inset-0 z-10 bg-black/[.6]"
id={'hidden-card-' + title}
onClick={hideEnlargement}
>
<div className="rounded-2xl overflow-hidden h-3/4 w-3/4 m-auto translate-y-[calc(50vh-50%)]">
<Image
src={img}
alt={alt}
quality={100}
className="bg-black w-full h-full object-cover"
fill={true}
/>
</div>
</div>
To avoid cropping but still have the images appear with rounded corners and preserve their intrinsic aspect-ratio, you have to pass the width and height of the images to the Next/Image component. I can't think of a way to do it without because if you don't pass the width and height to the Next/Image component, it requires that you set the fill
prop to true.
When the fill prop is set to true it takes the image out of the document flow by setting it to be absolutely positioned. As a result, you lose the ability to do something like set the width of the image to 100% and the height to auto and just allow the browser to determine the appropriate dimensions based on the natural width/height of the image.
So if you store the natural/desired height and width of the images with the image details, you can then avoid using the fill prop.
Your modal could then look like this:
// assumes you pass props with an img object containing
// src, alt, width and height properties
<div
className="fixed inset-0 z-10 bg-black/[.6] grid place-content-center"
onClick={closeModal}
>
<Image
src={img.src}
alt={img.alt}
width={img.width}
height={img.height}
quality={100}
className="max-w-[95vw] max-h-[95vh] rounded-2xl animate-bounceIn"
/>
</div>
You can see that since the image has height and width, you can use grid place-content-center
to center everything without any transform or margins required. Also, you don't need the extra wrapping div. The image itself will be its natural height and width, but to make sure that it doesn't overflow the viewport, you can set the max-width and max-height of the image. Of course, this is where the problem comes in with the width and height being set on the image. Once the max-height or max-width is applied, it causes the image to be out of proportion. Setting object-fit to contain works, but you'll lose the rounded corners.
You could instead optimize your images manually and just use an img
tag or picture
tag.
In the new Stackblitz demo there are multiple images of varying heights and widths. You can see the structure of the data in the pages/api/images.js
file. The new "modal" component can be found in the `pages/components/Modal.js file.