Responsive flex row display until min width reach - html

Basically, in a container, I have tabs (with ul.li) and another div (search box) next to these tabs which are displayed in a row using flex.
I want it responsive following this workflow:
if the width increases, the search section increases to fit the available space. I manage to do using the property flex-basis: 100%
if the width decreases, the search section decreases as well with the property flex-basis: 100%. However, I would like the search section to decrease until 100px and then tabs will decrease displaying ellipsis. I tried using flex-shrink or flex-grow without success since I am not an expert with flexbox.
I reproduced the simplest example in order to illustrate what I am saying.
body {
padding: 50px;
font-family: sans-serif;
}
.container {
resize: horizontal;
overflow: auto;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
border: 1px solid black;
}
.tabs__list {
align-items: flex-start;
display: flex;
list-style: none;
margin: 0;
padding: 0;
border-bottom: 0;
margin-right: 28px;
width: unset;
}
button {
height: 34px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search {
flex-basis: 100%;
height: 34px;
align-self: end;
min-width: 100px;
background-color: blue;
}
<div class="container">
<ul class="tabs__list">
<li>
<button type="button">Tab Item Number One</button>
</li>
<li>
<button type="button">Tab Item Number Two</button>
</li>
</ul>
<div class="search"></div>
</div>

You need to make use of the power of flex-grow, flex-shrink and flex-basis properties on the children of your flex container.
This is very handy youtube tutorial from Kevin Powell explaining these properties and other important flex-box concepts.
Anyway, here's a solution to your problem with comments explaining what has been added and why.
body {
padding: 50px;
font-family: sans-serif;
}
.container {
resize: horizontal;
display: flex;
overflow: auto;
align-items: center;
flex-wrap: nowrap;
/* don't allow items to go to a new line on shrink */
border: 1px solid black;
min-width: 138px;
/* 100px for the div.search width + 10px inline padding for div.search + 28px margin-rigt from ul.tabs__list */
}
.tabs__list {
flex-grow: 0;
/* this forbids ul from growing, so only div.search can take the remaining space */
flex-shrink: 1;
/* this allows ul to shrink */
flex-basis: content;
list-style: none;
margin: 0;
padding: 0;
border-bottom: 0;
margin-right: 28px;
overflow: hidden;
/* styling the ul with nowrap so that the li's do not go on the next line on shrink */
display: flex;
flex-wrap: nowrap;
}
.search {
flex-grow: 1;
/* this allows div.search to grow and take up the remaining space */
flex-shrink: 0;
/* this forbids div.search to shrink below 100px */
flex-basis: 100px;
/* this works quite like min-width: 100px; but not exactly the same */
align-self: end;
height: 45px;
background-color: blue;
/* more suggested styling to div.search */
color: white;
display: flex;
align-items: center;
padding: 5px;
}
.tabs__list li {
/* display: inline; */
margin: 5px;
padding: 5px;
flex: auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
/* the following styling is applied to give li's the look of a button */
border: black 1px solid;
text-align: center;
background-color: lightgray;
cursor: pointer;
}
.tabs__list li:hover {
background-color: gray;
color: white;
}
<div class="container">
<ul class="tabs__list">
<li> Tab Item Number One</li>
<li>Tab Item Number Two</li>
</ul>
<div class="search">Search</div>
</div>

The most important thing an element needs for ellipsis to work is width. Without a width, the browser won't know when the text is actually being cut from its predefined width. I set a width on your container to 100%. Then set each child element (tabs__list and search) to 50%. I also set the li to 50% so each item would take half of that space. However, I still have flex-basis: 100%; so search fills any remaining space.
With this set, you can now just set width: 100%; to your button, which is the defined width it needs so it knows the text is being cut off and it needs to show ellipsis.
Side note: you can still add your min-width: 100px; to search and it will still show ellipsis, but I removed it because it shows a vertical scrollbar.
body {
padding: 50px;
font-family: sans-serif;
}
.container {
resize: horizontal;
overflow: auto;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
border: 1px solid black;
width: 100%;
}
.tabs__list {
align-items: flex-start;
display: flex;
list-style: none;
margin: 0;
padding: 0;
border-bottom: 0;
margin-right: 28px;
width: 25%;
}
li {
width: 50%;
}
button {
height: 34px;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search {
flex-grow: 1;
flex-basis: 100%;
width: 75%;
min-width: 100px;
height: 34px;
align-self: end;
background-color: blue;
}
<div class="container">
<ul class="tabs__list">
<li>
<button type="button">Tab Item Number One</button>
</li>
<li>
<button type="button">Tab Item Number Two</button>
</li>
</ul>
<div class="search"></div>
</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>

Setting width of an element to that of one which overflows its container when both are in a flexbox?

Is there a CSS way of getting the blue line (class .cover) in this snippet to have a width equal that of the .scrl_can, which exceeds the width of its container .prnt?
The width of .scrl_can changes based on user input. A width of 100% sets the width of .cover to the width of .prnt. I tried align-items: stretch; in .prnt and it is the same as width of 100%.
I realize there are other ways of getting the blue line in that position and of the desired width, but the real UI piece has some relatively positioned elements that "drop" below the container when selected; and setting overflow-x: hidden, cuts them off; so I was trying this method which almost works.
Thank you.
.flex_can {
display: flex;
flex-flow: row nowrap;
width: 600px;
}
.space {
flex: none;
width: 150px;
height: 50px;
margin-left: 10px;
background-color: rgb(200,200,200);
}
.prnt {
flex: 1 1;
border: 1px solid black;
padding: 5px;
display: flex;
flex-flow: column nowrap;
overflow-x: scroll;
}
.scrl_can {
width: 500px;
height: 50px;
border: 0.1px solid green;
white-space: nowrap;
}
.cover {
flex: none;
width: 100%;
height: 2px;
background-color: blue;
margin-bottom: 20px;
}
<div class="flex_can">
<div class="prnt">
<div class="scrl_can">Some words here to span the width of prnt to make it a scroll can.</div>
<div class="cover"></div>
</div>
<div class="space"></div>
</div>
I would use the ::after pseudo element.
.flex_can {
display: flex;
flex-flow: row nowrap;
width: 600px;
}
.space {
flex: none;
width: 150px;
height: 50px;
margin-left: 10px;
background-color: rgb(200,200,200);
}
.prnt {
flex: 1 1;
border: 1px solid black;
padding: 5px;
display: flex;
flex-flow: column nowrap;
overflow-x: scroll;
}
.scrl_can {
position: relative;
width: 500px;
height: 50px;
border: 0.1px solid green;
white-space: nowrap;
}
.scrl_can::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: blue;
margin-bottom: 20px;
}
<div class="flex_can">
<div class="prnt">
<div class="scrl_can">Some words here to span the width of prnt to make it a scroll can.</div>
<!-- <div class="cover"></div> DON'T NEED THIS ANYMORE -->
</div>
<div class="space"></div>
</div>

flex direction "row" inside a flexbox with flex-direction "column"

I am trying to make a navbar using flexbox. In my code I have the actual navbar wrapped with flex- direction:"row" to align the logo and the button.
Now I want to have the nav-inner (the beige div) under the navbar (that should be 100vw wide), but actually it sits next to the navbar.
I have tried to change the flex-direction to "column" inside my nav-menu div, but the Hamburger button goes out of the screen. Am I doing something wrong?
body {
margin: 0;
overflow: hidden;
}
/* defaults */
.safe-view {
display: flex;
height: 100vh;
}
.hamburger {
height: 30px;
width: 30px;
}
/**/
/* navbar */
.navbar {
position: sticky;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
width: 100vw;
font-size: 1.2em;
height: 100px;
}
.nav-menu {
width: 100%;
display: flex;
flex-direction:row;
/*flex-direction:column;*/
position: absolute;
height: 100%;
overflow: hidden;
background-color: white;
}
.nav-inner {
height: 100%;
width: 100%;
background-color: blanchedalmond;
}
/**/
<div class="safe-view">
<div class="nav-menu">
<div class="navbar">
<h1>logo</h1>
<button class="hamburger"></button>
</div>
<div class="nav-inner"></div>
</div>
</div>
This is a CSS box-model issue. You need to add box-sizing: border-box. This will ensure that padding is included in calculation of the width.
*{
box-sizing: border-box;
}
By default box-sizing is set to content-box. This will only care about the element content and shift padding and border outside of the element. That is why you saw the button push out to the right! This can also help you to understand further.
Also, flex-direction for .nav-menu needs to be set to column in order to position .nav-inner below.
Heres an alternative. I removed padding and just used calc() function to create padding. But always include box-sizing:border-box in your CSS :)
*{
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.safe-view {
display: flex;
height: 100vh;
}
.navbar {
position: sticky;
display: flex;
flex-direction:row;
align-items: center;
justify-content: space-between;
width: calc(100vw - 60px);
margin: 0 auto;
font-size: 1.2em;
height: 100px;
}
.nav-menu {
width: 100%;
display: flex;
flex-direction:column;
position: absolute;
height: 100%;
overflow: hidden;
background-color: white;
}
.nav-inner {
height: 50px;
width: 100%;
background-color: blanchedalmond;
}
<div class="safe-view">
<div class="nav-menu">
<div class="navbar">
<h1 class="logo">logo</h1>
<button class="hamburger">button</button>
</div>
<div class="nav-inner"></div>
</div>
</div>

Expand child height based on taller sibling (parent height set to auto)

Simple question (snippet below):
<ul> with display: flex
each <li> should have the same size and together must occupy the full width of the <ul>
each <li> has a <a> which the content may have 1 or 2 lines of text.
each <li> has height set to auto to adjust to the <a> content
My problem:
I need the "one-line" links to auto expand to the height of the "two-line" links. Setting height: 100% doesn't work because their parent height it's intentionally set to auto to adjust for content.
But in some cases I'll get two-line links, and some cases all will be one-line. So I need the one-line links to auto-expand when that happens.
How is this possible?
#root {
width: 140px;
box-sizing: border-box;
}
ul {
display: flex;
justify-content: space-between;
margin: auto;
padding: 0;
}
li {
flex-grow: 1;
flex-basis: 30px;
list-style-type: none;
display: inline-block;
border: 1px dotted blue;
height: auto;
}
a {
cursor: pointer;
border: 1px dotted green;
display: inline-block;
background-color: lightblue;
padding: 8px 0px;
}
<div id="root">
<ul>
<li><a>Link 1</a></li>
<li><a>Long Link 2</a></li>
</ul>
</div>
You don't need to use inline-block with flex. Just use display: flex for li and display: block for a. Finally, add the width: 100% for a. It seems match your requirement.
#root {
width: 140px;
box-sizing: border-box;
}
ul {
display: flex;
justify-content: space-between;
margin: auto;
padding: 0;
}
li {
flex-grow: 1;
flex-basis: 30px;
list-style-type: none;
display: flex;
border: 1px dotted blue;
height: auto;
}
a {
cursor: pointer;
border: 1px dotted green;
display: block;
background-color: lightblue;
padding: 8px 0px;
width: 100%;
}
<div id="root">
<ul>
<li><a>Link 1</a></li>
<li><a>Long Link 2</a></li>
</ul>
</div>
you can omit padding from top and bottom of the anchor and use height 100% a{height: 100%;}
#root {
width: 140px;
box-sizing: border-box;
}
ul {
display: flex;
justify-content: space-between;
margin: auto;
padding: 0;
}
li {
flex-grow: 1;
flex-basis: 30px;
list-style-type: none;
display: inline-block;
border: 1px dotted blue;
height: auto;
}
a {
cursor: pointer;
border: 1px dotted green;
display: inline-block;
background-color: lightblue;
height: 100%;
}
<div id="root">
<ul>
<li><a>Link 1</a></li>
<li><a>Long Link 2</a></li>
</ul>
</div>

