aspect-ratio
propertyKeeping a given aspect ratio in CSS has always been a challenge, so much so that it’s the origin for one of the most well-known memes, the CSS IS AWESOME overflown square. Well, the new aspect-ratio
property from the CSS box sizing level 4 module has recently been implemented in all major browsers, allowing us to get rid of old hacks and finally giving us perfect control on aspect ratios for any element.
For years we’ve been relying on a very creative but troublesome hack — the padding-top
percentage technique, that gets the job done but requires us to plan everything around it. The trick relies on a very obscure behavior of CSS, where vertical paddings (or technically, block-direction padding) are calculated based on the element’s width instead of its height when declared as a percentage, otherwise, they would go into a redundant loop of getting the element taller and taller. We could use this to keep an aspect ratio, simply applying a padding-top percentage calculated as height / width * 100
.
For instance, if we wanted a 1 to 1 aspect ratio (i.e. a square) we would simply declare the element’s padding-top: 100%
.
The typical image / video ratios of 4:3 and 16:9 could be achieved with padding-top: 75%
(3 / 4 * 100) and padding-top: 56.25%
(9 / 16 * 100) respectively.
This worked great for creating empty containers that we could use for decorative purposes. If we wanted to have a container with a background image that adjusted to the container size and kept aspect ratio, it would be enough to declare:
.block-element{ width: 100%; padding-top: 75% /* 4:3 aspect ratio */ background: url(/img/example.svg); background-size:cover; /* prevents cropping */ }
See the Pen
Aspect ratio for background images, old way by Facundo Corradini (@facundocorradini)
on CodePen.
But things get a little more complicated as soon as we start adding actual content to the box (for instance, a video), as the padding will be pushing our content down. That means we have to rely on abstracting the hack to a container and absolute positioning the content just to make our content ignore the padding. Putting it all together, the typical aspect ratio implementation for a 16:9 video will be:
.video-container{ position: relative; width: 100%; padding-top: 56.25%; } iframe[src*="https://www.youtube.com"] { position: absolute; top: 0; width: 100%; height: 100%; }
See the Pen
Keeping aspect ratios on videos, old way by Facundo Corradini (@facundocorradini)
on CodePen.
That’s great for a video and a 100% width container, but what if we wanted to keep an aspect ratio and have the container dynamically sized to its content? That’s where the hack escalates to disproportionate levels. If we used the absolute positioning technique, having too much content would put us at risk of awful overflows. If we wanted the container’s width to be automatically sized to the content, our absolute positioned element wouldn’t be able to make the container grow.
One solution to both of those is using the padding-top hack in a floated pseudo-element to control the aspect ratio, letting the container grow with our content if needed. The challenge then is getting the pseudo-element to render in a way that stays hidden from the user but still gets actual dimensions to affect the container.
This can be achieved in normal flow with a negative margin matching a 1px width as if we tried to use a zero-width element the browser would ignore it completely:
.container::before { content: ""; float: left; padding-top: 75%; /* 4 : 3 ratio*/ width: 1px; margin-left: -1px; } .container::after { /* classic clearfix */ content: ""; display: table; clear: both; }
See the Pen
Aspect ratio with content, old way by Facundo Corradini (@facundocorradini)
on CodePen.
In grid layout, we could potentially force the pseudo-element to share the column and row of the actual content:
.element{ grid-column:1; grid-row: 1; } .container::before{ content: ""; padding-top: 100%; /* 1:1 aspect ratio*/ grid-column:1; grid-row: 1; }
See the Pen
Keeping circles circular, old(ish) way by Facundo Corradini (@facundocorradini)
on CodePen.
aspect-ratio
propertyThe new, simpler way doesn’t require any of those hacks. It allows us to explicitly declare the desired aspect-ratio and the browser will work its magic to keep it.
A square is simply aspect-ratio: 1 / 1
, a 16:9 video gets aspect-ratio: 16 / 9
, etc.
As long as at least one of the dimensions is auto
, the browser will adjust the box size to keep the given aspect-ratio
(if we specify both height
and width
, aspect-ratio
will be ignored).
Better yet, an element with a declared aspect-ratio
will try to keep it, but expand to prevent overflows if needed, which saves us from using absolute positioning and the pseudo-element hack.
This allows us to achieve the old tricks in a much simpler fashion but also opens the door to unlimited new possibilities.
aspect-ratio
for art directionOne neat trick we can use with the new property is changing an image aspect-ratio in media queries. We may want an image to go 16:9 when in landscape mode, but keep a 1:1 format when it’s in portrait.
Of course, using a multiple image source set with the <picture>
element would be the preferred way, but we don’t always have that luxury.
.ratiod-image { aspect-ratio: 1/1; object-fit: cover; width: 75vw; } @media (orientation: landscape) { .ratiod-image { aspect-ratio: 16/9; } }
See the Pen
Aspect ratio for art direction by Facundo Corradini (@facundocorradini)
on CodePen.
A neat thing about this is that future implementations will even allow for interpolation of the aspect-ratio property, so it will be able to transition smoothly from one to the other!
aspect-ratio
of videos without an auxiliary containerOne of my favorite applications for the new property is keeping the aspect ratio of embedded videos without having to rely on absolute positioning or the padding-top hack, therefore keeping the markup free of unnecessary containers.
To do so, we may simply declare the width as whatever we want (most likely, 100%
), use the aspect-ratio property to keep the relation between width and height and set the height
to unset
to prevent the height stated in the HTML from taking over:
iframe[src*="https://www.youtube.com"] { /* targets any iframe from YouTube*/ width: 100%; /* elongates the iframe to the container's width */ height: unset; /* ignore the height attribute from the HTML */ aspect-ratio: 16/9; /* adjust the height to keep aspect-ratio*/ }
See the Pen
Aspect-ratio on videos without container or hacks by Facundo Corradini (@facundocorradini)
on CodePen.
One of my favorite aspects of CSS grid is the autofit/minmax combo, which allows our grid to have as many columns of a determined minimum width as can fit in the container, but also have them evenly grow to distribute the extra space.
But usually, our rows would keep the predetermined height, turning what started as squares into rectangles. Now we can automatically adjust the row sizes according to the columns’ dynamic width!
We just have to define an aspect-ratio
for the grid items and the browser will work its magic:
.grid{ display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); } .grid > *{ aspect-ratio: 1 / 1; }
See the Pen
Keeping aspect-ratio in a CSS Grid with dynamic-sized columns by Facundo Corradini (@facundocorradini)
on CodePen.
We can argue that the issue in the meme is one of over-definition. The overflow is a design feature of CSS, not a bug, as it prioritizes preventing the loss of content. The supposed author has over-defined the interface, giving it a fixed width and a fixed height, and big letters that simply don’t fit, and that’s why it overflows.
There are several possible ways to fix it — for instance, we could let it grow wider by just changing from a fixed width to a min-content, let it grow taller if we hadn’t defined the height, allowing for scrollbars with overflow: auto
or scroll
, etc.
But I’ll assume the intention is to keep the square… square, and let the box grow as needed to fit whatever text we throw-in.
Well, with aspect-ratio, that’s easy:
.grid{ display:grid; /* minimum width is 120px, max autogrows */ grid-template-columns: minmax(120px, auto); /* square aspect ratio */ aspect-ratio: 1 / 1; align-items: center; }
See the Pen
CSS ASPECT-RATIO IS AWESOME by Facundo Corradini (@facundocorradini)
on CodePen.
Even though at first glance, aspect-ratio
can look like an alternate spelling for the old aspect-ratio hack, as we just saw it’s much more than that! It helps us simplify the HTML, get rid of containers, explicitly declare the intentions on the CSS making it easier to maintain, and achieve stuff that was extremely hard or even impossible before. A great new addition to our CSS toolbox!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.