Skip to main content

Design system components

AspectRatio

Layout primitive that locks its children to a fixed width/height ratio (1:1, 4:3, 16:9, …) so media-shaped containers reserve space before content loads.

Status: beta. Group: structure. Markdown version: /design-system/components/aspect-ratio.md

When to use

AspectRatio reserves a proportional box and absolutely fills it with its child, clipping overflow. Wrap media; images, video embeds, map iframes; and let the child fill (width/height 100%, object-fit cover). Because the box takes its height from the ratio, it also works as a CLS-free placeholder while media loads. 16:9 is the default; 1:1 suits avatars and thumbnails.

When not to use

  • Do not use this as a generic flex/grid item; pick a layout primitive instead.
  • Do not add padding or margin via className; children will overlap their own padding because the wrapper is position: relative.

Look-alikes

The contract names no look-alike with a deciding condition.

Props

AspectRatio props
PropTypeDefaultDescriptionRequired
childrenReactNodenoneMedia or placeholder content inside the ratio box.Yes
ratio"1:1" | "4:3" | "16:9" | "21:9" | "3:2" | "2:3"16:9Width-to-height ratio token.No
classNamestringnoneAdditional CSS class names.No

Variants

  • Allowed ratio values: 1:1, 4:3, 16:9, 21:9, 3:2, 2:3 (default 16:9).

Invalid combinations

The contract records no invalid combinations.

Keyboard

The contract records no keyboard interaction.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

AspectRatio rules
LevelRuleHow it is checked
MUSTARIA: No own ARIA; the wrapper inherits semantics from its children. Author the inside as a meaningful region (img, figure, iframe) yourself.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-05-28.
SHOULDMake the child fill: width/height 100% and object-fit cover for images.Not stated in the contract.
SHOULDUse 16:9 for video, 1:1 for square thumbnails, 3:2 for editorial photography.Not stated in the contract.
SHOULDWrap lazy media in it so the page reserves space up front (no layout shift on load).Not stated in the contract.
SHOULDCombine with Skeleton inside for a loading placeholder at the final footprint.Not stated in the contract.
SHOULD NOTDo not put text layouts inside; overflow hidden clips anything the ratio cannot fit.Not stated in the contract.
SHOULD NOTDo not nest ratio boxes; one box defines the frame, the child just fills it.Not stated in the contract.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { AspectRatio } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/layout exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.
  • Also available as the custom element <dt-aspect-ratio>; see the custom elements manifest.