DEV Community

Max
Max

Posted on • Updated on

Awaiting for aspect-ratio: "hacks" for proportional boxes

While waiting for widespread support of aspect-ratio property, I invite you to think back on a few variations of the hack that you can still use to achieve the same behaviour with regular boxes.

Table Of Contents

padding-top/bottom in %

Aspect-ratio without the new property could be achieved by setting the box with zero height padding-top or padding-bottom in %. The percentage was calculated using the formula:

height / width * 100%

For instance:
1x1 aspect ratio = 1 / 1 * 100% = padding-top: 100%
4x3 aspect ratio = 3 / 4 * 100% = padding-top: 75%
16x9 aspect ratio = 9 / 16 * 100% = padding-top: 56.25%

In some cases, percentages were rounded to the nearest hundredth:
3x2 aspect ratio = 2 / 3 * 100% = padding‑top: 66.67% (66.66666666666667%)

Apparently people didn't like writing 16-digit property values. Which brings us to

padding‑top/bottom + calc() function

.aspect-ratio-box {
  padding-bottom: calc(100% / (327 / 120));
  height: 0;
}
Enter fullscreen mode Exit fullscreen mode

It was more appealing and gave people, not familiar with the hack, a little hint about what was going on.

Since the box height was zero, we needed to use absolute positioning to place something (a heading, for example) in it. Because of this, it wasn't protected from content overflow.

Also, you may have come across another reason why we could not use this method. Here's a good example:
Block of the Loop studio landing page from frontendmentor.io
Block of the Loop studio landing page from frontendmentor.io

All projects of this block are essentially an article tag with the heading inside. Images are specified through the background-image property. Projects blocks must also be clickable. We can wrap the entire content of the article block in a link. In this case, it won't be a problem. However, if we ever want to add a description to the project card, we will have an accessibility problem. The screen reader will read everything inside the link — both the heading and the description section.
There is a solution to that issue: put the link in the header tag and stretch its clickable area through a pseudo-element. But for this, we need the headings not to be absolutely positioned. So we need another version of the aspect-ratio box.

padding-top/bottom for ::before

Next modification was to set the padding in % but for a pseudo‑element with a float: left; Like this:

.aspect-ratio-box {
    display: flow-root;
/* required to prevent the container collapsing */
}

.aspect-ratio-box::before {
    content: "";
    float: left;
    padding-bottom: calc(100% / (327 / 120));
}
Enter fullscreen mode Exit fullscreen mode

For broader support than flow-root (i.e. browsers ~ until mid-2017), you could alternatively use the column-count property for the container:

.aspect-ratio-box {
    column-count: 1;
/* Bulletproof clearfix */
}

.aspect-ratio-box::before {
    content: "";
    float: left;
    padding-bottom: calc(100% / (327 / 120));
}
Enter fullscreen mode Exit fullscreen mode

flexbox option

display: flex; will change the normal document flow to horizontal (from left to right):

.aspect-ratio-box {
  display: flex;
}

.aspect-ratio-box::before {
    content: "";
    padding-bottom: calc(100% / (327 / 120));
}
Enter fullscreen mode Exit fullscreen mode

Remember that the last two alternatives still do not allow you to add padding to the container. If you need padding, you will have to add it to the child boxes.

What's next? @supports not

What variation to use in work? It seems to me that we can start using aspect‑ratio and use a fail-safe with the @supports at-rule and not operator:

.aspect-ratio-box {
  aspect-ratio: 327 / 120;
}

@supports not (aspect-ratio: 1 / 1) {
/* if the browser does NOT support the property, then */
/* apply styles inside */
  .aspect-ratio-box {
    column-count: 1;
  }

  .aspect-ratio-box::before {
    content: "";
    float: left;
    padding-bottom: calc(100% / (327 / 120));
  }
}
Enter fullscreen mode Exit fullscreen mode

This method is the most reliable.

If someone who works with your CSS needs to reposition the heading, they can use flexbox and margins with value auto.
Float property will stop working. This way, they won't accidentally break anything.

And over time, when support becomes more widespread, we will only remove the at-rule with its content.

Top comments (0)