flex dynamic height fit image - html

Need to fit img into dynamically calculated height of li due to flex. Currently it takes all height, and pull height content. Image should just fit into dynamically calculated height of LI, e.g. 50px and fit into it.
No hardcoded height is needed. Page should be 100% in any device. header/footer - flex: 1, main - flex: 3.
body,
ul {
margin: 0;
padding: 0;
}
html,
body,
.root {
height: 100%;
}
.root {
display: flex;
flex-direction: column;
}
main {
flex: 3;
background: lightyellow;
}
ul {
display: flex;
flex-direction: column;
height: 100%;
}
ul li {
flex: 1;
background: aliceblue;
outline: 1px solid #000;
display: flex;
/* align-items: center; */
justify-content: space-between;
}
img {
/* object-fit: contain; */
}
header,
footer {
flex: 1;
}
header {
background: lightgray;
}
footer {
background: lightblue;
}
<div class="root">
<header>
HEAD
</header>
<main>
<ul>
<li>
<img src="http://via.placeholder.com/200x200" />
<span>text</span>
</li>
<li>
<img src="http://via.placeholder.com/200x200" />
<span>text</span>
</li>
<li>
<img src="http://via.placeholder.com/200x200" />
<span>text</span>
</li>
</ul>
</main>
<footer>
FOOTER
</footer>
</div>
https://jsfiddle.net/sefb3o95/18/

The main thing to make this work is to use Flexbox all the way.
Simply put, one need to nest flex parent/child instead of using height: 100%, to make use of Flexbox's own properties making its flex children stretch/fill their parents, and the reason is that height: 100% is not gonna work properly.
Additionally, having the img as a flex item will cause you some cross browser issues, so wrap it and give it a max-height: 100%, and it will size properly.
See my notes in the CSS. The min-height: 0 fix for Firefox is well explained here:
Why don't flex items shrink past content size?
Updated fiddle
Stack snippet
body,
ul {
margin: 0;
padding: 0;
}
html,
body,
.root {
height: 100%;
}
.root {
display: flex;
flex-direction: column;
}
main {
flex: 3;
background: lightyellow;
display: flex; /* added */
flex-direction: column; /* added */
min-height: 0; /* added, Firefox need this */
}
ul {
display: flex;
flex-direction: column;
min-height: 0; /* added, Firefox need this */
}
ul li {
flex: 1;
background: aliceblue;
outline: 1px solid #000;
display: flex;
/* align-items: center; */
justify-content: space-between;
min-height: 0; /* added, Firefox need this */
}
img {
max-height: 100%; /* added */
}
header,
footer {
flex: 1;
}
header {
background: lightgray;
}
footer {
background: lightblue;
}
<div class="root">
<header>
HEAD
</header>
<main>
<ul>
<li>
<span>
<img src="http://via.placeholder.com/200x200" />
</span>
<span>text</span>
</li>
<li>
<span>
<img src="http://via.placeholder.com/200x200" />
</span>
<span>text</span>
</li>
<li>
<span>
<img src="http://via.placeholder.com/200x200" />
</span>
<span>text</span>
</li>
</ul>
</main>
<footer>
FOOTER
</footer>
</div>

Related

CSS Make Div Expand to Fill Parent but not Overflow

