CSS Aspect Ratio

If you’ve ever tried to make an image, video, or “box” stay perfectly proportional while the page resizes… welcome to the club. Aspect ratio is the thing that stops your “perfect square” from becoming a sad rectangle.

In this tutorial you’ll learn the modern CSS way to control aspect ratio, how it interacts with width/height, how to make media crop nicely, and a classic fallback technique for older setups.


What is aspect ratio?

An aspect ratio is simply width ÷ height.

  • 1 / 1 is a square (same width and height)
  • 16 / 9 is “widescreen” (common for video)
  • 4 / 3 is an older TV / photo ratio

It’s not a unit like px or rem. It’s a relationship. If the width changes, the height changes to keep the same shape (or vice-versa).

.demo {
  width: 260px;
  aspect-ratio: 1 / 1;
  border: 3px solid #111;
}
.demo {
  width: 260px;
  aspect-ratio: 16 / 9;
  border: 3px solid #111;
}
.demo {
  width: 260px;
  aspect-ratio: 4 / 3;
  border: 3px solid #111;
}
*,
::before,
::after {
  box-sizing: border-box;
}

.demo {
  display: grid;
  place-items: center;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  background: #f2f2f2;
}
I keep my proportions ✅

Click the different CSS snippets above. Same width, different ratios → different heights.


The modern way: the aspect-ratio property

The modern CSS solution is wonderfully simple:

  • You set one dimension (usually width).
  • You set aspect-ratio.
  • The browser calculates the other dimension.

Syntax:

  • aspect-ratio: 1 / 1;
  • aspect-ratio: 16 / 9;
  • aspect-ratio: 3 / 2;
.box {
  width: 320px;
  aspect-ratio: 3 / 2;
  border: 3px solid #111;
  background: #e9f5ff;
  display: grid;
  place-items: center;
}

*,
::before,
::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
}

Resize my width 🎚️

Drag the slider: the width changes, and the height follows automatically to preserve 3 / 2.


Aspect ratio+sizing: the rules you should remember

Here are the beginner-friendly rules (no scary math required):

  1. aspect-ratio needs at least one dimension to work.
    Usually you give a width (width: 300px or width: 100% ) and the height becomes automatic.
  2. If you explicitly set both width and height, the ratio can’t “force” anything.
    The browser already has both numbers, so it doesn’t need the ratio for sizing.
  3. aspect-ratio plays nicely with responsive widths.
    Example: width: 100% plus max-width.

Let’s see these three scenarios side by side.


/* 1) Works: width + aspect-ratio */
.one {
  width: 100%;
  aspect-ratio: 16 / 9;
}

/* 2) “Does nothing”: both width and height are set */
.two {
  width: 100%;
  height: 90px;
  aspect-ratio: 16 / 9;
}

/* 3) Works responsively: width 100% with max-width */
.three {
  width: 100%;
  max-width: 220px;
  aspect-ratio: 1 / 1;
}

/* Fix #2: remove explicit height */
.one {
  width: 100%;
  aspect-ratio: 16 / 9;
}

.two {
  width: 100%;
  aspect-ratio: 16 / 9;
}

.three {
  width: 100%;
  max-width: 220px;
  aspect-ratio: 1 / 1;
}
*,
::before,
::after {
  box-sizing: border-box;
}

.wrap {
  max-width: 760px;
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(3, 1fr);
}

.card {
  border: 3px solid #111;
  background: #f2f2f2;
  display: grid;
  place-items: center;
  font-family: ui-monospace, SFMono-Regular, Menlo;
  padding: 10px;
}
1) width+ratio ✅
2) width+height+ratio 🤷
3) responsive ✅

Click the second snippet (“Fix #2”) and notice how removing the fixed height lets the ratio actually control the box.


Keeping images proportional with aspect-ratio

When you place an image inside a responsive layout, the image can:

  • stretch (not good)
  • overflow (also not good)
  • or behave nicely (we choose this one)

A super common pattern is: “I want a fixed ratio thumbnail area” (like 16:9), and the image should fit inside it.

.thumb {
  width: 320px;
  aspect-ratio: 16 / 9;
  border: 3px solid #111;
  overflow: hidden;
}

.thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.thumb {
  width: 320px;
  aspect-ratio: 16 / 9;
  border: 3px solid #111;
  overflow: hidden;
}

.thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  background: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
}

Demo photo

The magic combo here is:

  • The container controls the ratio: .thumb { aspect-ratio: 16 / 9; }
  • The image fills the container: width: 100%; height: 100%;
  • object-fit decides how the image behaves inside the box.

Cover vs contain (aka “crop it nicely”)

This is worth slowing down for 10 seconds because it comes up everywhere:

  • object-fit: cover fills the box, cropping anything that doesn’t fit.
  • object-fit: contain shows the whole image, adding empty space if needed.

If you want “thumbnails that all look consistent”, you typically want cover. If you want “don’t crop anything”, you want contain.

.frame {
  aspect-ratio: 1 / 1;
  overflow: hidden;
}

