Scale square div according to height keeping ratio [duplicate] - css

This question already has answers here:
Maintain div aspect ratio according to height
(6 answers)
Closed 6 years ago.
I need to create a square with 100% height and width scaling according to the height, and so that it always keeps its aspect ratio.
Example illustration:
The popular example of a square div scaling is according to width (https://spin.atomicobject.com/2015/07/14/css-responsive-square/). I would like to solve this using CSS/flexbox, but I cannot find a proper solution.
(IE and legacy browser support is not important)
There has to be more than one element sharing the same style.
I've tried drawing it, but I dont know if this makes much sense. The individual squares need to fit the outer divs, but the three squares should not be the same size - rather fit their individual outer div.
I would like to give and idea of how I've tried doing this, but it is difficult to go into details because I've done a lot of things - none of them really working. I've tried absolute positioning, but that requires a width. I've tried calculating the width/height using jquery - this takes waaaay to long - as there are a lot of entries. I've tried using height:100% + width:auto, which renders nothing, I've tried the above solution that I've linked to (which is brilliant, yes sadly is based onthe width as a percent and not the height). etcetcetc!! :)
The solution in the question this has been linked to is not sufficient for my problem.

There is, unfortunately, no straight forward way to implement this in CSS alone. You could use viewport units as others have mentioned, but this is always relative to the entire viewport size. It turns out Javascript is the only way to make it more flexible:
document.addEventListener('DOMContentLoaded', function() {
var el = document.getElementById('square');
(window.onresize = function() {
el.style.width = el.clientHeight + 'px';
})();
});
html, body {
height: 100%;
margin: 0;
}
#container {
background: red;
height: 80%;
}
#square {
background: lime;
height: 100%;
margin: auto;
}
<div id="container">
<div id="square"></div>
</div>

Try this:
width: 100vh;
height: 100vh;
Demo:
body {
margin: 0;
}
.square {
width: 100vh;
height: 100vh;
background: grey;
margin: auto;
}
<div class="square"></div>
jsFiddle

.row {
display:table;
width:100%;
}
.row div {
display:table-cell;
vertical-align:top;
}
.col-1 ,.col-3{
background-color:red;}
.col-2 {
background-color:green;
}
<div class=row>
<div class="col-1">1</div>
<div class="col-2">2</div>
<div class="col-3">3</div>
</div>

You can work with Vh and calc
for this html
<div class="square"></div>
you can use this css
body {
background-color: #515151;
}
.square {
margin: 1vh auto;
background-color: #cacaca;
height: 98vh;
width: calc(99vh * 0.8);
}
you can see how works in this example

Related

Problem with max-height not working in css grid element [duplicate]

