Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS-only square div fitting inside rectangle div

Tags:

css

I have determined how to set a div height based on its width (using percentage width and padding-bottom). But I also need to be able to set a div width based on its height. I have tried using vw/vm/vh units, but those are relative to the viewport and not the container.

So, in the following example

<body>
    <div class="rectangle">
        <div class="square">
        </div>
    </div>
</body>

Here's some non-working CSS that only works in portrait:

.rectangle {
  background-color:red;
  position:absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
}

.square {
  background-color:blue;
  width:100%;
  padding-bottom:100%;
}

I would like .rectangle to fill its container, meaning as the window size changes, .rectangle may go from a landscape layout to a portrait layout. When this happens, I want the .square div to fill as far as it can without losing its square shape.

So, if the browser window is wider than it is tall (landscape), the .rectangle should fill the window, and the .square should be as tall as the rectangle is tall, and as wide as the rectangle is tall.

And if the browser window is taller than it is wide (portrait), the .rectangle should fill the window, and the .square should be as wide as the rectangle is wide, and as tall as the rectangle is wide.

So, I can use media queries to swap out styles between landscape and portrait, and I have a solution for the portrait case, but I haven't found a solution to the landscape case. How do I get the square to fill its parent's height, and limit its width to maintain aspect ratio of a square?

like image 213
rich remer Avatar asked Oct 18 '22 20:10

rich remer


2 Answers

You can:

  • Place a replaced element with a 1:1 ratio inside the square.

    For example, it can be a 1px × 1px squared image, or a canvas.

    Style this replaced with height: 100%, so that it spans the entire square vertically.

    Let it have the default width: auto, so that its width respects the 1:1 aspect ratio.

    Style it with display: block to avoid the extra space below image problem.

  • Make the square use the shrink-to-fit width algorithm to calculate its width, so that it will have a 1:1 ratio too.

    You can achieve this by floating it or with display: inline-block.

  • If you want the square to have contents, place them in an absolutely positioned wrapper in order to prevent them from altering the sizes of the square.

    Make that wrapper as big as the square with top:0; right:0; bottom:0; left:0, and make the square its relative container.

This should work. However, for some reason browsers do not seem to update the width when the window is resized, so it only works initially. Forcing a rerender with JS solves this problem.

var s = document.getElementById('square'),
    p = s.parentNode,
    n = s.nextSibling;
window.addEventListener('resize', function() {
  // Force a rerender
  p.removeChild(s);
  p.insertBefore(s, n);
});
html, body, #square, canvas {
  height: 100%;
  margin: 0;
  display: block;
}
#square {
  float: left;
  position: relative;
  background: red;
}
#contents {
  overflow: auto;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
<div id="square">
  <canvas height="1" width="1"></canvas>
  <div id="contents">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</div>
</div>
like image 189
Oriol Avatar answered Oct 30 '22 14:10

Oriol


This is easiest done when .rectangle literally takes the full window - in other words, its width and height are 100vw and 100vh respectively.

If that is the case, then your square is simply:

.square {
    width: 100vh;
    height: 100vh;
    margin: 0 auto;
}
@media all and (orientation: portrait) {
    .square {
        width: 100vw;
        height: 100vw;
    }
}

It may be a little trickier to vertically-center the square in the portrait view, but certainly not impossible. margin-top: calc(50vh - 50vw) would do it, for instance.

like image 40
Niet the Dark Absol Avatar answered Oct 30 '22 14:10

Niet the Dark Absol