RandallB
RandallB

Reputation: 5585

Using a calc() to create an auto-sized element according to aspect ratio?

I have a video container, and it'd be splendid if I could use calc to calculate the height based on the width.

There are some inklings of stackoverflow answers that claim something like this is possible:

.selector{
  width: 100%;
  height: calc(width * 1.75);
}

But I haven't seen that work in Chrome 26. How can I calculate the height only using CSS3?

Upvotes: 9

Views: 30598

Answers (7)

Timber
Timber

Reputation: 373

The CSS property aspect-ratio lets you create boxes that maintain proportional dimensions where the height and width of a box are calculated automatically as a ratio. It’s a little math-y, but the idea is that you can divide one value by another on this property and the calculated value ensures a box stays in that proportion.

In other words, this property helps us to size elements consistently, so the ratio of an element stays the same as it grows or shrinks.

.element {
  aspect-ratio: 2 / 1; /* *w* is double the *h* */
}
.element {
  aspect-ratio: 1 / 1; /* perfect square */
}

aspect-ratio is defined in the CSS Box Sizing Module Level 4 specification, which is currently in Working Draft. That means it’s still in progress and has a chance of changing. But with Chrome and Firefox supporting it behind an experimental flag, and Safari Technology Preview adding support for it in early 2021, there are strong signals that aspect-ratio is gaining a lot of momentum.

Syntax aspect-ratio: auto || ; In plain English: aspect-ratio is either assumed to be auto by default, or accepts a as a value where <width / height>.

Initial value: auto
Applies to: all elements except inline boxes and internal ruby or table boxes
Inherited: no
Percentages: n/a
Computed value: specified keyword or a pair of numbers
Animation type: discrete

Values
/* Keyword values */
aspect-ratio: auto; /* default */

/* Ratio values */
aspect-ratio: 1 / 1; /* width and height are equal proportion */
aspect-ratio: 2 / 1; /* width is twice the height*/
aspect-ratio: 1 / 2; /* width is half the height */
aspect-ratio: 16 / 9  /* typical video aspect ratio */
aspect-ratio: auto 4 / 3; /* width:height, unless it's a replaced element */
aspect-ratio: 0.5; /* float value */

/* Global values */
aspect-ratio: inherit;
aspect-ratio: initial;
aspect-ratio: unset;

auto: The default value, which specifies that the element has no preferred aspect ratio and should size itself as it normally would. Therefore, replaced elements, like images with an intrinsic aspect ratio, use that aspect ratio. : Two positive numeric values separated by a forward slash (/) with or without space around them, targeting the width and height of the element. In the case of a single value, the second value is considered to be 1. Size calculations involving preferred aspect ratio work with the dimensions of the box specified by box-sizing.

initial: Applies the property’s default setting, which is auto. inherit: Adopts the aspect-ratio value of the parent. unset: Removes the current aspect ratio from the element.

It can take two values This property can take two values at the same time, one being auto, and the other a :

.element {
    aspect-ratio: auto 1 / 1;
}

If both auto and a are specified together, the preferred aspect ratio is the specified ratio of width divided by height, unless it is a replaced element with an intrinsic aspect ratio, in which case that aspect ratio is used instead.

As you can see in the following demo, the same values are set for a div and an (a replaced element), the div element is using the and becomes a square, but the image follows its intrinsic aspect ratio. If you remove auto from the values, you can see that the image is forced to be a square:

body {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 2rem;
  padding: 2rem;
}

.preferably-square {
  width: 300px;
  aspect-ratio: auto 1 / 1; 
}

div {
  background-image: linear-gradient(90deg,#ec4899,#ef4444,#f59e0b);
}

img {

}
<div class="preferably-square"></div>
<img class="preferably-square" src="https://source.unsplash.com/random/800x600?iran" alt="">

Source: https://css-tricks.com/almanac/properties/a/aspect-ratio/

Upvotes: 0

Roman Starkov
Roman Starkov

Reputation: 61530

Since 2021 all modern browsers support the aspect-ratio CSS property. So that's another option.

Upvotes: 3

vsync
vsync

Reputation: 130750

Using the padding trick and some CSS variables:

div{
 --w: 50%;
 --ratio: 2; /* width / height */
 
  width: var(--w);
  padding-top: calc(var(--w)/var(--ratio));
  
  background: #EEE;
  outline: 1px solid #CCC;
  position: relative;
}

/* Demo content, could be anything */
div::before{
  content: "VIDEO";
  font: 700 4vw Arial;
  color: #AAA;
  display: grid;
  place-items: center;

  /* important part: */
  position: absolute;
  top:0; right:0; bottom:0; left:0;
}
<div></div>

Upvotes: 1

Sagive
Sagive

Reputation: 1837

Since videos are (usually) 16/9 you can
use a simple calc solution while keeping
your video or iframe responsive.

iframe {width: 100%; height: calc(~'100% * 9/16');}

Upvotes: 0

jono
jono

Reputation: 1842

There is a way to get around this that bypasses the need to use calc() that I thought I ought to make you aware of.

You can set the height to zero and use padding as it is relative to the elements width to create a fixed ratio.

.selector {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom:175%;
}

I often use this technique to display 16:9 YouTube videos responsively. To do this all you have to do is set the child element like this -

.selector .child {
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
}

Upvotes: 7

MaxPRafferty
MaxPRafferty

Reputation: 4987

Using vh and vw units is a nice method for getting a fixed responsive viewframe:

.sixteen-nine {
   width: calc(75vh * 1.77);
   height: 75vh;
}

That will give you a viewport relative to screen height at approximately 16:9.

Upvotes: 5

pzin
pzin

Reputation: 4248

For the moment the CSS variables aren't really supported yet. I think they work in WebKit.

If you need that to be a variable, you should use for the moment some CSS preprocessor such as Sass, less or Stylus.

But I am not sure if you really need the width to be a variable. In any case, in plain CSS you need that to be a real value for the moment:

div {
  height: calc(100% * 1.75);
}

In the future, we could do something like this:

div {
  var-height: 100%
  height: calc(var(height) * 1.75);
}

Upvotes: 1

Related Questions