Flexbox: shrink image to fit - html

I am trying to design a page with the following properties that will be used as digital signage:
Page height is viewport height (100vh) so that scrolling is impossible
Page is arranged into full-width rows
All rows but the last are static (have pre-defined content)
Last row (which will contain an image slideshow) should fill the remaining space in the viewport.
Here is what I have so far:
body {
margin: 0;
}
div#container {
display: flex;
flex-direction: column;
height: 100vh;
}
div.red {
height: 100px;
background-color: red;
flex: 0 0 auto;
}
div.blue {
height: 150px;
background-color: blue;
flex: 0 0 auto;
}
div.green {
background-color: green;
flex: 0 1 auto;
}
img {
max-height: 100%;
}
<div id="container">
<div class="red"></div>
<div class="blue"></div>
<div class="green">
<img src="http://lorempixel.com/200/300/">
</div>
</div>
https://jsfiddle.net/62qqnx3m/6/
Clearly this is not working because flex is not shrinking the image div to the right size.
I can remove the flex: 0 0 auto from the first two divs, but then they shrink instead.
How can I force the green div/image to take up exactly what space remains, no more, no less?
So if a taller image was supplied, it would shrink even more to fit.
And if an image is smaller than the available space, it should simply display, with the background div still filling the available space.
It seems like max-height:100% would be great for this, but that also does not work.
Furthermore, I have seen examples of how to do this horizontally (which I also need, but am having less trouble with), but I can't figure out how to translate that into vertical scaling.

You can set the position of the green block to relative and the position of the image to absolute.
Also make sure the height of the green block is set to 100% (to take the rest of the height of the page).
This should fix the problem:
body {
margin: 0;
}
div#container {
display: flex;
flex-direction: column;
height: 100vh;
}
div.red {
height: 100px;
background-color: red;
flex: 0 0 auto;
}
div.blue {
height: 150px;
background-color: blue;
flex: 0 0 auto;
}
div.green {
background-color: green;
flex: 0 1 auto;
position: relative;
height: 100%;
}
img
{
max-height: 100%;
position: absolute;
}
<body>
<div id="container">
<div class="red"></div>
<div class="blue"></div>
<div class="green"><img src="http://lorempixel.com/200/300/"></div>
</div>
</body>

So here's what we know:
The page height is 100vh
The first row is static (height: 100px)
The second row is static (height: 150px)
The third row, which contains images, should fill the remaining height
I think the solution lies in basic math:
100vh - 100px - 150px = height of third row
Instead of this in your code:
div.green {
background-color: green;
flex: 0 1 auto;
}
img {
max-height: 100%;
}
Try this:
div.green {
background-color: green;
flex: 1 1 auto;
}
img {
height: calc(100vh - 250px);
}
body {
margin: 0;
}
div#container {
display: flex;
flex-direction: column;
height: 100vh;
}
div.red {
height: 100px;
background-color: red;
flex: 0 0 auto;
}
div.blue {
height: 150px;
background-color: blue;
flex: 0 0 auto;
}
/*
div.green {
background-color: green;
flex: 0 1 auto;
}
img
{
max-height: 100%;
}
*/
div.green {
background-color: green;
flex: 1 1 auto;
}
img {
height: calc(100vh - 250px);
}
<div id="container">
<div class="red"></div>
<div class="blue"></div>
<div class="green">
<img src="http://lorempixel.com/200/300/">
</div>
</div>
revised fiddle

I just change the img class and add to class .green min-height: 100%; Additionally the image is responsive now with that code.
body {
margin: 0;
}
div#container {
display: flex;
flex-direction: column;
height: 100vh;
}
div.red {
height: 100px;
background-color: red;
flex: 0 0 auto;
}
div.blue {
height: 150px;
background-color: blue;
flex: 0 0 auto;
}
div.green {
background-color: green;
flex: 0 1 auto;
min-height: 100%;
}
.green img {
max-width: 100%;
display: block;
margin: 0 auto;
height: auto;
}
<body>
<div id="container">
<div class="red"></div>
<div class="blue"></div>
<div class="green"><img src="http://lorempixel.com/200/300/"></div>
</div>
</body>

Try this fiddle: https://jsfiddle.net/ez4pf8wp/
Added this to the img class:
img {
max-height: 100%;
height: 100%;
display: block;
margin: 0;
}

Related

CSS flex-box, how do I make an item stretch smaller than the content in the cross-axis direction?

