Understanding and setting aspect ratios
Every element rendered to the page has a height and a width, and, therefore, an aspect ratio, which is the ratio between the width and height. The natural dimensions of a media object, which are its size without any sizing, scaling, zooming, or borders applied, are known as its natural or intrinsic size. An element's intrinsic size is determined by the element itself, not by applying formatting such as box sizing or setting border, margin, or padding widths.
When developing sites, you often want to be able to set the width of an element to a percentage of the viewport or parent container size and have the height change size proportionally, thereby maintaining a specific aspect ratio depending on the size of the viewport. For replaced elements, like images and videos, maintaining a specific aspect ratio is not only necessary for creating responsive web design, but also a vital component of providing good user experience. Setting an asset's aspect ratio prevents loading jank—the layout shift that occurs when media loads after the page has already been painted, causing a reflow because the space for the asset has not been reserved.
Using CSS, you can adjust the size of replaced and non-replaced elements based on their aspect ratio. In this guide, we will learn about the aspect-ratio
property, discuss aspect ratios for replaced and non-replaced elements, and then examine some common aspect ratio use cases.
How the aspect-ratio
property works
The CSS aspect-ratio
property value defines the preferred width-to-height ratio of an element's box. The value is either a <ratio>
, the keyword auto
, or a space-separated combination of both.
The <ratio>
is the ratio of the width and height, in that order. It is represented by two positive <number>
values separated by a forward slash (/
) or a single <number>
. When a single number is used, it is the same as writing the ratio as <number> / 1
, which is also the width divided by the height.
The following values are all equivalent:
aspect-ratio: 3 / 6;
aspect-ratio: 1 / 2;
aspect-ratio: 0.5 / 1;
aspect-ratio: 0.5;
The following values are also all equivalent:
aspect-ratio: 9/6;
aspect-ratio: 3/2;
aspect-ratio: 1.5;
The effect of the auto
keyword depends on whether the element on which it is applied is a replaced element or not. For replaced elements with an intrinsic aspect ratio, auto
means the intrinsic aspect ratio should be used. In all other instances, the auto
value means the box has no preferred aspect ratio. In both cases, this is the default behavior as if no aspect-ratio
property were applied.
When the value contains both the auto
keyword and a <ratio>
value, such as aspect-ratio: auto 2 / 3;
or aspect-ratio: 0.75 auto;
, the auto
value is applied to replaced elements with a natural aspect ratio and the specified ratio of the width / height
or <number>
is used as the preferred aspect ratio.
You will have noticed the word "preferred" in the definitions above. The aspect-ratio
value is not always applied when set. The aspect-ratio
property sets a "preferred" aspect ratio, so only ever has an effect if at least one of the box's sizes is automatic.
When both the height and width or inline and block sizes are explicitly set, the aspect-ratio
property value is ignored. In this case, no dimension is allowed to be automatically sized - the preferred sizes are explicitly set - so the aspect-ratio
property has no effect. When you declare both the inline and block dimensions, those take precedence.
With replaced elements, if you don't explicitly set a value (other than auto
) to either dimension, both will default to their intrinsic size (any aspect-ratio
value isn't applied). The aspect-ratio
will apply to non-replaced elements that don't have a dimension explicitly set, as non-replaced elements are either intrinsically or extrinsically sized, getting their size from their content, container, box model properties, etc.
When an element is rendered to the page, if no CSS is applied and no HTML sizing attributes are included, the user agent will render the object at its natural size.
Adjusting aspect ratios of replaced elements
Replaced elements like <img>
and <video>
are replaced with media that have set dimensions and, therefore, an intrinsic aspect ratio. Consider a raster image, such as a JPEG, PNG, or GIF. If you place an image on a page and do not set a height or width, either via <img>
attributes or with CSS, it will be displayed at its intrinsic size.
This is a 220px
square image with no CSS applied; it is displayed at its intrinsic or default size.
If replaced content is auto-sized or you provide a size for only one dimension, such as setting a value for width
, the browser will automatically resize the other dimension, in this case, the height, while maintaining the media's original aspect ratio.
In this example, only the width
is set on the image, so the user agent preserves its aspect ratio. The same image is repeated three times, displayed at different widths: 55px
, 110px
, and at its natural size of 220px
via the width: auto
value.
Only when you provide sizes for both dimensions is there a risk of distorting the replaced element. For example, setting width: 100vw;
and height: 100vh;
on an image creates a variable aspect ratio; the image will appear either stretched or squashed when the viewport's aspect ratio differs from the image's natural aspect ratio.
In this example, the same image is repeated three times, explicitly sized with the same height
value (110px
) but different width
values (55px
, 110px
, and 220px
).
We have distorted the images intentionally by setting both a height
and width
: we've squashed the first one and stretched the third.
We could have created this same distorted effect using the CSS aspect-ratio
property, by setting a single dimension (not both or neither) and providing a value other than 1
(or 1 / 1
). You likely don't want to do this, but it's good to know that it's possible.
img {
height: 100vh;
aspect-ratio: 3;
}
We have declared a single dimension; 100vh
is the full height of the example <iframe>
viewport. For aspect-ratio
to apply to replaced elements, only one dimension must be set. Setting both or neither doesn't work.
Fitting replaced elements within their containers
To fit a replaced element to the dimensions of its container while maintaining its intrinsic aspect ratio, set the object-fit
property value to cover
or contain
. This will resize the replaced element and either clip it to "cover" the container or display it at a smaller size, fully "contained" within it.
In this example, the square image is placed into a grid of three items, each with an aspect ratio of 5 / 2
.
To begin with, we create a container with three items, each containing one image:
<div class="grid">
<div>
<img
src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
alt="Pride flag" />
</div>
<div>
<img
class="cover"
src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
alt="Pride flag" />
</div>
<div>
<img
class="contain"
src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
alt="Pride flag" />
</div>
</div>
Next, we designate the container as a grid, where each item has an aspect ratio of 2.5
(5/2
) with a minimum width of 150px
. Therefore, the minimum height will be 60px
. However, the final width and height are determined by the width of the example's iframe, which will be based on your viewport size:
.grid {
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
font-size: 0; /* to minimize whitespace */
}
div div {
aspect-ratio: 5 / 2;
background-color: #ccc;
}
We then size the images and set the object-fit
property on the last two images:
img {
height: 100%;
width: 100%;
}
.cover {
object-fit: cover;
}
.contain {
object-fit: contain;
}
Only the first image is distorted (stretched). We could have used the fill
value of object-fit
to create the same effect. The cover
image spans the full width of the container, centered vertically, and clipped to fit in the container. The contain
value ensures the image is contained within the container, centered horizontally, and shrunk to fit.
Defining aspect ratios for non-replaced elements
While the aspect ratio of a replaced element is maintained by default, adjusting the intrinsic size of a non-replaced element usually changes its aspect ratio. For example, identical content may appear as three lines on a widescreen or in a wide parent container, but as eight lines on a narrow screen or container.
In this example, the same quote is displayed in 200px
and 600px
wide containers, and a square is set with a height matching its 200px
width:
To highlight the issue with setting a non-replaced element's aspect ratio via size dimensions, toggle the overflow
property between auto
and visible
.
blockquote {
width: 200px;
}
blockquote:nth-of-type(2) {
width: 600px;
}
blockquote:nth-of-type(3) {
height: 200px;
}
While it's possible to define an aspect ratio on non-replaced elements by setting both the dimensions and hiding overflowing content, the CSS aspect-ratio
property provides explicit aspect ratio support. This means a specific aspect ratio can be set even when you don't know the content or screen sizes.
In the next example, we render square boxes regardless of the width of the text by setting aspect ratio: 1
on <blockquote>
, a non-replaced element:
blockquote {
inline-size: max-content;
aspect-ratio: 1;
}
Each box has one dimension defined: the inline-size
, which is the width in horizontal languages, is set to max-content
, which sets the size to be as wide as it needs to be to fit the content without wrapping. The second dimension, in this case, the block-size
or height
, is set to be the same length as the first dimension. This is accomplished with the aspect-ratio
property. We defined the desired width-to-height ratio of the element's box to be 1
, which is the same as 1 / 1
, a square. This sets the block direction to match the width of the element, without using the height
or block-size
properties.
In these examples, a size was explicitly set on the element itself. When working with non-replaced elements, aspect ratio comes into play when no size dimension is explicitly set.
Creating a circle based on the container size
The inline-size of non-replaced block-level elements is the size of their container's content box. Because they have a size by default, they don't need to have an explicit size set for the aspect-ratio
property to work.
In this example, we have a container <div>
that is 200px
wide, which includes 5px
of padding on each side. Therefore, the inline-size of the content box is 190px
. Without setting a height or width on the nested <p>
element, we know its inline-size is 190px
. With aspect-ratio: 1
set, the paragraph will be 190px
tall, unless it has visible overflowing content causing it to be taller (which it doesn't).
The height of the <div>
element is not explicitly set, but it contains the 190px
tall paragraph, the 5px
of padding on top and bottom, and the combined heights of the default top and bottom margins of <p>
. As a result, it is taller than it is wide. Both elements have a border-radius
of 50%
, so the container is an oval while the child, with an aspect-ratio
of 1
but no inline or block sizing explicitly defined, is a circle.
<div><p>Hello world</p></div>
div,
p {
border-radius: 50%;
}
div {
width: 200px;
padding: 5px;
background-color: #66ccff;
}
p {
aspect-ratio: 1;
text-align: center;
border: 10px solid #ffffff;
background-color: #f4aab9;
}
To make the <div>
a circle, we can set the height
and width
to the same value, or set aspect-ratio: 1
and set the overflow
to auto
or hidden
. Alternatively, we can simply remove the margins on the paragraph with margin-block: 0
. Both these options are shown below.
<section>
<div><p>Hello world</p></div>
<div><p>Hello world</p></div>
<section></section>
</section>
div,
p {
aspect-ratio: 1;
border-radius: 50%;
}
div:first-of-type {
overflow: hidden;
}
div:last-of-type p {
margin-block: 0;
}
Common aspect-ratio
use cases
Let's look at a few situations where you can use aspect-ratio
to address some common challenges while creating responsive designs.
Making external assets responsive
All content should be responsive, even when that content is third-party embeds, such as videos from TikTok, YouTube, or Instagram. The code snippet you include to embed these external videos generally creates an <iframe>
.
While a <video>
element typically adopts the aspect ratio of its media file, iframe
elements lack this capability. This poses the challenge of ensuring that the <iframe>
is responsive while always maintaining the aspect ratio of the video it contains. One of the techniques we can use is to set the iframe's width to 100%
of its container or 100vw
to match the viewport width regardless of the viewport's size. However, setting a fixed height might stretch or squash the video. Instead, we set the aspect-ratio
on the video's container, aligning it to be the same aspect ratio as the video. Problem solved!
For context, the standard aspect ratio of YouTube videos is 16:9 when viewed on a desktop computer or laptop, while TikTok and Instagram videos have a 9:16 aspect ratio.
.youtube {
aspect-ratio: 16/9;
}
.instagram,
.tiktok {
aspect-ratio: 9/16;
}
We can use the aspect-ratio
feature within the @media
query alongside the aspect-ratio
property to adjust the size of both the iframe and the video it contains. This ensures that the video content is always as large as possible - taking up either the full width or height of the viewport, regardless of the viewport size - while maintaining a specific aspect ratio.
We can set the landscape-oriented YouTube videos to be as wide as the viewport and the portrait-oriented TitTok and Instagram video iframes to be as tall as the viewport. If a viewport's aspect ratio is wider than 16:9, we set the YouTube video to be the height of the viewport. If the viewport is narrower than 9:16, we set both Instagram and TikTok videos to the width of the viewport.
iframe.youtube {
aspect-ratio: 16/9;
width: 100vw;
height: auto;
}
iframe.instagram,
iframe.tiktok {
aspect-ratio: 9/16;
height: 100vh;
width: auto;
}
/* If the viewport is very wide but not very tall */
@media (aspect-ratio > 16 / 9) {
iframe.youtube {
width: auto;
height: 100vh;
}
}
/* If the viewport is very tall but not very wide */
@media (aspect-ratio < 9 / 16) {
iframe.instagram,
iframe.tiktok {
height: auto;
width: 100vw;
}
}
Making grid cells square
A grid of square cells can be created by defining fixed column track sizes, ensuring each row matches the size of the column track. However, when creating responsive grids using auto-fill
to fit as many column tracks as possible within the container, the width of each item becomes uncertain. This makes it challenging to determine the appropriate height for creating square items.
By setting an aspect ratio on the items, we can ensure when the grid items are laid out, each grid item will be as tall as it is wide, creating square grid items regardless of the container's dimensions.
In this example of square grid items, the grid tracks are auto-sized, taking their size from the items. Each item will be at least 95px
wide but could be much wider. No matter the width, each item will be a square, with the height determined by the aspect-ratio
to match its width.
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(95px, 1fr));
}
.item {
aspect-ratio: 1;
}
For the content of a grid item to not grow beyond the preferred height set by the aspect-ratio
, set the min-height
to 0
and the overflow
to a value other than visible
. This will work for intrinsically sized content. If you have content that is intrinsically larger than the available space, set that content to not be larger than the grid item by setting the max-height
(or max-width
, depending on the content) to 100%
.
.item {
min-height: 0;
overflow: auto;
}
.item > * {
max-height: 100%;
}
See also
- CSS box sizing module