19

I saw solution for height depending on width: css height same as width. Or here: https://stackoverflow.com/a/6615994/2256981. But my question is opposite.

My element:

<body>
    <div id="square"></div>
</body>

Style for it:

body, html {
    margin: 0;
    height: 100%;
    min-height: 300px;
    position: relative;
}
div#square { /* My square. */
    height: 75%; /* It's height depends on ancestor's height. */
    width: 75vh; /* If supported, preliminarily sets it's width. */
    position: absolute; /* Centers it. */
    left: 50%; top: 50%; transform: translate(-50%, -50%);
    background-color: darkorange; /* Makes it visible. */
}

Script, that keeps it square:

window.onload = window.onresize = function (event) {
    var mySquare = document.getElementById("square");
    mySquare.style.width = mySquare.offsetHeight + 'px';
};

Complete code here: http://jsfiddle.net/je1h/hxkgfr9g/

The question is to make the same thing in pure CSS. No scripting.

Community
  • 1
  • 1
jevgenij
  • 824
  • 1
  • 7
  • 16
  • 1
    I don't know if you can do this in css. You might try https://css-tricks.com/snippets/css/a-guide-to-flexbox/, but I don't know if that'll help you. – xdhmoore Apr 16 '15 at 14:38
  • Does this do what you're looking for? http://stackoverflow.com/questions/12899031/css-squares-on-resize – xdhmoore Apr 16 '15 at 14:40
  • I also think it's not possible in pure CSS, tried many times... We'll have to wait before styling dynamic perfect squares! The only option I see is Javascript like you wrote actually. – Sebastien Apr 16 '15 at 14:41

2 Answers2

16

There are two techiques I am aware of to keep the aspect ratio of an element according to it's height :

When height is relative to the viewport :

You can use vh units :

div {
  width: 75vh;
  height: 75vh;
  background:darkorange;
}
<div></div>

For a height based on the height of a parent element :

You can use a dummy image that has the aspect ratio you want. Example with a 1:1 aspect ratio you can use a 1*1 transparent .png image or as commented by @vlgalik a 1*1 base64 encoded gif :

html,body{
    height:100%;
    margin:0;
    padding:0;
}
#wrap{
    height:75%;
}
#el{
    display:inline-block;
    height:100%;
    background:darkorange;
}
#el img{
    display:block;
    height:100%;
    width:auto;
}
<div id="wrap">
    <div id="el">
        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
    </div>
</div>

Note that this last demo doesn't update on window resize. But the aspect ratio is kept on page load

UPDATE :
As reported in the comments setting display:inline-flex: on #el seems to solve the updating on window resize problem.

Community
  • 1
  • 1
web-tiki
  • 99,765
  • 32
  • 217
  • 249
  • 1
    Or you can use Base64 Encoded 1x1px transparent GIF , or inline SVG (but there are some issues in FF and older browsers with the SVG). – vlgalik Apr 16 '15 at 14:54
  • @vlgalik nice, I updated the answer. – web-tiki Apr 16 '15 at 14:59
  • Thanks for your answer. It is funny hack, and to be honest, I didn't completely manage to figure out how does it work :) Why do we need '#wrap', to be precise. And as you mantioned, unfortunately it doesn't update '#el' width on window resize. – jevgenij Apr 16 '15 at 17:05
  • @jevgenij `#wrap` isn't necessary, it's there for the demo, to show that you can maintain the aspect ratio according to the parent's (`#wrap`) height. – web-tiki Apr 16 '15 at 17:11
  • 1
    Ok, I see now, just got entangled :) Also have found that `display:inline-flex;` for `#el` solves update problem, but of course it gives us compatibility issues as well. – jevgenij Apr 16 '15 at 18:01
  • nice @jevgenij I wasn't aware of that. I updated the answer. – web-tiki Apr 17 '15 at 12:19
-1

Edit: Missed that you wanted to set the width depending of the height, this does the opposite :(


To support all ratios you can use padding-bottom. Percentages in padding-bottom are always relative to the width of the element:

/* the wrapper has a width */
.wrapper {
  position: relative;
  width: 25%;
  margin-bottom: 10px;
}
/* these elements set the height (using padding-bottom) */
.square {
  padding-bottom: 100%;
  position: relative;
}
.widescreen {
  padding-bottom: 56.25%; /* 9/16 = 0.5625 */
}

/* 
 * This is the content. 
 * Needs position:absolute to not add to the width of the parent 
 */
.content {
  /* fill parent */
  position: absolute;  
  top: 0; bottom: 0;
  left: 0; right: 0;
  
  /* visual */
  padding: 10px;
  background: orange;
  color: white;
}
<div class="wrapper">
  <div class="square">
    <div class="content">square</div>
  </div>
</div>

<div class="wrapper">
  <div class="widescreen">
    <div class="content">16:9 ratio</div>
  </div>
</div>

The only downside is, that you need a bunch of wrapper elements.

darthmaim
  • 4,970
  • 1
  • 27
  • 40