I am working on a project which requires some basic HTML/CSS. I have a code pen which may be easier to visualize: https://codepen.io/Sean713/pen/yLEZVEe
My objective is to make the innerBottomHalf element expand to fully fill the bottomHalf element (barring any padding). I would also like the navbar + wholePage element to take up the full VH.
I currently have it set so that the wholePage takes up the full VH, I'm not sure how to subtract the navbar height from this.
I also see that my innerBottomHalf expands outside of my BottomHalf, I do not know why this is, because I've set its height to be 100%.
I tried a lot of solutions online, the GPT chatbot, and prodding around with the code, but have been unable to figure it out. I appreciate any help.
My code is as follows:
ul {
list-style-type: none;
display: flex;
align-items: center;
}
li a {
display: block;
text-align: center;
padding: 10px 15px;
text-decoration: none;
}
div {
padding: 10px;
}
.wholePage {
background-color: blue;
display: flex;
flex-direction: column;
height: 100vh;
}
.topHalf {
background-color: purple;
display: flex;
justify-content: space-between;
}
.bottomHalf {
background-color: grey;
height: 100%;
}
.innerBottomHalf {
background-color: brown;
height: 100%;
}
.topLeftHalf {
background-color: green;
flex: 1;
height: 50vh;
}
.topRightHalf {
background-color: orange;
flex: 1;
height: 50vh;
}
<ul>
<li><a>Solve</a></li>
<li><a>About</a></li>
<li><a>Other</a></li>
</ul>
<div class="wholePage">
<div class="topHalf">
<div class="topLeftHalf">
This is the top left
</div>
<div class="topRightHalf">
This is the top right
</div>
</div>
<div class="bottomHalf">
This is the bottom half
<div class="innerBottomHalf">
This is the inner bottom half
</div>
</div>
</div>
With height: 100% on nested elements you'll get an overflow because there are heights from other elements being added. Instead of percentage heights, just use flex properties all the way.
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0; /* remove default margins */
}
.wholePage {
background-color: blue;
display: flex;
flex-direction: column;
/* height: 100vh; */
flex: 1; /* new */
}
.bottomHalf {
background-color: grey;
/* height: 100%; */
display: flex;
flex-direction: column;
flex: 1;
}
.innerBottomHalf {
background-color: brown;
/* height: 100%; */
flex: 1;
}
/* no adjustments below this line */
ul {
list-style-type: none;
display: flex;
align-items: center;
}
li a {
display: block;
text-align: center;
padding: 10px 15px;
text-decoration: none;
}
div {
padding: 10px;
}
.topHalf {
background-color: purple;
display: flex;
justify-content: space-between;
}
.topLeftHalf {
background-color: green;
flex: 1;
height: 50vh;
}
.topRightHalf {
background-color: orange;
flex: 1;
height: 50vh;
}
<ul>
<li><a>Solve</a></li>
<li><a>About</a></li>
<li><a>Other</a></li>
</ul>
<div class="wholePage">
<div class="topHalf">
<div class="topLeftHalf">
This is the top left
</div>
<div class="topRightHalf">
This is the top right
</div>
</div>
<div class="bottomHalf">
This is the bottom half
<div class="innerBottomHalf">
This is the inner bottom half
</div>
</div>
</div>

Flex item content overflows container

I have setup the following layout. The content__item elements (which are commented below) are overflowing vertically outside main container.
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
height: 100%;
padding: 2rem;
}
.content__item {
flex: 1;
margin-left: 1rem;
background-color: white;
}
<div className="root">
<nav className="nav"></nav>
<main className="main">
<div className="menu"></div>
<div className="content">
<!-- Overflowing -->
<div className="content__item"></div>
<div className="content__item"></div>
<div className="content__item"></div>
</div>
</main>
</div>
I am pretty sure its a flexbox bug. I tried using min-height: 0 on the container but it still doesn't work. I setup an environment here for reference.
The reason the content_items are overflowing is because height: 100% causes problems with flex. However if you remove that, the elements don't appear to fill the available height. This is because their parent (the content div) is not the child of a flex element, so it is in fact this element and not the content__item that isn't taking up the available height.
We can fix this by adding display:flex to the main div (the parent of content)... however now we have another problem! This makes the other child of content (the nav element) appears to the side. To fix this, we can use flex-direction: column;
The main changes you need to make this work as as follows:
.main {
flex-grow: 1; /* you already have this to allow the children grow */
display: flex; /* Add this so the content element can use the full height */
flex-direction: column; /* Add this to make the children stack one below another */
}
.content {
display: flex; /* you already had this */
flex:1; /* Add this to make it take up the available height */
}
.content__item {
flex: 1; /* You don't actually need this now */
}
Working Example:
Also FYI, you need to set the body margin to 0 - otherwise the 100vh extends larger than the screen as it is getting added to the default margin.
body { margin:0; }
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
display:flex;
flex-direction:column;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
padding: 2rem;
flex:1;
}
.content__item {
margin-left: 1rem;
background-color: white;
}
<div class="root">
<nav class="nav"></nav>
<main class="main">
<div class="menu"></div>
<div class="content">
<!-- Overflowing -->
<div class="content__item">some text here</div>
<div class="content__item">some text here</div>
<div class="content__item">some text here</div>
</div>
</main>
</div>
body, html {
margin: 0;
}
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
display: flex;
flex-direction: column;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
padding: 2rem;
flex: 1;
}
.content__item {
flex: 1;
margin-left: 1rem;
background-color: white;
}
<div class="root">
<nav class="nav"></nav>
<main class="main">
<div class="menu"></div>
<div class="content">
<!-- Overflowing -->
<div class="content__item">a</div>
<div class="content__item">b</div>
<div class="content__item">c</div>
</div>
</main>
</div>

Absolute Position Vertical Nav with Flexbox