I'm trying to make a container with side-by-side divs, one item has a fixed width and the other item has a fixed height. However, the flexible-height div won't shrink below its contents.
.parent {
background: #f00;
display: contents;
}
.container {
background: #0f0;
width: 25vw;
display: flex;
}
.object {
background: #00f;
margin: 10px;
width: 100px;
flex-shrink: 0;
}
.object.flexes-main-axis {
flex-grow: 1;
flex-shrink: 1;
}
.object.sets-cross-axis-size {
height: 75px;
}
.object.shrinks-cross-axis-below-content {
overflow-y: auto;
}
.child {
background: #f0f;
height: 100px;
width: 100px;
}
<div class='parent'>
<div class='container'>
<div id="object1" class='object flexes-main-axis sets-cross-axis-size'></div>
<div id="object2" class='object shrinks-cross-axis-below-content'>
<div class="child"></div>
</div>
</div>
</div>
I'm trying to get object2 to shrink and show a vertical scroll bar when I shrink object1 below 100px. The magenta box represents fixed-height content I can’t shrink, while object1 represents the element I want to control the container’s height.
The blue is hardcoded 75px tall, while the magenta is hardcoded 100px.
As the main .container has no height set it will grow to fit the tallest child. A main .container with a fixed height: ..px would still not make .child shrink. That's Flexbox.
But, if you set the .child to height: 100% and you will see it shrink:
* { outline: 1px dashed } /* for debugging */
.parent {
background: #f00;
display: contents;
}
.container {
background: #0f0;
width: 25vw;
display: flex;
}
.object {
background: #00f;
margin: 10px;
width: 100px;
flex-shrink: 0;
}
.object.flexes-main-axis {
flex-grow: 1;
flex-shrink: 1;
}
.object.sets-cross-axis-size {
height: 75px;
}
.object.shrinks-cross-axis-below-content {
overflow-y: auto;
}
.child {
background: #f0f;
height: 100%; /* Changed from 100px */
width: 100px;
}
<div class='parent'>
<div class='container'>
<div id="object1" class='object flexes-main-axis sets-cross-axis-size'>1</div>
<div id="object2" class='object shrinks-cross-axis-below-content'>
<div class="child">2</div>
</div>
</div>
</div>
So, I figured it out. I needed to add an extra wrapper div between object2 and child with height set to 0. Object2 will scroll to accommodate child overflow, but child's height won't count toward object2's content as far as the flexbox is concerned. Now as you change object1's height below child's height, you'll get a scroll bar in object2.
Also in this version, I made object1 responsive to the viewport width, so you can try it out by resizing the browser window.
.container {
display: flex;
background-color: red;
padding: 10px;
width: 50vw;
}
.sizecontroller {
aspect-ratio: 16 / 9;
background-color: blue;
padding: 10px;
flex-grow: 1;
flex-shrink: 1;
align-self: flex-start;
}
.sizeresponsive {
overflow-y: auto;
background-color: green;
padding: 10px;
flex-grow: 0;
flex-shrink: 0;
}
.sizeeraser {
height: 0;
background-color: purple;
padding: 10px;
}
.sizefixed {
width: 25px;
height: 250px;
background-color: yellow;
padding: 10px;
}
<div class="container">
<div class="sizecontroller"></div>
<div class="sizeresponsive">
<div class="sizeeraser">
<div class="sizefixed"></div>
</div>
</div>
</div>

Make an element scroll inside a grid section

I am trying to make a scrollable area inside my div like the image below. I want only the scrollable area to change height on different screen heights and scrolls accordingly.
The problem, is if the scrollable content is big enough to scroll, it will make the whole page scroll. If its small, the footer stays at the bottom correctly.
Here is a what I have so far
* {
margin: 0;
padding: 0;
width: 100%;
}
.container {
display: grid;
grid-template-rows: auto 1fr auto;
height: 100vh;
}
.header {
height: 30px;
background-color: lightblue;
}
.footer {
height: 30px;
background-color: lightblue;
}
.content {
display: grid;
grid-template-rows: auto 1fr;
}
.profile {
height: 60px;
background-color: lightpink;
}
.tabs {
height: 20px;
background-color: lightgreen;
}
.scroller {
background-color: cyan;
height: 100%;
overflow-y: scroll;
}
.scrollable-content {
background-color: yellow;
width: 200px;
height: 600px;
margin: 0 auto;
position: relative;
}
span {
bottom: 0;
left: 0;
position: absolute;
}
<div class="container">
<div class="header">Header</div>
<div class="content">
<div class="profile">Profile</div>
<div class="tab-control">
<div class="tabs">Tabs</div>
<div class="scroller">
<div class="scrollable-content">scrollable content<span>end</span></div>
</div>
</div>
</div>
<div class="footer">Footer</div>
</div>
Any help is appriciated
It works if you set some elements to have overflow: hidden;. Set that for .container, .content, and .tab-control
.container, .content, .tab-control {
overflow: hidden;
}
You will have a small issue with the .scroller element, part of it will be covered by the footer.
To fix that, add this too:
.tab-control {
display: flex;
flex-direction: column;
}
.scroller {
flex: 100% 1 1;
}
Set a fixed height on scroller. 100vh = height of the browser - 140px (the cumulative height of all the other elements on the page)
Set overflow-y: auto on the bar you want to scroll and you can set the height of .scrollable-content to as big as you want.
.scroller {
background-color: cyan;
height: calc(100vh - 140px);
overflow-y: scroll;
}
.scrollable-content {
background-color: yellow;
width: 200px;
height: 600px;
margin: 0 auto;
position: relative;
overflow-y: auto;
}

