How to Create a Responsive Square with CSS

During a recent project I came upon an interesting CSS problem. I needed to create a square element that would maintain its square shape as it responsively resized to a changing window size.

The Problem

It is easy to create a square in CSS when we can explicitly declare its 'width' and 'height':

.square {
  width: 100px;
  height: 100px;
}

square1

However, when we try to make our square element responsive by changing our units to percentages, we run into a problem:

.square {
  width: 50%;
  height: 50%;
}

square2

The element’s width is calculated as a percentage of its parent’s width, while its height is calculated as a percentage of its parent’s height. Because our width and height are calculated from different measurements, the square will no longer hold its shape.

The Solution

After quite a bit of searching, I came upon a surprising solution to the problem. By making use of the :after pseudo-element and 'padding-bottom', we can create our responsive square using only CSS.

The solution relies on the somewhat counterintuitive fact that padding is calculated as a percentage of its parent element’s width, not height. This rule applies, even for 'padding-top' and 'padding-bottom', despite being vertical measurements.

To capitalize on this fact, we must first remove the 'height' property from our .square element. In its place, we add an empty :after element to .square and set 'padding-bottom: 100%':

.square {
  width: 50%;
}

.square:after {
  content: "";
  display: block;
  padding-bottom: 100%;
}

Because the pseudo element’s padding is calculated as a percentage of its parent’s width, and its parent is our .square element, we are effectively fixing the height of the pseudo-element to the width of .square. Whenever .square’s width changes, so will the pseudo-element’s height.

square3

Adding Content

Finally, we might like to add some content to our .square element. The above solution relies on both .square and .square:after having a height of 0, so adding content to either of them would break our solution.

Fortunately, this problem is easily solved by creating an absolutely positioned element and placing it within .square. The new element can be sized to match the square, but since it is absolutely positioned, its content will not affect the dimensions of our square:

 

Hello!

 


.square {
  position: relative;
  width: 50%;
}

.square:after {
  content: "";
  display: block;
  padding-bottom: 100%;
}

.content {
  position: absolute;
  width: 100%;
  height: 100%;
}

square4

Conversation
  • emerson says:

    An alternate way would be using viewport units. While they are a bit newer, using them means not having to mess around with absolute positioning and ::after. The CSS would be:

    .square {
    border: 5px solid red;
    text-align: center;
    width: 50vw;
    height: 50vw;
    }

    .content {
    font-size: 2em;
    padding-top: 20%;
    }

    Codepen fork here

    It is a bit limited just because viewport units are taken from the width of the window, not the width of the parent element. But if that fits with your desired layout, its cleaner code and less of a “hack”.

  • Bjarni Wark says:

    Thanks for sharing and explained so well with examples and all.

  • User says:

    Thank you for this! Very helpful and clear, it saved me a lot of headache. Another one of those “it shouldn’t be this difficult/hacky” kind of CSS problems :)

  • Jaakko K says:

    This is cool, but what is the browser compatibility with it?

  • Ali Sheehan-Dare says:

    This is amazing. Thanks.

  • lyrics songs says:

    Thanks for sharing.

  • Thiago says:

    Exactly what I need. Thanks!

  • Matthew says:

    Just awesome! Until height can be defined as a percentage this will be my go to. And for the record, it works for other shapes too. I’ve made a scaling circle and scaling rectangle with slight variations. The circle, adding border-radius:50%, a 2-to-1 ratio’d rectangle, setting the padding-bottom to 50% instead of 100%. Thank you for this elegant “hack”

  • Eswarkumar says:

    Very Nice!

  • Victor says:

    Thanks for sharing this!

  • eddie says:

    so helpful thanks!!

  • Mariana says:

    Thank you!!!

  • elias says:

    ı need the to do responsive this div pls help thanx

    Untitled

    .edgeLoad-D0111 { visibility:hidden; }

    AdobeEdge.loadComposition(‘/lms/static/images/D0111’, ‘D0111’, {
    scaleToFit: “none”,
    centerStage: “none”,
    minW: “0”,
    maxW: “undefined”,
    width: “0px”,
    height: “810px”
    }, {dom: [ ]}, {dom: [ ]});

    Dinleme MetniÜnite 1: MERHABA – Konu 1: Tanışma

  • elias says:

    ı cant upload all code

    can you anybody help me about a dive make a responsive

    ı can send

  • David says:

    Thank you!!!

  • Tomica says:

    Bobby this is an awesome solution. But I think there’s a problem with it in Firefox. It just doesn’t work (collapses the element height to 0). Has anyone else got the same problem?

  • laurens says:

    Isn’t it a rectangle? a square has on all sides the same length. Can be wrong though.

  • Comments are closed.