I want to make a vertical nav that say will be 50px and then I want to have a flex area that has my header, main content area and footer.
Right now when I use absolute the flexbox container gets covered over as absolute it doing it's own thing. I am wondering if I can tell my flex container to start 50px from the left so I don't have to worry about icons and such getting swallowed up by it.
Do I have to make the flex container absolute as well?
You don't need any positioning or margins, just make it natural with the additional flex wrapper:
body {margin: 0}
.outerFlex {
display: flex; /* displays flex-items (children) inline */
height: 100vh; /* 100% of the viewport height */
}
nav {
flex-basis: 50px; /* initial width */
background: lightblue;
}
.innerFlex {
flex: 1; /* takes the remaining width */
display: flex;
flex-direction: column; /* stacks flex-items vertically */
background: lightgreen;
}
main {
flex: 1; /* takes the remaining height */
}
<div class="outerFlex">
<nav>Nav</nav>
<div class="innerFlex">
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
</div>
</div>
You can use margin-left:50px on the flex area to make it start at 50px;
see code sample
body {
margin: 0;
height: 100vh;
}
.container {
height: 100%;
}
.nav {
position: absolute;
width: 50px;
background: green;
height: 100%;
}
.flex-container {
display: flex;
background: yellow;
height: 100%;
margin-left: 50px;
}
<div class="container">
<div class="nav"></div>
<div class="flex-container">text sample</div>
</div>
You just need to position your nav as absolute or fixed, then give padding/margin that equals the width of your nav to the main content.
Here's an example.
.container {
width: 100%;
}
.left-nav {
position: fixed;
width: 50px;
height: 100%;
background: black;
}
.main {
display: flex;
flex-direction: column;
justify-content: space-between;
padding-left: 50px;
background: rgba(255,0,0,0.1);
}
.main-header {
background: red;
}
.main-body {
background: green;
}
.main-footer {
background: blue;
}
<div class="container">
<div class="left-nav">Nav</div>
<div class="main">
<div class="main-header">Header</div>
<div class="main-body">Body</div>
<div class="main-footer">Footer</div>
</div>
</div>

HTML/CSS div alignment

When resized, I need the div order changed to move two logos next to each other at 50% width each and the third div (title) 100% width below. Can anyone please direct me with where I am going wrong.
.logo1 {
width: 50%;
float: left;
top: 0px;
}
.logo2 {
width: 50%;
float: right;
top: 0px;
}
.title {
width: 100%;
clear: both;
}
<header>
<div class="logo1">
<img src="Pictures/logo.png" width="189" height="61" border="0">
</div>
<div class="title">
<h1 class="map_title">Infrastructure Web Map</h1>
</div>
<div class="logo2">
<img src="Pictures/Picture1.png" width="310" height="70" border="0">
</div>
</header>
You can do it with the Flexbox:
body {margin: 0}
h1 {
text-align: center;
}
header {
display: flex; /* displays flex-items (children) inline */
flex-wrap: wrap; /* enables their wrapping */
}
header > div {
flex: 1; /* each takes 33.33% of the parent's width */
display: flex; /* addition */
justify-content: center; /* addition */
}
img {
display: block; /* removes bottom margin/whitespace */
width: 100%; /* responsiveness */
}
#media (max-width: 568px) {
header > div {
flex: 0 1 50%; /* flex-grow = default, flex-shrink = default, flex-basis = 50% (initial width) */
}
.title {
flex: 1; /* stretches to fill the parent's width (100%) */
order: 1; /* changes the order, i.e. puts it below/after the #logo2 (by default its "order: 0") */
}
}
<header>
<div class="logo1">
<img src="http://placehold.it/300x200" alt="logo1">
</div>
<div class="title">
<h1 class="map_title">Infrastructure Web Map</h1>
</div>
<div class="logo2">
<img src="http://placehold.it/300x200" alt="logo2">
</div>
</header>
Thanks to all for the help, after some further research and a combination of all the suggestions, this is what I used to get it working:
header {
background-color: #cccccc;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
img {
margin: auto;
display: block;
}
.logo1, .logo2 {
width: 50%;
flex: 1 1 0;
}
.title {
color: #336699;
flex: 100%;
order: 3;
text-align: center;
}
}

Flex-grow dependent on presence of child content