This question already has an answer here:
Why does height: 100% on a child element not apply when the parent element has a min-height/max-height value but no height value?
(1 answer)
Closed 1 year ago.
Edit: I misunderstood my problem quite a bit, as #onkar-ruikar explained below. I still don't know exactly how to handle the consequences of the cyclic dependencies in a way which really works for me, but I would have had to completely rewrite my question here, so I'm treating both as separate problems, marked this one as solved and asked a follow up to it: Dealing with cyclic dependencies of percentage sized boxes css (specifically how to get a max-height)
I have a problem with max-height not working properly in grid elements. Basically I have a grid element with flexible size, and within it I am showing an iframe. I want to scale the iframe to its full size, and have a surrounding div which fits to the iframe size, with a maximum size equal to the grid element, and from there scroll. My problem is, that max-height somehow does not work properly. I prepared a jsfiddle for this:
For this code I get different results for using "height: 100%;" or "max-height: 100%" in #grid1:
#grid0 {
display: grid;
grid-template-rows: 50px;
grid-template-columns: 200px;
height: 500px;
}
#grid1 {
height: fit-content;
max-height: 100%;
background: blue;
/* it works using height instead of max-height
height: 100%;
*/
}
#out {
height: auto;
max-height: 100%;
overflow: hidden;
background: purple;
width: 150px;
}
#large {
height: 100px;
width: 100px;
background: red;
}
<div id='grid0'>
<div id='grid1'>
<div id='out'>
<div id='large'>
</div>
</div>
</div>
</div>
Compare the results between this and commenting in the "height: 100%;"-line in the css for #grid1. Interestingly when inspecting #grid1 with firefox its height gets shown as it should be even with max-width, but clearly it is not rendered this way.
I tried to introduce another container around #out, set its height to auto and max-height to 100% and #grid1's height to 100% fix, because I thought it might be the "fit-content", but it did not work either...
Does anyone have suggestions how to get around this, or am I doing something wrong?
I would be happy about every hint!
I think the issue not actually related to the grid. I can reproduce it using normal block element as well.
#div0 {
width: 200px;
height: 50px;
}
#div1 {
height: fit-content;
max-height: 100%;
background: blue;
/* it works using height instead of max-height
height: 100%;
*/
}
#out {
height: auto;
max-height: 100%;
overflow: hidden;
background: purple;
width: 150px;
}
#large {
height: 100px;
width: 100px;
background: red;
}
<div id='div0'>
<div id='div1'>
<div id='out'>
<div id='large'>
</div>
</div>
</div>
</div>
My explanation is based on following specs:
Percentages specify sizing of a box with respect to the box’s containing block.ref
Intrinsic sizing determines sizes based on the contents of an element, without regard for its context.
That is using min-content, max-content, and fit-content values.
In your case #grid1(blue) is working as expected it is getting 50px height because of max-height: 100%. height:fit-content has no effect, if it did then blue would be 100px high.
The problem is that #out(purple) is overflowing. It is because the container blue is saying my height is dependent on my children(because of fit-content). When you set height:100% you say it's height is dependent on container #grid0 and not #out(purple). When both #grid1(blue) and #out(purple) purple say they depend on each other then it creates a cyclic dependency.
Sometimes the size of a percentage-sized box’s containing block depends on the intrinsic size contribution of the box itself, creating a cyclic dependency. When calculating the intrinsic size contribution of such a box (including any calculations for a content-based automatic minimum size), a percentage value that resolves against a size in the same axis as the intrinsic size contribution (a cyclic percentage size) is resolved specially: ref
If the box is non-replaced, then the entire value of any max size property or preferred size property (width/max-width/height/max-height) specified as an expression containing a percentage (such as 10% or calc(10px + 0%)) that is cyclic is treated for the purpose of calculating the box’s intrinsic size contributions only as that property’s initial value. For example, given a box with width: calc(20px + 50%), its max-content contribution is calculated as if its width were auto.
That means percentage heights are treated as auto on purple. So it relies on it's children #large for height. As large has 100px height purple gets 100px height as well.
You said you added one more container around #out:
#div0 {
width: 200px;
height: 50px;
}
#div1 {
max-height: 100%;
background: blue;
/* it works using height instead of max-height*/
height: 100%;
}
#div2 {
height: auto;
/* uncomment following to get desired result */
/* height: inherit;*/
max-height: 100%;
}
#out {
height: auto;
max-height: 100%;
overflow: hidden;
background: purple;
width: 150px;
}
#large {
height: 100px;
width: 100px;
background: red;
}
<div id='div0'>
<div id='div1'>
<div id="div2">
<div id='out'>
<div id='large'>
</div>
</div>
</div>
</div>
</div>
In this code if you use inherit instead of auto it fixes the issue. Inherit makes it dependent on parent and auto on children.

CSS - Can I use an object's Width in Calc()?

I want something like this:
height: calc(width + 5px);
This is just a small example but I'm ultimately looking for a way to use the set width as a part of the calculation, not a simpler way to solve the example. Any searches I do just gives examples on how to use calc() to set the width. Thanks!
Use a container query. The cqw unit represents 1% of the item's width. You can use that unit in your calc() function.
I made the item resizable in the following demo, to make the functionality easier to observe.
.container {
container-type: inline-size;
background: #ddd;
/* Make resizable, for demo purposes */
resize: horizontal;
overflow: hidden;
}
.item {
background: orange;
}
#container (min-width: 0) {
.item {
/* 50% of container width + 30px */
height: calc(50cqw + 30px);
}
}
<div class="container">
<div class="item">Resize me!</div>
</div>
Note that, as of 2022, container queries are very new and not supported in all browsers. Modern Chrome and Safari are supported, though.
The closest you might be able to come with pure CSS is combining CSS variables with calc
:root {
--width: 100px;
}
div {
border: 1px solid red;
height: calc(var(--width) + 5px);
width: var(--width);
}
<div>
test
</div>
This will let you define a CSS variable --width and use it to calculate a new height for the div.

How do I make two columns--one flexible--that wrap when necessary?