CSS3 Flex: Pull child to the right

here's what I have Fiddle
ul {
display: flex;
justify-content: flex-start;
flex-direction: row;
align-items: center;
width: 100%;
height: 100px;
background: #333;
padding: 15px;
}
ul li {
padding: 15px;
margin: 5px;
background: #efefef;
border: 1px solid #ccc;
display: inline-block;
list-style: none;
}
#item-1 {
height: 50px;
}
#item-2 {
height: 70px;
}
<ul>
<li id="item-1">Home</li>
<li id="item-2">Menu</li>
<li>More</li>
<li>Stuff</li>
<li>Settings</li>
</ul>
I want the last item inside the flex-box to be pulled to the right ("Settings" in my fiddle) while keeping all other items the way they are. The "Settings"-item should also be centered vertically and everything.
align-self: flex-end pushes the item to the bottom (I want it on the right).
I would very much prefer a solution using flex-box because my items have variable heights and should always be centered vertically.
What is the cleanest way to achieve this?
Thanks for your help!
Simple fix, use an auto-adjusting margin:
ul li:last-child {
margin-left: auto;
}
You may also want to not use width: 100% so that the element stays inside the visible area:
ul {
display: flex;
justify-content: flex-start;
flex-direction: row;
align-items: center;
/* width: 100%; */
height: 100px;
background: #333;
padding: 15px;
}
http://jsfiddle.net/dwLHE/
See also https://www.w3.org/TR/css-flexbox-1/#auto-margins