Content overflowing flex item despite overflow property

I have a pretty simple page setup in the following manner using flexboxes:
The blue div is supposed to make up 25% in height and the violet div 75%. In case there are too many lines in the blue div, it should stay the same size an show a scrollbar. This works for a few lines, but breaks at some point and the blue div overflows and grows into the violet one. I'm new to flexboxes, so I don't really understand why this is happening. Would I be better off not using flexboxes? Thankful for any hints or pointer at this point.
This is the code I use (run in full page):
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: blue;
overflow: auto;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.</br>
Click to enter lines into the bottom right:</br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
For the overflow property to work properly, the container needs an actual height or max-height. Flex heights (you have flex: 1 on .content) won't cut it.
In order for overflow to have an effect, the block-level container
must have either a set height (height or max-height) or
white-space set to nowrap. ~ MDN
Since you already know the height of the primary container (100vh) and the first two rows (30px and 40px), the rest is simple using the calc() function.
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
.body {
display: flex;
flex-direction: column;
height: 100vh; /* adjustment */
}
#static1 {
flex-shrink: 0; /* disable shrinking */
height: 30px;
/* width: 100%; */
background-color: red;
}
#static2 {
flex-shrink: 0; /* disable shrinking */
height: 40px;
/* width: 100%; */
background-color: orange;
}
#content {
height: calc(100vh - 70px); /* new */
display: flex;
/* flex: 1; */ /* may work in some browsers, but not reliable */
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: aqua; /* adjusted for illustration */
overflow: auto;
}
body {
margin: 0; /* new; override browser default */
}
* {
box-sizing: border-box;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.<br> Click to enter lines into the bottom right:<br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
jsFiddle demo
I hope this is what you mean, but If I'm wrong, apologies. The problem I can see lies in the way you are using flex: 1 & flex: 3 to define the proportions of the right column, without specifying to what height their parent container has, i.e. #right has no height, so the box can always expand as it gets more filled with content.
Please try this, I hope this works and if I can answer anything else, just ask please.
The only thing I changed was your CSS and added max-height: calc(100vh - 70px); to the #right div. And changed overflow: auto; to overflow-y: scroll;
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
max-height: calc(100vh - 70px);
}
#upper {
flex: 3;
height: 75%;
background-color: violet;
}
#lower {
flex: 1;
height: 25%;
background-color: blue;
overflow-y: scroll;
}
Change the top part of CSS to this:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

Canvas pushes other flexbox items outside the container

I am creating a drawing app for mobile and web. The UI structure is as in the attached code sample. I want the canvas to take maximum possible space without pushing other content outside the parent flexbox.
Currently, as I resize the window from portrait to landscape mode, the canvas grows and pushes other boxes out of the view.
Prefer codepen to check the output, as the output window can be easily resized there.
body {
margin: 0;
padding: 0;
}
.root {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
flex: 0 0 60px;
background: #ff9;
}
.box1 {
flex: 0 0 20px;
background: #f9f;
}
canvas {
background: black;
max-width: 100%;
max-height: 100%;
}
.canvas-box {
flex: 1 1 400px;
background: #f99;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.box3 {
flex: 0 0 150px;
background: #9ff;
}
.box4 {
flex: 0 0 20px;
background: #9f9;
}
.in-box1 {
flex: 0 0 40px;
height: 100%;
background: #ff0;
}
.in-box2 {
flex: 1 1;
background: #fff;
}
.in-box3 {
flex: 0 0 40px;
height: 100%;
background: #0ff;
}
<div class="root">
<div class="header"></div>
<div class="box1"></div>
<div class="canvas-box">
<div class="in-box1"></div>
<div class="in-box2">
<canvas width="1000" height="1000" />
</div>
<div class="in-box3"></div>
</div>
<div class="box3"></div>
<div class="box4"></div>
</div>
Codepen link: https://codepen.io/80avin/pen/oNxWyje