I need two columns, basically blocks side-by-side, that wrap when necessary for a responsive design.
The issue that I'm running into is that the first column/block is statically sized, but the second column/block needs to fill the remaining width. However, they should still wrap when necessary.
Say the left-most block has a static width of 200px, while the right-most fills the remaining width, BUT with a min-width of 300px. That way it should wrap (the second block placed below the first block instead of on the right side) when necessary.
I've tried a variety of methods to no avail--floating the left block, using absolute position, etc., but I can't get the results I'm looking for.
Hopefully it's possibly using CSS alone, and not using a CSS3 media query to show/hide two different versions. Or resorting to JS... :P
Did you want something like this
HTML
<div class="outer">
<div class="leftBar">Test</div>
<div class="rightCnt"></div>
</div>
CSS
* {margin: 0}
.leftBar {
width: 200px;
min-height: 600px;
float: left;
background: red;
}
.rightCnt {
margin-left: 200px;
min-height: 600px;
background: yellow;
}
#media (max-width : 500px) {
.leftBar {
float: none;
width: auto;
min-height: 200px;
}
.rightCnt {
margin-left: 0;
}
}

How to always center a flexible square in viewport with pure CSS?

I know this question: Height equal to dynamic width (CSS fluid layout)
But I want more!! I want a flexible container which has always the aspect ratio of a square but with max-height and max-width of 100% of the page (the window element) and on the top of it is always vertically and horizontally centered.
Like this:
// Container matches height of the window
// container-height = 100%; container-width = absolute-container-height
-page/browser-window-
- ####### -
- #cont-# -
- #ainer# -
- ####### -
---------------------
// container matches width of the window
// container-width = 100%; container-height = absolute-container-width
--page---
- -
- -
-#######-
-#######-
-#######-
-#######-
- -
- -
---------
Is it possible to achieve this with pure css (and even better cross-browser)?
Edit:
I know there is calc() for css3, but due to the poor mobile browser-support, I don't want to use it.
Edit2:
Seems like, I didn't make myself clear enough. I need height and width of the wrapper to match the height OR the width of the window, depending on which is smaller.The square-container should never exceed the smaller value of the window-height/width.
This is, how it would be done with jQuery:
// container/#main-wrapper has top: 50%, left: 50%, position: absolute via css
$(window).resize(function () {
var $ww = $(window).width();
var $wh = $(window).height();
if ($ww > $wh) {
$('#main-wrapper').css({
height: $wh,
width: $wh,
marginTop : ($wh / 2) * -1,
marginLeft : ($wh / 2) * -1
});
} else {
$('#main-wrapper').css({
height: $ww,
width: $ww,
marginTop : ($ww / 2) * -1,
marginLeft : ($ww / 2) * -1
});
}
});
I finally figured it out. The magic ingredients are the view-port units.
Given this html structure:
.l-table
.l-table-cell
.square
You can use this css (well actuall its scss, but you get the idea) to make it work
html,
body{
height: 100%
}
l-table{
background: orange;
display: table;
height: 100%;
width: 100%;
}
.l-table-cell{
display: table-cell;
vertical-align: middle;
border: 2px solid black;
}
.square{
background: red;
margin: auto;
#media (orientation:landscape) {
width: 70vh;
height: 70vh;
}
#media screen and (orientation:portrait) {
width: 70vw;
height: 70vw;
}
}
http://codepen.io/johannesjo/pen/rvFxJ
For those who need it, there is a polyfill.
EDIT: Since writing the below, I appealed on Twitter and got a response from Brian Johnson. He came up with a solution that isn't 100% perfect semantically, but it's pretty damn good and I'll certainly be using it. He asked that I share it in this discussion. LINK
I'm having the same issue right now and I was just typing out pretty much this exact question, so although I can't answer it, I wanted to share what I've found so far in case it helps anyone come up with the final solution.
To clarify, I need my content to fit into a square which fills 60% of the browser's width if portrait or 60% of the height if landscape.
However, this square must never exceed the width or height of the viewport.
Using the technique found here I've managed to create the fluid square, but it still exceeds the viewport when landscape.
width: 60%;
height:0;
padding-bottom: 60%;
Link to Codepen example
I have tried flipping that technique on it's side for landscape but that doesn't work. (You can see that code in the above example, noted out.)
I can't use a simple max-height property because the height is being worked out by the padding-bottom property.
I've thought about adding an extra div as someone else has suggested (C-Link's post is really interesting) but I can't work out how I'd get it to do what we want it do here.
html
<div id="outer">
<div id="inner">YOUR CONTENTS HERE
</div>
</div>
css
html, body{
height: 100%;
}
#outer{
max-height: 100%;
max-width: 100%;
position: relative;
height: 100%;
}
#inner{
position: relative;
top: 50%;
margin-top: -50% auto auto auto;
background: red;
text-align: center;
color: yellow;
}
See this fiddle.
How about if you take the earlier concept a step further with a similar div as a container. The container has an added max-height & width. I tried this and the container does not throw a scrollbar at me. It is quite interesting in behavior I must say myself. Does this work for you?
<div id="container">
<div id="centered">A DIV</div>
</div>
#container {
top:0;
bottom:0;
right:0;
left:0;
margin:auto;
position:absolute;
width:200px;
height:200px;
max-width:100%;
max-height:100%;
background:#00f;
padding:0;
}
#centered {
background: green;
bottom: 0;
height: 80px;
left: 0;
margin: auto;
position: absolute;
top: 0;
right: 0;
width: 80px;
}
updated fiddle:
http://jsfiddle.net/djwave28/mBBJM/96/