I have two "Splits" that are using flex-grow to either be 100% if only one is present or a 50%/50% if both are present. The issue is I would like for this behavior to be dependent on the presence of content within the div.splits.
Through some fiddling I can either get it to do the proper expanded heights or the proper removal of content, but not both at the same time.
The content DOM structure really needs to remain the same. Maybe adding an additional wrapper would be ok if need be. I am trying to resolve this with a pure CSS solution if possible.
JS Bin Code Snippet
CSS:
body {
border: 1px solid black;
display: flex;
width: 100vw;
}
section {
display: flex;
flex-direction: column;
width: 100vw;
}
.split {
width: 100vw;
display: flex;
flex: 1;
}
.content {
/* probably something here? */
}
/*-------------- Non pertinent styles -----------------*/
.pink { text-align: center; background-color: pink; }
.blue { text-align: center; background-color: turquoise; }
nav { background-color: steelblue; }
HTML:
<body>
<section>
<div class="split pink">
<!-- If I remove this <h1> I would like for
the behavior to be the same as if I
removed this .pink.split div from the DOM -->
<h1 class="content">A</h1>
</div>
<nav> Some Nav </nav>
<div class="split blue">
<h2 class="content">B</h2>
</div>
</section>
</body>
As the JSBin demo fills the viewport, there is 2 solutions for solving this.
The solution to the inline code, where the section doesn't fill the viewport.
You should use flex-grow: 1;, not flex: 1, as with flex: 1, which is the same as flex: 1 1 0, the flex-basis is 0, and when, the flex items will flex-grow based on their content is 0, hence take equal space.
Alternatively you could use flex: 1 1 auto.
Src: https://www.w3.org/TR/css-flexbox-1/#flex-common
Stack snippet - with content
body {
border: 1px solid black;
box-sizing: border-box;
display: flex;
}
section {
display: flex;
flex-direction: column;
width: 100%;
}
.split {
display: flex;
flex-grow: 1; /* or "flex: 1 1 auto" */
}
/*-------------- Non pertinent styles -----------------*/
.pink { text-align: center; background-color: pink; }
.blue { text-align: center; background-color: turquoise; }
nav { background-color: steelblue; }
<section>
<div class="split pink">
<!-- If I remove this <h1> I would like for
the behavior to be the same as if I
removed this .pink.split div from the DOM -->
<h1 class="content">A</h1>
</div>
<nav> Some Nav </nav>
<div class="split blue">
<h2 class="content">B</h2>
</div>
</section>
Stack snippet - without content
body {
border: 1px solid black;
box-sizing: border-box;
display: flex;
}
section {
display: flex;
flex-direction: column;
width: 100%;
}
.split {
display: flex;
flex-grow: 1; /* or "flex: 1 1 auto" */
}
/*-------------- Non pertinent styles -----------------*/
.pink { text-align: center; background-color: pink; }
.blue { text-align: center; background-color: turquoise; }
nav { background-color: steelblue; }
<section>
<div class="split pink">
<!-- If I remove this <h1> I would like for
the behavior to be the same as if I
removed this .pink.split div from the DOM -->
</div>
<nav> Some Nav </nav>
<div class="split blue">
<h2 class="content">B</h2>
</div>
</section>
The solution for the JSBin, where the section fill the viewport
Use the :empty selector, and when the split is empty, change it to flex: 0.
Stack snippet - with content
body {
border: 1px solid black;
box-sizing: border-box;
display: flex;
margin: 0;
height: 100vh;
}
section {
display: flex;
flex-direction: column;
width: 100%;
}
.split {
display: flex;
flex: 1;
}
.split:empty {
flex: 0;
}
/*-------------- Non pertinent styles -----------------*/
.pink { text-align: center; background-color: pink; }
.blue { text-align: center; background-color: turquoise; }
nav { background-color: steelblue; }
<section>
<div class="split pink">
<!-- If I remove this <h1> I would like for
the behavior to be the same as if I
removed this .pink.split div from the DOM -->
<h1 class="content">A</h1>
</div>
<nav> Some Nav </nav>
<div class="split blue">
<h2 class="content">B</h2>
</div>
</section>
Stack snippet - without content
body {
border: 1px solid black;
box-sizing: border-box;
display: flex;
margin: 0;
height: 100vh;
}
section {
display: flex;
flex-direction: column;
width: 100%;
}
.split {
display: flex;
flex: 1;
}
.split:empty {
flex: 0;
}
/*-------------- Non pertinent styles -----------------*/
.pink { text-align: center; background-color: pink; }
.blue { text-align: center; background-color: turquoise; }
nav { background-color: steelblue; }
<section>
<div class="split pink"></div>
<nav> Some Nav </nav>
<div class="split blue">
<h2 class="content">B</h2>
</div>
</section>