AspectRatio
Constrains content to a specific width/height ratio using CSS aspect-ratio. Seven named presets, arbitrary string or numeric ratios, eight corner-radius presets, automatic object-fit on single media children, and polymorphic render.
Playground
Installation
pnpm add @tessinaui/uiUsage
import { AspectRatio } from "@tessinaui/ui";{/* Named preset */}
<AspectRatio ratio="video" rounded="md">
<img src="/hero.jpg" alt="" />
</AspectRatio>
{/* Custom numeric ratio */}
<AspectRatio ratio={21 / 9}>
<img src="/panorama.jpg" alt="" />
</AspectRatio>
{/* Arbitrary CSS string */}
<AspectRatio ratio="2.35 / 1">
<video src="/cinemascope.mp4" />
</AspectRatio>
{/* Object fit on single media children */}
<AspectRatio ratio="square" objectFit="cover">
<img src="/avatar.jpg" alt="" />
</AspectRatio>
{/* Non-media content */}
<AspectRatio ratio="video" rounded="lg">
<div className="grid place-items-center bg-primary-light">
Placeholder
</div>
</AspectRatio>
{/* Polymorphic — render as a figure */}
<AspectRatio ratio="portrait" render={<figure />}>
<img src="/photo.jpg" alt="" />
</AspectRatio>Showcase
Named ratios
Seven opinionated presets cover most common cases.
| Preset | Ratio | Typical use |
|---|---|---|
"square" | 1 / 1 | Avatars, profile tiles |
"video" (default) | 16 / 9 | Video thumbnails, hero banners |
"portrait" | 3 / 4 | Phone-oriented photos |
"landscape" | 4 / 3 | Classic photography |
"ultrawide" | 21 / 9 | Panoramas, cinematic headers |
"vertical" | 9 / 16 | Stories, TikTok, Reels |
"golden" | 1.618 / 1 | Editorial layouts |
Custom ratios
Pass a CSS-valid string or a number.
<AspectRatio ratio="7 / 5">…</AspectRatio>
<AspectRatio ratio={5 / 4}>…</AspectRatio>
<AspectRatio ratio="2.35 / 1">…</AspectRatio>Under the hood this writes to style.aspectRatio, so anything the CSS aspect-ratio property accepts is valid.
Rounded
Corner-radius presets. When set to anything other than "none", overflow-hidden is added automatically so clipped children match the rounded corners.
| Value | Tailwind |
|---|---|
"none" (default) | — |
"sm" | rounded-sm |
"md" | rounded-md |
"lg" | rounded-lg |
"xl" | rounded-xl |
"2xl" | rounded-2xl |
"3xl" | rounded-3xl |
"full" | rounded-full |
Object fit
Single <img> / <video> / <iframe> / <picture> children automatically receive block size-full, and objectFit maps to the corresponding Tailwind object-* class.
| Value | Tailwind |
|---|---|
"cover" | object-cover |
"contain" | object-contain |
"fill" | object-fill |
"none" | object-none |
"scale-down" | object-scale-down |
Non-media children are absolutely positioned with inset-0 size-full.
Polymorphic render
Replace the default <div> with a semantic element.
<AspectRatio ratio="video" render={<figure />}>
<img src="/photo.jpg" alt="" />
</AspectRatio>Accessibility
- AspectRatio is a structural primitive — it applies no ARIA roles.
- Always provide
alttext on<img>children — the wrapper does not carry any accessible name. - Use
render={<figure />}+ a<figcaption>sibling when the content benefits from a semantic caption.
API Reference
AspectRatio Props
| Prop | Type | Default | Description |
|---|---|---|---|
ratio | "square" | "video" | "portrait" | "landscape" | "ultrawide" | "vertical" | "golden" or string or number | "video" | Target aspect ratio |
rounded | "none" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full" | "none" | Corner radius — auto-applies overflow-hidden when non-none |
objectFit | "cover" | "contain" | "fill" | "none" | "scale-down" | — | Applied to single media children as object-* |
render | ReactElement | — | Polymorphic target |
className | string | — | Extra classes |
style | CSSProperties | — | Custom styles — merged with aspect-ratio |
Notes
- Single-child convention. When you pass exactly one
<img>/<video>/<iframe>/<picture>child it receivesblock size-full. Any other child (including multiple) is wrapped withabsolute inset-0 size-full. - No hooks. Pure wrapper, safe for SSR.
- Browser support. CSS
aspect-ratioworks in all modern browsers (Safari 15+, Chrome 88+, Firefox 89+).
Grid
A CSS-grid layout primitive with static and responsive column counts, gap presets, flow control, and per-cell placement via GridItem. Polymorphic render, RTL, and no hard-coded tokens.
Spacer
A decorative gap-filler. Three axis modes, eight sizes, optional grow-to-fill behaviour, and polymorphic render. Accessibility-hidden by default — screen readers skip it.