How to get the content of the bottom element of a flexbox to be 100% height its container

If I make a flexbox with 2 children and column flow and set the second child to flex-grow 1 the second child expands to fill the flexbox. This works
(ps: Didn't want to clutter the example with safari support so use Chrome or Firefox)
* {
box-sizing: border-box;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
color: white;
}
#outer {
display: flex;
flex-flow: column;
height: 100%;
}
#top {
background-color: red;
}
#bottom {
background-color: blue;
flex: 1 0 auto;
}
<div id="outer">
<div id="top">top</div>
<div id="bottom">bottom (blue)</div>
</div>
But, if I then put a child #inside inside #bottom and set its height to 100% it doesn't increase its height to match even though the flexbox has stretched #bottom.
added css
#inside {
background-color: green;
height: 100%;
}
html
<div id="outer">
<div id="top">top</div>
<div id="bottom">
<div id="inside">inside</div> <!- added ->
</div>
</div>
* {
box-sizing: border-box;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
color: white;
}
#outer {
display: flex;
flex-flow: column;
height: 100%;
}
#top {
background-color: red;
}
#bottom {
background-color: blue;
flex: 1 0 auto;
}
#inside {
background-color: green;
height: 100%;
}
<div id="outer">
<div id="top">top</div>
<div id="bottom">
<div id="inside">inside (green)</div>
</div>
</div>
So I add a height: 100% to #bottom but now bottom is as big as #outer instead of the flex stretched size.
#bottom {
background-color: blue;
flex: 1 0 auto;
height: 100%; /* added */
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
color: white;
}
#outer {
display: flex;
flex-flow: column;
height: 100%;
}
#top {
background-color: red;
}
#bottom {
background-color: blue;
flex: 1 0 auto;
height: 100%;
}
#inside {
background-color: green;
height: 100%;
}
<div id="outer">
<div id="top">top</div>
<div id="bottom">
<div id="inside">inside (green) (would not scroll if working)</div>
</div>
</div>
How do I get #bottom to stretch to fit the flexbox and also get a the child #inside to be 100% height of its container #bottom?
Flex has a quirk where you need to set the height to 0.
Change the #bottom rule's height property to this height: 0;
For the inside to work I changed it to "position: absolute" and as well added a position:relative to the bottom
Update
If you don't want to use absolute position, you can set these 2 css rules like this:
(Note though, that this propagates the original issue if a new inner div is used like the first one)
#bottom {
position: relative;
background-color: blue;
flex: 1 0 auto;
height: 0;
display: flex;
}
#inside {
background-color: green;
flex: 1 0 auto;
}
Sample using "position: absolute"
* {
box-sizing: border-box;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
color: white;
}
#outer {
display: flex;
flex-flow: column;
height: 100%;
}
#top {
background-color: red;
}
#bottom {
position: relative;
background-color: blue;
flex: 1 0 auto;
height: 0;
}
#inside {
background-color: green;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
<div id="outer">
<div id="top">top</div>
<div id="bottom">
<div id="inside">inside (would not scroll if working)</div>
</div>
</div>
How do I get #bottom to stretch to fit the flexbox and also get a the child #inside to be 100% height of its container #bottom?
Just add two lines of code to the CSS.
CSS
#bottom {
background-color: blue;
flex: 1 0 auto;
display: flex; /* NEW */
}
#inside {
flex: 1; /* NEW */
background-color: green;
}
DEMO: http://jsfiddle.net/wf2L8dse/
Here's what's happening:
You have a flex container (#outer) with two flex items (#top and #bottom).
#outer is in column alignment.
#bottom has flex: 1 (i.e., flex-grow: 1), so it occupies all available height in the container.
A new element (#inside) is made a child of #bottom and must occupy the same height as parent.
Solution:
Make #bottom a (nested) flexbox. This activates default flex rules.
One such rule is align-items: stretch, which tells flex items (#inside) to stretch the full height of their container. (Height, in this case, because the flex-direction is row, by default.)
Then apply flex: 1 (or flex-grow: 1) to #inside, so it expands the full width of the container.
Addressing the height: 100% issue
I'm not sure there's anything wrong with your code. You have applied height: 100% to #inside and, as required by the spec when using percentage heights, specified a height for all parent elements including body and the root element (html).
The only thing you may want to consider (to remove the vertical scrollbar on the browser window), is applying overflow: hidden to body.
DEMO: http://jsfiddle.net/wf2L8dse/1/