One approach is to use viewport units and a media query.
Given:
- A flex container of arbitrary height, set in
vh
units, and
- A flex child of arbitrary width, set in vw units, which fills the height of its container
We can set our inner square to 100% width and then:
- Give it a
max-width
that is equal to the same number used for the height
of the container, but set in vmin
units
- Give it a
max-height
that is equal to the same number used for the width
of the flex child (the square's immediate parent), but set in vmin
units
That gets us pretty close, but we're left with a slightly rectangular inner square when our window gets to portrait orientation (i.e., when it's taller than it is wide).
So the last step we can use is to add a media query that kicks in at the same width at which the square's height becomes equal to its width, and in that query change the max-height
to match the max-width
, so our square isn't too tall.
The trick is figuring out how exactly to set that media query. In our example below, our inner square will cease being square when the window gets taller than it is wide. So we want to do something like
@media all and (max-width: calc(100vh - 1px))
But we can't do calc expressions in our media query, and viewport units in media queries also prove to be problematic. So probably our best bet is to use JavaScript to detect the window height and then insert a <style>
element into our document with the appropriate media query.
The snippet below does just that, though it could be improved by updating the media query on window resize (or, more specifically, only if the window height changes).
function createMediaQuery() {
var containerHeight = window.innerHeight - 1;
var mediaQuery = '@media all and (max-width: ' + containerHeight + 'px) { ' +
'#square { max-height: 70vmin } }'
var head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = mediaQuery;
} else {
style.appendChild(document.createTextNode(mediaQuery));
}
head.appendChild(style);
}
createMediaQuery();
html, body {
height: 100%;
width: 100%;
margin: 0;
}
.container {
display: flex;
height: 70vh;
margin: 15vh auto;
}
.flex-item {
background-color: midnightblue;
width: 80vw;
margin: 0 auto;
display: flex;
}
.square {
background-color: gainsboro;
width: 100%;
max-width: 70vmin;
max-height: 80vmin;
margin: 0 auto;
}
<div class="container">
<div class="flex-item">
<div class="square"></div>
</div>
</div>