Acrylic
Components

Media Box

A measured media frame for images, video posters, and player overlays.

MediaBox is a reusable media frame. It measures the available column width and sizes a frame by kind: an image takes the media's natural aspect ratio (the cover fills it with no crop and no letterbox); a video snaps to a standard 16:9 (landscape) / 9:16 (portrait) frame by orientation and cover-crops the poster to it. It clips children to rounded corners and leaves playback controls to the caller through children.

It composes CardMedia: CardMedia owns the object-cover cover image plus the load retry-on-error and fallback, while MediaBox's job is to measure the media, pick the frame ratio, and bound the frame width by minWidth / maxWidth / maxHeight.

It does not own a video player. Compose an Artplayer, iframe, or custom controls in the overlay slot when the consuming app needs playback; a <video> child is clipped to the frame's rounded corners.

Installation

npx shadcn add https://acrylic-ui.vercel.app/r/media-box.json

Usage

min 240max 520
measuring…
import { MediaBox } from "@/components/acrylic/media-box"

<MediaBox
  kind="video"
  src={poster}
  naturalWidth={1280}
  naturalHeight={720}
  maxWidth={520}
>
  <div className="absolute inset-0">{/* player overlay */}</div>
</MediaBox>

Props

proptypedefaultdescription
kind"image" | "video""image"selects the default maxHeight
srcstringimage URL or video poster URL
naturalWidth / naturalHeightnumberknown intrinsic poster/image size; skips measurement (no reflow)
mediaSize{ width: number; height: number } | nullactual media size, preferred over poster size
minWidthnumberhard floor on frame width — wins over maxWidth / maxHeight on conflict
maxWidthnumbercap the frame width
maxHeightnumberimage: 510, video: 507cap frame height (reduces width to honor it)
videoMaxHeightnumberextra height ceiling applied only to kind="video" (clamped with maxHeight) — keep tall images while capping vertical videos to a compact height, still at natural ratio (no letterbox)
frameClassNamestringclasses for the rounded frame
imageClassNamestringclasses for the poster/image
fallbackReactNodeimage-off iconcustom broken-image fallback
onSizingChange(snapshot) => voidreports measured sizing details
maxRetriesnumber2failed loads to retry (exponential backoff) before showing fallback
retryDelayMsnumber800base backoff delay in ms; doubles each attempt

Plus native <div> props on the wrapper.

On this page