CSS 100% height with padding/margin

With HTML/CSS, how can I make an element that has a width and/or height that is 100% of it's parent element and still has proper padding or margins?
By "proper" I mean that if my parent element is 200px tall and I specify height = 100% with padding = 5px I would expect that I should get a 190px high element with border = 5px on all sides, nicely centered in the parent element.
Now, I know that that's not how the standard box model specifies it should work (although I'd like to know why, exactly...), so the obvious answer doesn't work:
#myDiv {
width: 100%
height: 100%;
padding: 5px;
}
But it would seem to me that there must be SOME way of reliably producing this effect for a parent of arbitrary size. Does anyone know of a way of accomplishing this (seemingly simple) task?
Oh, and for the record I'm not terribly interested in IE compatibility so that should (hopefully) make things a bit easier.
EDIT: Since an example was asked for, here's the simplest one I can think of:
<html style="height: 100%">
<body style="height: 100%">
<div style="background-color: black; height: 100%; padding: 25px"></div>
</body>
</html>
The challenge is then to get the black box to show up with a 25 pixel padding on all edges without the page growing big enough to require scrollbars.
I learned how to do these sort of things reading "PRO HTML and CSS Design Patterns". The display:block is the default display value for the div, but I like to make it explicit. The container has to be the right type; position attribute is fixed, relative, or absolute.
.stretchedToMargin {
display: block;
position:absolute;
height:auto;
bottom:0;
top:0;
left:0;
right:0;
margin-top:20px;
margin-bottom:20px;
margin-right:80px;
margin-left:80px;
background-color: green;
}
<div class="stretchedToMargin">
Hello, world
</div>
Fiddle by Nooshu's comment
There is a new property in CSS3 that you can use to change the way the box model calculates width/height, it's called box-sizing.
By setting this property with the value "border-box" it makes whichever element you apply it to not stretch when you add a padding or border. If you define something with 100px width, and 10px padding, it will still be 100px wide.
box-sizing: border-box;
See here for browser support. It does not work for IE7 and lower, however, I believe that Dean Edward's IE7.js adds support for it. Enjoy :)
The solution is to NOT use height and width at all! Attach the inner box using top, left, right, bottom and then add margin.
.box {margin:8px; position:absolute; top:0; left:0; right:0; bottom:0}
<div class="box" style="background:black">
<div class="box" style="background:green">
<div class="box" style="background:lightblue">
This will show three nested boxes. Try resizing browser to see they remain nested properly.
</div>
</div>
</div>
The better way is with the calc() property. So, your case would look like:
#myDiv {
width: calc(100% - 10px);
height: calc(100% - 10px);
padding: 5px;
}
Simple, clean, no workarounds. Just make sure you don't forget the space between the values and the operator (eg (100%-5px) that will break the syntax. Enjoy!
According the w3c spec height refers to the height of the viewable area e.g. on a 1280x1024 pixel resolution monitor 100% height = 1024 pixels.
min-height refers to the total height of the page including content so on a page where the content is bigger than 1024px min-height:100% will stretch to include all of the content.
The other problem then is that padding and border are added to the height and width in most modern browsers except ie6(ie6 is actually quite logical but does not conform to the spec). This is called the box model. So if you specify
min-height: 100%;
padding: 5px;
It will actually give you 100% + 5px + 5px for the height. To get around this you need a wrapper container.
<style>
.FullHeight {
height: auto !important; /* ie 6 will ignore this */
height: 100%; /* ie 6 will use this instead of min-height */
min-height: 100%; /* ie 6 will ignore this */
}
.Padded {
padding: 5px;
}
</style>
<div class="FullHeight">
<div class="Padded">
Hello i am padded.
</div
</div>
1. Full height with padding
body {
margin: 0;
}
.container {
min-height: 100vh;
padding: 50px;
box-sizing: border-box;
background: silver;
}
<div class="container">Hello world.</div>
2. Full height with margin
body {
margin: 0;
}
.container {
min-height: calc(100vh - 100px);
margin: 50px;
background: silver;
}
<div class="container">Hello world.</div>
3. Full height with border
body {
margin: 0;
}
.container {
min-height: 100vh;
border: 50px solid pink;
box-sizing: border-box;
background: silver;
}
<div class="container">Hello world.</div>
This is one of the outright idiocies of CSS - I have yet to understand the reasoning (if someone knows, pls. explain).
100% means 100% of the container height - to which any margins, borders and padding are added. So it is effectively impossible to get a container which fills it's parent and which has a margin, border, or padding.
Note also, setting height is notoriously inconsistent between browsers, too.
Another thing I've learned since I posted this is that the percentage is relative the container's length, that is, it's width, making a percentage even more worthless for height.
Nowadays, the vh and vw viewport units are more useful, but still not especially useful for anything other than the top-level containers.
Another solution is to use display:table which has a different box model behaviour.
You can set a height and width to the parent and add padding without expanding it. The child has 100% height and width minus the paddings.
JSBIN
Another option would be to use box-sizing propperty. Only problem with both would be they dont work in IE7.
Another solution: You can use percentage units for margins as well as sizes. For example:
.fullWidthPlusMargin {
width: 98%;
margin: 1%;
}
The main issue here is that the margins will increase/decrease slightly with the size of the parent element. Presumably the functionality you would prefer is for the margins to stay constant and the child element to grow/shrink to fill changes in spacing. So, depending on how tight you need your display to be, that could be problematic. (I'd also go for a smaller margin, like 0.3%).
A solution with flexbox (working on IE11): (or view on jsfiddle)
<html>
<style>
html, body {
height: 100%; /* fix for IE11, not needed for chrome/ff */
margin: 0; /* CSS-reset for chrome */
}
</style>
<body style="display: flex;">
<div style="background-color: black; flex: 1; margin: 25px;"></div>
</body>
</html>
(The CSS-reset is not necessarily important for the actual problem.)
The important part is flex: 1 (In combination with display: flex at the parent). Funnily enough, the most plausible explanation I know for how the Flex property works comes from a react-native documentation, so I refer to it anyway:
(...) flex: 1, which tells a component to fill all available space, shared evenly amongst other components with the same parent
To add -webkit and -moz would be more appropriate
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
Frank's example confused me a bit - it didn't work in my case because I didn't understand positioning well enough yet. It's important to note that the parent container element needs to have a non-static position (he mentioned this but I overlooked it, and it wasn't in his example).
Here's an example where the child - given padding and a border - uses absolute positioning to fill the parent 100%. The parent uses relative positioning in order to provide a point of reference for the child's position while remaining in the normal flow - the next element "more-content" is not affected:
#box {
position: relative;
height: 300px;
width: 600px;
}
#box p {
position: absolute;
border-style: dashed;
padding: 1em;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
<div id="box">
<p>100% height and width!</p>
</div>
<div id="more-content">
</div>
A useful link for quickly learning CSS positioning
This is the default behavior of display: block The fastest way that you can fix it in 2020 is to set display: 'flex' of parent element and padding e.g. 20px then all its children will have 100% height relative to its height.
Border around div, rather than page body margin
Another solution - I just wanted a simple border around the edge of my page, and I wanted 100% height when the content was smaller than that.
Border-box didn't work, and the fixed positioning seemed wrong for such a simple need.
I ended up adding a border to my container, instead of relying on the margin of the body of the page - it looks like this :
body, html {
height: 100%;
margin: 0;
}
.container {
width: 100%;
min-height: 100%;
border: 8px solid #564333;
}
<style type="text/css">
.stretchedToMargin {
position:absolute;
width:100%;
height:100%;
}
</style>

Resources