fix(stories): size the video player box to the real aspect ratio

The player box was full-bleed (w-full h-full) with object-fit: contain
handling the letterboxing internally — visually correct but the box
itself still spanned the whole screen, so the black letterbox area
read as part of the player rather than just backdrop. Sizes the box
itself to the story's aspectRatio (defaulting to 9:16 before Mux
reports the real value), centered via flex — same visual result, but
now only the actual content area is the "box".
This commit is contained in:
admin committed 2026-08-16 14:47:34 +02:00
1 parent 8186c36b9a
commit 07c0f82293
1 file changed
+51 -36
+51 -36
View File
@@ -224,43 +224,58 @@ export function StoryViewer({
normal feed videos) — object-contain here matters even more:
no fixed aspect-video box like VideoCard uses. */}
{isVideo ? (
<div className="relative w-full h-full">
{videoStatus === "READY" && videoPlaybackId ? (
<MuxPlayer
playbackId={videoPlaybackId}
streamType="on-demand"
autoPlay
muted={isMuted}
style={{ width: "100%", height: "100%", objectFit: "contain" }}
onTimeUpdate={handleVideoTimeUpdate}
onEnded={handleVideoEnded}
// The player BOX itself is sized to the video's real aspect
// ratio (default 9:16 — most story videos are phone-shot
// portrait — until Mux reports the actual value), not just the
// content inside a full-bleed box. object-fit: contain on a
// full-screen box still leaves a full-screen click/visual
// target with the letterboxing baked into the box; sizing the
// box itself means the black is purely the backdrop showing
// through, not part of the box.
<div className="relative w-full h-full flex items-center justify-center">
<div
className="relative h-full w-auto max-w-full"
style={{
aspectRatio: (currentStory.aspectRatio ?? "9:16").replace(":", " / "),
}}
>
{videoStatus === "READY" && videoPlaybackId ? (
<MuxPlayer
playbackId={videoPlaybackId}
streamType="on-demand"
autoPlay
muted={isMuted}
style={{ width: "100%", height: "100%" }}
onTimeUpdate={handleVideoTimeUpdate}
onEnded={handleVideoEnded}
/>
) : videoStatus === "ERROR" ? (
<div className="flex h-full w-full items-center justify-center text-white/70 text-sm">
{t("videoError")}
</div>
) : (
<div className="flex h-full w-full items-center justify-center">
<Loader2 className="h-8 w-8 text-white animate-spin" />
</div>
)}
{videoStatus === "READY" && (
<button
className="absolute bottom-3 right-3 flex h-10 w-10 items-center justify-center rounded-full bg-black/40 text-white hover:bg-black/60"
onClick={() => setIsMuted((m) => !m)}
aria-label={isMuted ? t("unmute") : t("mute")}
>
{isMuted ? (
<VolumeX className="h-4 w-4" aria-hidden="true" />
) : (
<Volume2 className="h-4 w-4" aria-hidden="true" />
)}
</button>
)}
<AiDisclosureBadge
value={currentStory.aiDisclosure}
className="top-auto left-auto bottom-3 right-16 h-10"
/>
) : videoStatus === "ERROR" ? (
<div className="flex h-full w-full items-center justify-center text-white/70 text-sm">
{t("videoError")}
</div>
) : (
<div className="flex h-full w-full items-center justify-center">
<Loader2 className="h-8 w-8 text-white animate-spin" />
</div>
)}
{videoStatus === "READY" && (
<button
className="absolute bottom-3 right-3 flex h-10 w-10 items-center justify-center rounded-full bg-black/40 text-white hover:bg-black/60"
onClick={() => setIsMuted((m) => !m)}
aria-label={isMuted ? t("unmute") : t("mute")}
>
{isMuted ? (
<VolumeX className="h-4 w-4" aria-hidden="true" />
) : (
<Volume2 className="h-4 w-4" aria-hidden="true" />
)}
</button>
)}
<AiDisclosureBadge
value={currentStory.aiDisclosure}
className="top-auto left-auto bottom-3 right-16 h-10"
/>
</div>
</div>
) : (
mediaUrl && (