243

I want my div to adapt its height to always equal its width. The width is percental. When the parent's width decreases, the box should decrease by keeping its aspect ratio.

How to do this is CSS?

danijar
  • 32,406
  • 45
  • 166
  • 297
  • Square elements in pure css is challenging and with limitations, but [has been answered before](http://stackoverflow.com/questions/13851940/pure-css-solution-square-elements/13852277#13852277). Another useful reference may be [this answer](http://stackoverflow.com/questions/14161321/scaling-object-element-height-proportional-to-width-constant-with-css/14162000#14162000). – ScottS Sep 28 '13 at 15:01
  • 1
    For a responsive grid of square elements you can check this answer : [responsive square columns](http://stackoverflow.com/questions/20456694/responsive-square-columns/20457076#20457076) – web-tiki Jun 19 '14 at 13:38
  • 2
    Perfect square on any screen: http://jsfiddle.net/far4jqt2/2/ – Junior Apr 28 '18 at 10:19
  • Here's a good article with a good explanation... https://spin.atomicobject.com/2015/07/14/css-responsive-square/ – clayRay Sep 12 '19 at 00:19

6 Answers6

259

Works on almost all browsers.

You can try giving padding-bottom as a percentage.

<div style="height:0;width:20%;padding-bottom:20%;background-color:red">
<div>
Content goes here
</div>
</div>

The outer div is making a square and inner div contains the content. This solution worked for me many times.

Here's a jsfiddle

kelorek
  • 6,042
  • 6
  • 29
  • 32
rahulbehl
  • 2,629
  • 1
  • 12
  • 6
  • 7
    It works, but I don't understand the technique. Could you please explain why this actually works? – danijar Sep 28 '13 at 15:59
  • 13
    This is very good tutorial if you want to use padding-bottom method. [link]http://www.dwuser.com/education/content/creating-responsive-tiled-layout-with-pure-css/ – rahulbehl Sep 28 '13 at 19:44
  • 32
    Preferable to the chosen answer because it works within the constraints of a container, NOT the viewport which is rarely useful – Matt Saunders Aug 28 '14 at 15:31
  • I didn't like this one at first because it looks like a hack, anyhow there's a good discussion at http://stackoverflow.com/questions/11003911/why-are-margin-padding-percentages-in-css-always-calculated-against-width – Coded Monkey Mar 30 '15 at 17:24
  • Don't forget `box-sizing: content-box;` in case you add border on the entity. Else it can distort the 1:1 ratio. (happened when adding fat border-radius) – zsitro Dec 10 '15 at 10:11
  • 1
    This is really, really cool. How did I not know that? You can get anything to have an aspect ratio, for example I just set my padding-bottom to be 120%, and now the width:height ratio is 1:1.2 - amazing. – Matt Fletcher Jan 07 '16 at 13:39
  • 1
    does not seem to work on new ie - Edge when element has display:flex;, it actually works on older versions – mmln Mar 24 '16 at 22:47
  • It doesn't seem to work for me in Chrome somehow. If there is no content in the inner div, the box ratio is 1:1, but when I add content in the inner div, it takes some space and make the height greater than width. – C.Lee Jun 15 '16 at 17:17
  • 3
    @C.Lee You have to add `position: absolute;` on the inner `div` to make it work. Elements with absolute positioning are ignored when the parent dimensions are calculated. That means you can add content, but also padding, borders, etc to the inner element. :) See [this fiddle](https://jsfiddle.net/sg4q2qyc/). – Max Truxa Oct 27 '16 at 10:15
  • This technique is actually used to display images in Google AMP. Cool! – Mel Nov 26 '19 at 06:25
  • If someone is using flex containers beware because it does not work correctly in nested structures – David Arreola Dec 16 '20 at 21:12
217

To achieve what you are looking for you can use the viewport-percentage length vw.

Here is a quick example I made on jsfiddle.

HTML:

<div class="square">
    <h1>This is a Square</h1>
</div>

CSS:

.square {
    background: #000;
    width: 50vw;
    height: 50vw;
}
.square h1 {
    color: #fff;
}

I am sure there are many other ways to do this but this way seemed the best to me.

Daniel Burkhart
  • 2,572
  • 2
  • 21
  • 23
  • 9
    This is definitely the cleanest solution. For people interested in browser support, I found [this overview](http://caniuse.com/viewport-units). – danijar Sep 28 '13 at 16:11
  • 2
    Very nice but browser support is a little bit sketchy at the moment though isn't it - http://www.google.ie/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CEAQFjAA&url=http%3A%2F%2Fcaniuse.com%2Fviewport-units&ei=H3OfUojKDoWu7Aav64HICQ&usg=AFQjCNGClMNGOWMz16go3FPckmKbSmthnw&sig2=ra5HzgNeZxkBSVlFXY1cSA&bvm=bv.57155469,d.ZGU – byronyasgur Dec 04 '13 at 18:25
  • 7
    This solution doesn't play nice with a grid system. If you're looking at squares expanding with columns width, the answer of @rahulbehl works better. – eightyfive Sep 11 '14 at 02:41
  • 1
    Browser support for `vw` : http://caniuse.com/#search=vw – Chemical Programmer Jan 29 '16 at 00:38
  • Browser support is pretty solid for vw, but it probably won't work for you if your pages scroll. vw is effectively the width of the window, not the viewport, as it includes scrollbars (same with vh for vertical stuff). Bad decision by the spec authors in my view, because this has the potential to be a super useful unit of measurement, but if your pages scroll, you're better off using @rahulbehl's solution. I can't think of a use case where it's handy to have the width including the scrollbars... but it is what it is. – Tom Walker May 18 '16 at 23:06
  • this deform the image, and is better if you can recort it, so :/ – Alberto Acuña Jan 04 '17 at 12:07
  • 2
    Tip: if you want to fit a square inside a viewport on either portrait or landscape view: ``` @media (orientation:portrait){width:100vw; height:100vw;} @media (orientation:landscape){width:100vh; height:100vh;} ``` – MoonLite Jun 16 '17 at 12:55
  • Note: this would be bigger than the viewport on superultrawide monitors where 50vw > 100vh – tbjgolden Mar 28 '22 at 21:51
72

HTML

<div class='square-box'>
    <div class='square-content'>
        <h3>test</h3>
    </div>
</div>

CSS

.square-box{
    position: relative;
    width: 50%;
    overflow: hidden;
    background: #4679BD;
}
.square-box:before{
    content: "";
    display: block;
    padding-top: 100%;
}
.square-content{
    position:  absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    color: white;
    text-align: center;
}

http://jsfiddle.net/38Tnx/1425/

gidzior
  • 1,807
  • 3
  • 25
  • 38
35

It is as easy as specifying a padding bottom the same size as the width in percent. So if you have a width of 50%, just use this example below

id or class{
    width: 50%;
    padding-bottom: 50%;
}

Here is a jsfiddle http://jsfiddle.net/kJL3u/2/

Edited version with responsive text: http://jsfiddle.net/kJL3u/394

Chipe
  • 4,641
  • 10
  • 36
  • 64
  • 5
    This grows out of the square aspect ratio once you add content to it, though. – zoul Mar 27 '15 at 08:01
  • 2
    You can fix that problem with `overflow:hidden` or other absolute positioning of children inside the container. Conveniently exactly like @gidzior's answer – mix3d Oct 26 '15 at 16:34
8

Another way is to use a transparent 1x1.png with width: 100%, height: auto in a div and absolutely positioned content within it:

html:

<div>
    <img src="1x1px.png">
    <h1>FOO</h1>
</div>

css:

div {
    position: relative;
    width: 50%;
}

img {
    width: 100%;
    height: auto;
}

h1 {
    position: absolute;
    top: 10px;
    left: 10px;
}

Fidle: http://jsfiddle.net/t529bjwc/

gskalinskii
  • 967
  • 2
  • 11
  • 17
3

This is what I came up with. Here is a fiddle.

First, I need three wrapper elements for both a square shape and centered text.

<div><div><div>Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat
volutpat.</div></div></div>

This is the stylecheet. It makes use of two techniques, one for square shapes and one for centered text.

body > div {
    position:relative;
    height:0;
    width:50%; padding-bottom:50%;
}

body > div > div {
    position:absolute; top:0;
    height:100%; width:100%;
    display:table;
    border:1px solid #000;
    margin:1em;
}

body > div > div > div{
    display:table-cell;
    vertical-align:middle; text-align:center;
    padding:1em;
}
danijar
  • 32,406
  • 45
  • 166
  • 297
  • 1
    Extra elements for a simple square, yuk, but works I suppose. Side note; `display:table-cell` is not supported in IE7 or older. – TheCarver Feb 23 '14 at 15:31