This question already has answers here:
Maintain the aspect ratio of a div with CSS
(37 answers)
Closed 4 years ago.
Im trying to achieve the following:
Where the blue box is of variable height and the yellow box is always of height 50% of the blue box.
Its fairly simple using flex
<div style="display:flex;align-items:center">
<div id="yellow" style="height:50%">
</div>
</div>
The problem is that im trying to keep the inner box a specific ratio, in this case square. How do i approach this?
Bonus points:
How do i generally specify a ratio? Is there a solution that works not only for 1:1 but any x:y?
How would i do that without using flexbox while potentially still aiming for a)?
Extra information: The blue box is always wider than higher, think a button.
I don't think there is a way to define the width using the height (even if we can do the opposite using some trick like padding) but an idea is to rely on a square image that you make invisible in order to keep the ratio. Then the content should be positionned:
#blue {
display: flex;
align-items: center;
justify-content:center;
height:80vh;
background: blue;
}
#yellow {
height: 50%;
background: yellow;
position:relative;
}
img {
max-height:100%;
visibility:hidden;
}
#yellow .content {
position:absolute;
top:0;
right:0;
left:0;
bottom:0;
}
<div id="blue" >
<div id="yellow" >
<img src="https://picsum.photos/500/500?image=1069" >
<div class="content">Some content here</div>
</div>
</div>
But in case the height of the blue is a fixed value, better rely on CSS variable like this:
#blue {
display: flex;
align-items: center;
justify-content:center;
--h:80vh;
height:var(--h);
background: blue;
}
#yellow {
height: calc(var(--h) / 2);
width:calc(var(--h) / 2);
background: yellow;
position:relative;
}
<div id="blue" >
<div id="yellow" >
<div class="content">Some content here</div>
</div>
</div>
A similar answer to the one provided by Temani Afif, but using an svg instead of an image (so no need to the extra request).
Also, it's easier to adapt it to arbitrary aspect ratios
.container {
height: 150px;
background-color: lightblue;
display: flex;
align-items: center;
justify-content: center;
margin: 10px;
}
.aspectRatio {
display: grid;
background-color: yellow;
height: 50%;
}
.aspectRatio svg {
height: 100%;
border: solid 1px red;
animation: resize 1s infinite;
}
.aspectRatio > * {
grid-area: 1 / 1 / 2 / 2;
}
#keyframes resize {
from {height: 100%;}
to {height: 99.9%;}
}
<div class="container">
<div class="aspectRatio">
<svg viewBox="0 0 1 1"></svg>
<div class="inner">square</div>
</div>
</div>
<div class="container">
<div class="aspectRatio">
<svg viewBox="0 0 4 3"></svg>
<div class="inner">ratio 4/3</div>
</div>
</div>
See if this can help you,
.outer {
background-color: lightblue;
height: 100px; /* Change as per your requirement */
display: flex;
align-items: center;
justify-content: center;
max-width: 200px; /* You can Remove this */
}
.inner {
background-color: lightyellow;
height: 50%;
width: 50px;
}
<div style="" class="outer">
<div id="yellow" class="inner">
</div>
</div>
If you rotate by 90deg, it's possible :)
variable width and height of the parent (and ratio)
child is always 50% as tall as its parent
and a square
It'll surimpose to other content if it wants to because of the transform though.
⇒ Codepen
.flex {
display: table-cell; /* allows "vertical" centering (not possible with flex/grid here because of the padding-top trick on child) */
width: 12rem;
height: 20rem;
vertical-align: middle; /* "vertical" centering */
transform: rotate(90deg) translateY(-50%); /* vertical becomes horizontal */
background-color: lightblue;
}
.flex.large {
height: 35rem;
}
.item {
width: 50%;
height: 0;
margin-left: 25%; /* "horizontal" centering */
padding-top: 50%; /* padding-top trick for a square */
background-color: lightyellow;
}
<div class="flex">
<div class="item"></div>
</div>
<hr>
<div class="flex large">
<div class="item"></div>
</div>
Try this if it can help you.(with out flex)
.outerdiv
{
background-color: lightblue;
height: 100px;
display: grid;
align-items: center;
}
.innerdiv
{
background-color: lightyellow;
height: 50%;
width: 50px;
margin:0 auto;
}
<div style="" class="outerdiv">
<div id="yellow" class="innerdiv"></div>
</div>
Related
Let I use transform in this:
.parent {
display: inline-flex;
flex-direction: column;
align-items: center;
}
.first {
height: 100px;
}
.second {
height: 100px;
transform: translateY(-50%);
}
<div class="parent">
<div class="first"></div>
<div class="second"></div>
</div>
So, after using transform: translateY(-50%); the height of parent element should reduce as it is not using full space. But the height of parent div is still 200px.
How can we remove extra space and I want to use transform only?
https://jsfiddle.net/r4c3g02o/12/
translate only visually moves an element. It does not effect the flow of other elements in the document.
Perhaps the simplest way to achieve your desired result would be a negative margin.
.parent {
border: 1px solid purple;
}
.first {
height: 100px;
background: yellow;
}
.second {
height: 100px;
margin-top: -50px;
background: salmon;
}
<div class="parent">
<div class="first">
First
</div>
<div class="second">
second
</div>
</div>
I think y can use SVG and <animateTransform> so)
I want for the boxes to be more responsive, and to keep 1:1 ratio all the time.
When I set min-width to the .box1, .box2 they always take the whole width of the .box ! And it is like they don't respond to height?
I don't want to boxes be full width (or height) of the flex items, since i want some space between them, and I don't want them to overflow their .box container,..( I want keep them inside)
I know I can use media queries to resize the .box1,.box2,.box3, .. but is there any other way?
.grid {
width: 100%;
height: 100%;
display: flex;
flex-flow: row wrap;
justify-content: space-around;
//margin:0 -40px;
}
/*first two children*/
.grid>.box:not(:last-child) {
background: grey;
width: 50%;
}
.box1,
.box2,
.box3 {
border: 2px solid #111;
width: 100px;
height: 100px;
transform: rotate(-45deg);
text-align: center;
}
.box1,
.box2 {
margin: 0 auto;
}
.box p {
position: relative;
color: white;
}
<div class="wrapper">
<div class="grid">
<div class="box">
<div class="box1">
<p>Here is something !</p>
</div>
</div>
<div class="box ">
<div class="box2"></div>
</div>
<div class="box ">
<div class="box3"></div>
</div>
</div>
</div>
I didnt get your complete query but if you want to make square responsive without adjusting its height manually then use padding hack
.box1,
.box2,
.box3{
width: 100%;
height: 0;
padding-bottom: 50%;
postition:relative
background: red;
}
I have to create a layout which looks like:
I've prepared code like:
.red {
width: 50px;
height: 50px;
background-color: red;
margin-right: 20px;
}
.yellow {
width: 50px;
height: 50px;
background-color: yellow;
}
.blue {
width: 50px;
height: 50px;
background-color: blue;
justify-self: end;
}
.wrapper {
display: flex;
background-color: green;
width: 100%;
}
<div class="wrapper">
<div class="red"> </div>
<div class="yellow"> </div>
<div class="blue"> </div>
</div>
But this blue div don't want to align to the right side:
Here you can a have a preview of that:
https://jsfiddle.net/ncszob80/17/
I know that I can fix it with margin-left: auto css style for blue div.
But I'm wondering if there is some possibility of creating such layout only by using flex functionality.
So:
we can use only flex functionalities
there needs to be some margin between red div and yellow one
blue div needs to be at the very right
How to achieve that?
You wrote:
I know that I can fix it with margin-left: auto css style for blue div. But I'm wondering if there is some possibility of creating such layout only by using flex functionality.
Actually, margin-left: auto is flex functionality. It's a feature of flex layout.
From the flexbox specification:
§ 8.1. Aligning with auto
margins
Also see:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
In summary, just use the auto margin. It's the cleanest, simplest and most efficient solution.
My best solution for you would be to change your DOM structure a little bit - but it accomplishes what you're looking for:
.left {
display: flex;
}
.red {
width: 50px;
height: 50px;
background-color: red;
margin-right: 20px;
}
.yellow {
width: 50px;
height: 50px;
background-color: yellow;
}
.blue {
width: 50px;
height: 50px;
background-color: blue;
}
.wrapper {
display: flex;
background-color: green;
width: 100%;
justify-content: space-between;
}
<div class="wrapper">
<div class="left">
<div class="red"> </div>
<div class="yellow"> </div>
</div>
<div class="right">
<div class="blue"> </div>
</div>
</div>
Basically, I wrapped your boxes in .left and .right, and then changed the .wrapper to justify-content: space-between so that the .right box is shoved to the right. Then, we make .left { display: flex; } to fix the issue with those boxes stacking without doing this, or changing the elements inside to display: inline; or display: inline-block;.
You can use nested flex boxes. Make the flex wrapper for your blue item and justify that to the end:
.wrapper {
display: flex;
background-color: green;
width: 100%;
}
.red {
width: 50px;
height: 50px;
background-color: red;
margin-right: 20px;
}
.yellow {
width: 50px;
height: 50px;
background-color: yellow;
}
.blueWrap {
width: 100%;
height: 50px;
display: flex;
justify-content: flex-end;
}
.blue {
width: 50px;
height: 50px;
background-color: blue;
}
<div class="wrapper">
<div class="red"> </div>
<div class="yellow"> </div>
<div class="blueWrap">
<div class="blue"></div>
</div>
</div>
Aside from changing your DOM structure or using the margin-left: auto fix CSS Grid is fantastic for this type of layout. I know you said only Flexbox but if you don't want any of the other solutions Grid might be a nice alternative. You can mix Flex functionality within the grid as well for finer control. I do this regularly to achieve the layout I'm in need of and it works well!
Happy coding!
Here is another idea if you don't want to consider margin:auto and without changing your html but like said in the accepted answer, margin is a feature of flexbox:
.red {
width: 50px;
height: 50px;
background-color: red;
margin-right: 20px;
}
.yellow {
width: 50px;
height: 50px;
background-color: yellow;
}
.blue {
width: 50px;
height: 50px;
background-color: blue;
order:1; /*make the blue the last element*/
}
.wrapper {
display: flex;
background-color: green;
width: 100%;
}
.wrapper:after {
content:"";
flex-grow:1; /*make this hidden element to take all the space and push the blue*/
}
<div class="wrapper">
<div class="red"> </div>
<div class="yellow"> </div>
<div class="blue"> </div>
</div>
This question already has answers here:
Maintain the aspect ratio of a div with CSS
(37 answers)
Closed 4 years ago.
Im trying to achieve the following:
Where the blue box is of variable height and the yellow box is always of height 50% of the blue box.
Its fairly simple using flex
<div style="display:flex;align-items:center">
<div id="yellow" style="height:50%">
</div>
</div>
The problem is that im trying to keep the inner box a specific ratio, in this case square. How do i approach this?
Bonus points:
How do i generally specify a ratio? Is there a solution that works not only for 1:1 but any x:y?
How would i do that without using flexbox while potentially still aiming for a)?
Extra information: The blue box is always wider than higher, think a button.
I don't think there is a way to define the width using the height (even if we can do the opposite using some trick like padding) but an idea is to rely on a square image that you make invisible in order to keep the ratio. Then the content should be positionned:
#blue {
display: flex;
align-items: center;
justify-content:center;
height:80vh;
background: blue;
}
#yellow {
height: 50%;
background: yellow;
position:relative;
}
img {
max-height:100%;
visibility:hidden;
}
#yellow .content {
position:absolute;
top:0;
right:0;
left:0;
bottom:0;
}
<div id="blue" >
<div id="yellow" >
<img src="https://picsum.photos/500/500?image=1069" >
<div class="content">Some content here</div>
</div>
</div>
But in case the height of the blue is a fixed value, better rely on CSS variable like this:
#blue {
display: flex;
align-items: center;
justify-content:center;
--h:80vh;
height:var(--h);
background: blue;
}
#yellow {
height: calc(var(--h) / 2);
width:calc(var(--h) / 2);
background: yellow;
position:relative;
}
<div id="blue" >
<div id="yellow" >
<div class="content">Some content here</div>
</div>
</div>
A similar answer to the one provided by Temani Afif, but using an svg instead of an image (so no need to the extra request).
Also, it's easier to adapt it to arbitrary aspect ratios
.container {
height: 150px;
background-color: lightblue;
display: flex;
align-items: center;
justify-content: center;
margin: 10px;
}
.aspectRatio {
display: grid;
background-color: yellow;
height: 50%;
}
.aspectRatio svg {
height: 100%;
border: solid 1px red;
animation: resize 1s infinite;
}
.aspectRatio > * {
grid-area: 1 / 1 / 2 / 2;
}
#keyframes resize {
from {height: 100%;}
to {height: 99.9%;}
}
<div class="container">
<div class="aspectRatio">
<svg viewBox="0 0 1 1"></svg>
<div class="inner">square</div>
</div>
</div>
<div class="container">
<div class="aspectRatio">
<svg viewBox="0 0 4 3"></svg>
<div class="inner">ratio 4/3</div>
</div>
</div>
See if this can help you,
.outer {
background-color: lightblue;
height: 100px; /* Change as per your requirement */
display: flex;
align-items: center;
justify-content: center;
max-width: 200px; /* You can Remove this */
}
.inner {
background-color: lightyellow;
height: 50%;
width: 50px;
}
<div style="" class="outer">
<div id="yellow" class="inner">
</div>
</div>
If you rotate by 90deg, it's possible :)
variable width and height of the parent (and ratio)
child is always 50% as tall as its parent
and a square
It'll surimpose to other content if it wants to because of the transform though.
⇒ Codepen
.flex {
display: table-cell; /* allows "vertical" centering (not possible with flex/grid here because of the padding-top trick on child) */
width: 12rem;
height: 20rem;
vertical-align: middle; /* "vertical" centering */
transform: rotate(90deg) translateY(-50%); /* vertical becomes horizontal */
background-color: lightblue;
}
.flex.large {
height: 35rem;
}
.item {
width: 50%;
height: 0;
margin-left: 25%; /* "horizontal" centering */
padding-top: 50%; /* padding-top trick for a square */
background-color: lightyellow;
}
<div class="flex">
<div class="item"></div>
</div>
<hr>
<div class="flex large">
<div class="item"></div>
</div>
Try this if it can help you.(with out flex)
.outerdiv
{
background-color: lightblue;
height: 100px;
display: grid;
align-items: center;
}
.innerdiv
{
background-color: lightyellow;
height: 50%;
width: 50px;
margin:0 auto;
}
<div style="" class="outerdiv">
<div id="yellow" class="innerdiv"></div>
</div>
I have a container that is 100vh (minus the fixed nav height).
<section class="container">
Inside this container I have some text:
<div class="text">
<p>title</p>
</div>
Which can be of any length as the content is dynamic.
Below this text I have an image:
<div class="image">
<img src="https://s-media-cache-ak0.pinimg.com/736x/d1/a6/64/d1a664bca214bf785a293cbc87950fc4.jpg">
</div>
The image needs to fill the rest of the 100vh (- nav height) container.
I use:
.container{
display:flex;
flex-flow: column nowrap;
....
Fiddle
The issue I am having is that I need the image to be the height of the rest of the space.
How can I do this? In my fiddle, if your screen is small it is being cut off and if your screen is large it does not fill the space. Height: 100% fails to work, making it too large.
Flex solutions only please, no table tricks - thanks!
Make the image container (.image) a flex container with height: 100%.
You can then fine-tune the image's aspect ratio and alignment with object-fit / object-position.
nav {
position:fixed;
background:grey;
width:100%;
height: 100px;
}
main {
padding-top: 100px;
}
.container {
display:flex;
flex-flow: column nowrap;
height: calc(100vh - 100px);
background: green;
border: 3px solid brown;
}
.text { background: yellow; }
/* ***** NEW ***** */
.image {
display: flex;
justify-content: center;
height: 100%;
}
img {
width: 100%;
object-fit: contain;
}
<nav>Nav</nav>
<main>
<section class="container">
<div class="text"><p>title</p></div>
<div class="image">
<img src="https://s-media-cache-ak0.pinimg.com/736x/d1/a6/64/d1a664bca214bf785a293cbc87950fc4.jpg">
</div>
</section>
<section class="container">
<div class="text"><p>hello</p></div>
<div class="image">
<img src="https://s-media-cache-ak0.pinimg.com/736x/d1/a6/64/d1a664bca214bf785a293cbc87950fc4.jpg">
</div>
</section>
</main>
Revised Fiddle
Note that the object-fit property is not supported by IE. For more details and a workaround see: https://stackoverflow.com/a/37127590/3597276
Maybe not exactly what you wanted, but if you move the image to a div and use it as a background, you can get the desired effect.
Fiddle
HTML:
Nav
title
<section class="container">
<div class="text">
<p>hello</p>
</div>
<div class="imageWrap">
<div class="image"></div>
</div>
</section>
</main>
CSS:
nav {
background: grey;
width: 100%;
height: 100px;
position: fixed;
}
main{
padding-top: 100px;
}
.container{
display: flex;
flex-flow: column nowrap;
height: calc(100vh - 100px);
background: green;
border: 3px solid brown;
}
.imageWrap {
flex: 1;
display: flex;
}
.image {
flex: 1;
background-size: contain;
background-repeat: no-repeat;
background-image: url(https://s-media-cache-ak0.pinimg.com/736x/d1/a6/64/d1a664bca214bf785a293cbc87950fc4.jpg)
}
.text{
background: yellow;
}