Search code examples
htmlcssscaleaspect-ratio

Scale divs height pure CSS


I need to scale the height of divs.

How it should work

  • The divs should scale, keep current aspect ratio
  • The width should be 100px always
  • The height should scale in propotion to the 100px width
  • I added some example width and height in the code
  • Add more HTML + CSS if needed

Example

A div block is originally 300px x 600px. When fit the width of 100px it will be 100px x 200px.

The result

The result will be divs equally wide but they will be different height, some taller than others.

https://jsfiddle.net/80pk066L/5/

Notes

If it whould be an image it would contain aspect ratio when setting width to 100%, but this is not an image. It is a div.

HTML

<ul>
    <li class="scale1"><div></div></li>
    <li class="scale2"><div></div></li>
    <li class="scale3"><div></div></li>
    <li class="scale4"><div></div></li>
    <li class="scale5"><div></div></li>
</ul>

CSS

ul {
    width: 300px;
}

li {
    float: left;
    background: red;
    margin: 10px;
    list-style: none;
    display: block;
    height: 100px;
    width: 100px;
    /*REMOVE HEIGHT AND WIDTH HERE, JUST DECORATION
    Width should always be 100px
    Height should be whatever, can be larger than 100px
    */
}

.scale1 div {
    width: 300px;
    height: 500px;
}

.scale2 div {
    width: 400px;
    height: 400px;
}

.scale3 div {
    width: 200px;
    height: 300px;
}

.scale4 div {
    width: 50px;
    height: 60px;
}

.scale5 div {
    width: 150px;
    height: 75px;
}

Solution

  • You can use a bottom padding trick to force a responsive aspect ratio on each individual element. You'll have to calculate the padding-bottom manually of each element, as a percentage of the height to width ratio.

    .scale1 div {
        padding-bottom: 166.667%;
    }
    
    .scale2 div {
        padding-bottom: 100%;
    }
    
    .scale3 div {
        padding-bottom: 150%;
    }
    
    .scale4 div {
        padding-bottom: 120%;
    }
    
    .scale5 div {
        padding-bottom: 50%;
    }
    

    See fiddle here: https://jsfiddle.net/teddyrised/80pk066L/9/

    If you want content in each element, though, they have to be wrapped within an absolutely positioned child element — that is because if they are not taken out of the document flow, the content height will add on to the bottom padding, therefore skewing the aspect ratio:

    li div {
        position: relative;
    }
    li div span {
        position: absolute;
        padding: 10px;
        top: 0; 
        left: 0;
        bottom: 0;
        right: 0;
    }
    

    https://jsfiddle.net/teddyrised/80pk066L/10/

    If using CSS to specify the aspect ratio is too tedious, you can programatically do this using JS. I understand that you might not want to achieve this using JS, but this might be more efficient if you have large number of elements you want to go through. For convenience's sake, I am using jQuery—but any other libraries or even raw JS would work, as long as the same logic is applied:

    $(function() {
        $('ul li').each(function() {
            $(this).children('div').css('padding-bottom', $(this).width()/parseFloat($(this).data('aspect-ratio')));
        });
    });
    

    I have chosen to store the aspect ratio (in the form of width-to-height) in a HTML5 data- attribute:

    <ul>
        <li class="scale1" data-aspect-ratio="0.6"><div><span>1</span></div></li>
        <li class="scale2" data-aspect-ratio="1"><div><span>2</span></div></li>
        <li class="scale3" data-aspect-ratio="0.667"><div><span>3</span></div></li>
        <li class="scale4" data-aspect-ratio="0.833"><div><span>4</span></div></li>
        <li class="scale5" data-aspect-ratio="2"><div><span>5</span></div></li>
    </ul>
    

    See proof-of-concept example here: https://jsfiddle.net/teddyrised/80pk066L/11/