.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.frame {
  aspect-ratio: 1 / 1;
  overflow: hidden;
}

.frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  background: #fff;
}

*,
::before,
::after {
  box-sizing: border-box;
}
.grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(2, 220px);
}
.frame {
  border: 3px solid #111;
}
Demo 1
Demo 2

Common UI pattern: “Card media” area

A very common UI pattern is a card with:

  • a consistent media area (same ratio for every card)
  • a title and description underneath

Here’s a clean beginner version.

.card-media {
  aspect-ratio: 16 / 9;
  background: #f2f2f2;
}

.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Switch to square thumbnails */
.card-media {
  aspect-ratio: 1 / 1;
  background: #f2f2f2;
}

.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
*,
::before,
::after {
  box-sizing: border-box;
}

.cards {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(3, 1fr);
  max-width: 960px;
}

.card {
  border: 3px solid #111;
  background: #fff;
  overflow: hidden;
}

.card-body {
  padding: 12px;
  font-family: system-ui, -apple-system;
}

.card-body h3 {
  margin: 0 0 6px;
  font-size: 18px;
}

.card-body p {
  margin: 0;
  opacity: 0.8;
}

Card 1

CSS Card

Consistent media size, even with random photos.

Card 2

Still Consistent

That’s aspect ratio doing the heavy lifting.

Card 3

No Stretching

Only tasteful cropping. Like a haircut.

Notice how changing aspect-ratio on .card-media immediately changes the whole layout, without touching the HTML.


Fallback: the old “padding-top hack”

These days, aspect-ratio is widely supported in modern browsers. But you may still run into:

  • older embedded browsers
  • weird email clients
  • legacy projects that already use the old method

The classic trick is:

  • Set the container to position: relative
  • Use padding-top (or padding-bottom) as a percentage
  • Absolutely position the inner content to fill it

Why does it work? Because vertical padding percentages are calculated from the element’s width. So padding-top: 56.25% gives you a 16:9 box (because 9 ÷ 16=0.5625).

.ratio-box {
  width: 320px;
  border: 3px solid #111;
  position: relative;
  overflow: hidden;
}

/* 16:9 fallback */
.ratio-box::before {
  content: "";
  display: block;
  padding-top: 56.25%;
}

.ratio-box>.content {
  position: absolute;
  inset: 0;
}

.ratio-box {
  width: 320px;
  border: 3px solid #111;
  position: relative;
  overflow: hidden;
}

/* 1:1 fallback */
.ratio-box::before {
  content: "";
  display: block;
  padding-top: 100%;
}

.ratio-box>.content {
  position: absolute;
  inset: 0;
}

*,
::before,
::after {
  box-sizing: border-box;
}
.ratio-box>.content {
  display: grid;
  place-items: center;
  background: #e9f5ff;
  font-family: ui-monospace, SFMono-Regular, Menlo;
}

Old-school ratio box

If you can use aspect-ratio, do it. It’s clearer, easier, and doesn’t require pseudo-elements or absolute positioning gymnastics. But it’s useful to recognize this technique when you see it in the wild.


Troubleshooting: “Why does aspect-ratio do nothing?”

If your ratio isn’t working, it’s usually one of these:

  • You didn’t set a width (or height). The element has no size to base calculations on.
  • You set both width and height. The ratio can’t override your explicit sizing.
  • The element is inline. Many inline elements don’t behave like boxes. Use display: block or inline-block.
  • The element is constrained by layout rules. For example, grid/flex sizing can affect things. Usually it still works, but you may need to check the parent sizing.
.inline-demo {
  aspect-ratio: 1 / 1;
  width: 140px;
  border: 3px solid #111;
  background: #f2f2f2;
  /* display is inline by default for span */
}

.inline-demo {
  aspect-ratio: 1 / 1;
  width: 140px;
  border: 3px solid #111;
  background: #f2f2f2;
  display: inline-block;
}

*,
::before,
::after {
  box-sizing: border-box;
}

.inline-demo {
  padding: 8px;
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
}

I am a spanMe too

Inline elements (like span) are not “boxy” by default. Making them inline-block turns them into a box that can have a real width/height relationship.


Quick reference+best practices

Quick reference

  • Square: aspect-ratio: 1 / 1;
  • Widescreen: aspect-ratio: 16 / 9;
  • Portrait-ish: aspect-ratio: 3 / 4;

Best practices

  • Put aspect-ratio on the container when you want consistent “frames” (cards, thumbnails, placeholders).
  • Pair ratio containers with: img { width: 100%; height: 100%; object-fit: cover; } for clean thumbnails.
  • Avoid setting both width and height if you want the ratio to control sizing.
  • If something’s weird, temporarily add a border: border: 3px solid red; so you can see the box you’re actually sizing.

CSS Aspect Ratio Conclusion

That’s CSS aspect ratio for you: fewer stretched images, fewer layout headaches, and more squares that stay square. Your rectangles can stay rectangles too. Everyone wins.