fix(media): rework portrait image sizing to fill+object-contain
Chasing this through width/height class combinations (w-fit/h-fit vs w-auto/h-auto) kept trading one bug for another: centering broke height, fixing height broke centering. Root cause: max-h-full/max-w-full on the image are percentages, and every wrapper in the chain only ever had an intrinsic (auto/fit-content) size — CSS treats percentages against an intrinsically-sized parent as unresolvable, so which axis "won" depended on incidental flex-stretch behavior rather than anything deliberate. Replaced the shrink-wrap-to-image approach with fill-the-definite-parent: wrapper and image now take w-full h-full of the already definitely-sized media column (PostDetailDialog) / full-screen overlay (StoryViewer), and object-contain (default centered) handles aspect-ratio scaling entirely on its own — no percentage resolution against an ambiguous parent left anywhere in the chain. Not visually verified locally (auth-gated route) — needs a real check against a portrait post/story after deploy.
This commit is contained in:
1 parent
9efc207710
commit
23722f8b6d
2 files changed
+26
-15
No files matched your search
@@ -243,6 +243,14 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
|
||||
</>
|
||||
);
|
||||
|
||||
// Image wrappers below fill the (definitely-sized) media column and let
|
||||
// object-contain handle aspect-ratio scaling/centering. An earlier
|
||||
// "shrink-wrap to the image's own rendered box" approach relied on
|
||||
// percentage max-height/max-width resolving against an intrinsically-sized
|
||||
// (auto/fit-content) parent, which CSS treats as unresolvable — portrait
|
||||
// images ended up either uncentered or rendered at full natural size
|
||||
// (clipped) depending on which axis lost. Filling a definite-size parent
|
||||
// avoids that class of bug entirely.
|
||||
const mediaContent = post.type === "VIDEO" && post.videoPost ? (
|
||||
<div className="relative h-full w-full">
|
||||
<VideoCard
|
||||
@@ -259,13 +267,13 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
|
||||
<Carousel setApi={setCarouselApi} className="h-full">
|
||||
<CarouselContent className="h-full">
|
||||
{images.map((img) => (
|
||||
<CarouselItem key={img.id} className="basis-full flex items-center justify-center h-full">
|
||||
<CarouselItem key={img.id} className="basis-full h-full">
|
||||
<BlurImage
|
||||
src={getMediaUrl(img.storageKey) ?? ""}
|
||||
blurhash={img.blurhash}
|
||||
alt={currentCaption ?? `${pet.name}'s post`}
|
||||
className="block max-h-full max-w-full object-contain"
|
||||
wrapperClassName="relative block h-auto w-fit"
|
||||
className="block w-full h-full object-contain"
|
||||
wrapperClassName="relative block w-full h-full"
|
||||
/>
|
||||
</CarouselItem>
|
||||
))}
|
||||
@@ -288,13 +296,13 @@ export function PostDetailDialog({ post, open, onClose, onDeleted }: PostDetailD
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative inline-flex w-fit max-h-full max-w-full">
|
||||
<div className="relative w-full h-full">
|
||||
<BlurImage
|
||||
src={getMediaUrl(images[0].storageKey) ?? ""}
|
||||
blurhash={images[0].blurhash}
|
||||
alt={currentCaption ?? `${pet.name}'s post`}
|
||||
className="block max-h-full max-w-full object-contain"
|
||||
wrapperClassName="relative block h-auto w-fit"
|
||||
className="block w-full h-full object-contain"
|
||||
wrapperClassName="relative block w-full h-full"
|
||||
/>
|
||||
<AiDisclosureBadge value={post.aiDisclosure} className="h-8" />
|
||||
</div>
|
||||
|
||||
@@ -155,20 +155,23 @@ export function StoryViewer({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Story image — inline-flex wrapper shrinks to the actual rendered
|
||||
image box (not the full-screen overlay) so both the centering
|
||||
and the badge overlay track the real image bounds. BlurImage's
|
||||
own wrapper defaults to w-full/h-full (fine for fill-container
|
||||
usages like feed thumbnails), so it's overridden here via
|
||||
wrapperClassName. */}
|
||||
{/* Story image — wrapper fills the full-screen overlay and
|
||||
object-contain does all the aspect-ratio scaling/centering
|
||||
itself. A previous "shrink-wrap to the image's own rendered
|
||||
box" approach relied on percentage max-height/max-width
|
||||
resolving against an intrinsically-sized (auto/fit-content)
|
||||
parent, which CSS treats as unresolvable — portrait images
|
||||
would render at full natural size (clipped) or lose centering
|
||||
depending on which axis' ambiguity won. Filling a definite-size
|
||||
parent sidesteps that class of bug entirely. */}
|
||||
{mediaUrl && (
|
||||
<div className="relative inline-flex w-fit max-h-screen max-w-full">
|
||||
<div className="relative w-full h-full">
|
||||
<BlurImage
|
||||
src={mediaUrl}
|
||||
blurhash={currentStory.blurhash}
|
||||
alt={t("storyAriaLabel", { petName })}
|
||||
className="block max-h-screen max-w-full object-contain"
|
||||
wrapperClassName="relative block h-auto w-fit"
|
||||
className="block w-full h-full object-contain"
|
||||
wrapperClassName="relative block w-full h-full"
|
||||
/>
|
||||
<AiDisclosureBadge
|
||||
value={currentStory.aiDisclosure}
|
||||
|
||||
Reference in new issue
Block a user