Flexbox wrongly calculating width of items with text - css

Here is my code https://jsfiddle.net/n4qpv91t/4/
HTML
<nav class="m-nav-main">
<ul class="m-nav-main__menu">
<li class="m-nav-main__item">
PLATFORM OF JOYSSS SAMPLETEXT
</li>
<li class="m-nav-main__item">
LOREM DIMTURAGUA
</li>
<li class="m-nav-main__item">
WORDS
</li>
<li class="m-nav-main__item">
OFFRES SPECIALS OF WORLD
</li>
<li class="m-nav-main__item">
LEARN FROM HOME
</li>
</ul>
</nav>
CSS
.m-nav-main {
width: 1000px;
}
.m-nav-main__menu {
list-style: none;
display: inline-flex;
justify-content: flex-end;
padding: 0;
margin: 0;
}
.m-nav-main__item {
display: inline-block;
border: solid 1px red;
}
.m-nav-main__link {
display: flex;
height: 50px;
padding: 0 1rem;
color: inherit;
border: solid 1px green;
align-items: center;
}
I have menu with text that breaks to 2nd line. Flexbox desnt get proper width of this text resulting in big empty spaces between text. I want those menu items to be as narrow as possible. I use flex on links to have centered vertically text. Changing it to inline or anything doesnt make a change.

Try adding flex: 1; to .m-nav-main__item.

Related

Nested Flexbox Wrapping [duplicate]

This question already has answers here:
When flexbox items wrap in column mode, container does not grow its width
(9 answers)
Closed 1 year ago.
I am trying to create a flexbox container that contains a list of items in a row format and then each of those items will be a flexbox with items in a wrapping column format. However, it seems that the first containers rows do not expand to fit the contents of the wrapping columns and end up overlapping each other.
Demo
I want the end result to look like this:
.flex-group {
display: flex;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
border: 1px solid silver;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 500px;
}
.red li {
background: red;
}
.gold li {
background: gold;
}
.blue li {
background: deepskyblue;
}
.flex-item {
padding: 5px;
width: 100px;
height: 100px;
margin: 10px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
}
<div class="flex-group">
<ul class="flex-container blue">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
<li class="flex-item">8</li>
</ul>
<ul class="flex-container red">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
</ul>
<ul class="flex-container gold">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
</ul>
<div>
So at the end of the day, I'm looking to have a non-wrapping row where each of the child elements (dynamic amount) can contain a set of column wrapping elements (dynamic amount). Note: You can almost get the solution if you make the .flex-container have flex-direction: row but I need flex-direction: column since order does matter in this scenario. The main container needs to have a fixed height and each child container can have a dynamic width (due to wrapping elements causing them to grow horizontally).
If you will use grid, you can use this:
.flex-group {
display: flex;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
border: 1px solid silver;
display: grid;
grid-template-rows: repeat(3, auto);
gap: 10px;
grid-auto-flow: column;
height: 500px;
}
.red li {
background: red;
}
.gold li {
background: gold;
}
.blue li {
background: deepskyblue;
}
.flex-item {
padding: 5px;
width: 100px;
height: 100px;
margin: 10px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
}
<div class="flex-group">
<ul class="flex-container blue">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
<li class="flex-item">8</li>
</ul>
<ul class="flex-container red">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
</ul>
<ul class="flex-container gold">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
</ul>
<div>
There are a lot of similar question but no answers. I think a workaround can satisfy your needs. Include a script to calculate the width depending on the height you assign to each flex-container and the flex-item height:
check it out here
I had to fix the height to 400px to get the same output as yours but this script will calculate number of cols and lines needed. Couldn't get the padding properly though so I set a variable that will get me the desired output
document.querySelectorAll('.flex-container').forEach((e)=>{
let p = 10 ;
let childCount = e.childElementCount;
let childHeight = e.children[0].clientHeight ;
let childWidth = e.children[0].clientWidth ;
let lines = Math.round((e.clientHeight / (e.children[0].clientHeight + 2*p))) ;
let cols = Math.round(childCount / lines) ;
let width = cols * ( 2*p + childWidth );
e.style.width = width+"px";
});
Still if you want a column based display for your items similar to Pinterest then you need to specify in advance the number of columns and use Masonry Layout :
.masonry-container {
column-count: 3;
column-gap: 15px;
}
.masonry-item {
display: inline-block;
width: 100%;
}
I'm not sure I got this, but it sounds like you don't want your columns to be in 'wrap' mode.
You may try to change those values:
.flex-container {
/* leave the rest of what was here*/
flex-wrap: nowrap;
height: 100%;
}
This is what I thought you want to achieve:

How to adjust button text to make it fit on top botton and sides

I have a nav bar list with links, the last item is a button. I displayed them using flex box but when its rendered the button text is not flush/even against the top so it will display uneven.
Here's a photo:
As you can see the FOO BAR is not even with the items to the left.
Here's the html:
<nav className={styles.PrivateHeaderNav}>
<ul>
<li>
<h5>LOGO HERE</h5>
</li>
<li>
<NavLink activeClassName={styles.NavLink__active} to="/profile">
FOO
</NavLink>
</li>
<li>
<NavLink activeClassName={styles.NavLink__active} to="/game_groups">
FOO
</NavLink>
</li>
<li>
<NavLink activeClassName={styles.NavLink__active} to="/feed">
FOO
</NavLink>
</li>
<li>
<button type="button" onClick={() => logOutCurrentUser()}>
FOO BAR
</button>
</li>
</ul>
</nav>
Here's the css:
.PrivateHeaderNav {
background-color: #252334;
color: #ff7376;
font-size: 20px;
ul {
display: flex;
flex-direction: row;
justify-content: space-evenly;
padding: 0.5em 0.1em;
li {
a {
text-decoration: none;
&:visited {
color: #ff7376;
}
&.NavLink__active {
color: #ffe880;
}
}
button {
padding: 0px;
white-space: normal;
background: transparent;
border: none;
color: inherit;
font-size: inherit;
}
}
}
}
Here's a photo with a border so I can see it clearly.
Is there a inner padding btn something on css that I can use to remove the padding? or margin? I don't even know if there's such a thing.
.privateHeaderNav {
background-color: #252334;
color: #ff7376;
font-size: 20px;
}
.privateHeaderNav ul {
/* Changing justify-content: space-around to make beautiful auto spacing between the li, in this case no need for the left-right padding (Check #media below), and finally align items to fit perfectly centered*/
display: flex;
flex-direction: row;
justify-content: space-around; /*Changed*/
padding: 0.5em 0; /*Changed*/
align-items: center; /*Added*/
}
.privateHeaderNav ul li a {
text-decoration: none;
}
.privateHeaderNav ul li a:visited {
color: #ff7376;
}
.privateHeaderNav ul li a.NavLink__active {
color: #ffe880;
}
.privateHeaderNav ul li button {
/*Align center the button content to match the a tag(links) */
padding: 0px;
white-space: normal;
background: transparent;
border: none;
color: inherit;
font-size: inherit;
display: flex; /*Added*/
justify-content: center; /*Added*/
align-items: center; /*Added*/
}
/*Add #media to remove the beautiful space we added between the li, basicly we're stretching the list 100%*/
#media(max-width:576px){
.privateHeaderNav ul {
justify-content: space-between;
}
/*Since we removed the space, we have to add padding left-right we removed previously*/
.privateHeaderNav ul {
padding: 0.5em 0.3em;
}
}
<header>
<nav class="privateHeaderNav">
<ul>
<li>
<h5>LOGO HERE</h5>
</li>
<li>
FOO
</li>
<li>
FOO
</li>
<li>
FOO
</li>
<li>
<button type="button">
FOO BAR
</button>
</li>
</ul>
</nav>
</header>
Give 'ul' a align-items: flex-start

Vertically center content inside an inline parent

Trying to create an adaptive post meta for a post card.
The idea is that sometimes the post meta could have more than one category inside and because of that I need somehow to wrap the items, but at the same time to vertically center the li content (like the avatar and icons).
I tried to apply display: inline-flex to the .entry-meta in order to be able to use the align-items: center property but in this case the list items - li's are not anymore wrapping nice like when applying display: inline to li.
1. The display: inline method (not centering items vertically)
This method is wrapping correctly and how I want all the list items but it can't center vertically the content (like the avatar and icons)
article {
max-width: 450px;
background: #eee;
font-family: arial;
font-size: 12px;
text-transform: uppercase;
}
article a {
text-decoration: none;
}
.entry-meta {
list-style: none;
margin: 0;
padding: 0;
}
.entry-meta li {
display: inline;
margin-right: 10px;
background: #ccc;
}
.avatar {
display: inline-block;
width: 25px;
height: 25px;
border-radius: 100%;
background: #FA7059;
}
.icon {
display: inline-block;
width: 13px;
height: 13px;
background: #BE4C4E;
}
<article>
<ul class="entry-meta">
<li class="author">
<div class="avatar">
</div>
<span>By </span>
Admin
</li>
<li class="date">
<span>On </span>
<time>Feb 22, 2019</time>
</li>
<li>
<span>On </span>
2 Comments
</li>
<li class="categories">
<span class="icon">
</span>
Food
Nature
People
Travel
Trends
Business
Sport
Music
Gadgets
Uncategorized
</li>
<li class="tags">
<span class="icon">
</span>
Red
Blue
Green
Yellow
</li>
</ul>
</article>
2. The display: inline-flex method
The category li is not wrapped correctly, it breaks in a new line, but in exchange the content is vertically centered like I need.
article {
max-width: 450px;
background: #eee;
font-family: arial;
font-size: 12px;
text-transform: uppercase;
}
article a {
text-decoration: none;
}
.entry-meta {
list-style: none;
margin: 0;
padding: 0;
display: inline-flex;
flex-wrap: wrap;
align-items: center;
}
.entry-meta li {
margin-right: 10px;
background: #ccc;
display: inline-flex;
flex-wrap: wrap;
align-items: center;
}
.avatar {
display: inline-block;
width: 25px;
height: 25px;
border-radius: 100%;
background: #FA7059;
}
.icon {
display: inline-block;
width: 13px;
height: 13px;
background: #BE4C4E;
}
<article>
<ul class="entry-meta">
<li class="author">
<div class="avatar">
</div>
<span>By </span>
Admin
</li>
<li class="date">
<span>On </span>
<time>Feb 22, 2019</time>
</li>
<li>
<span>On </span>
2 Comments
</li>
<li class="categories">
<span class="icon">
</span>
Food
Nature
People
Travel
Trends
Business
Sport
Music
Gadgets
Uncategorized
</li>
<li class="tags">
<span class="icon">
</span>
Red
Blue
Green
Yellow
</li>
</ul>
</article>
So, in other words, I need somehow to keep the list items to wrap like they are with display: inline but at the same time center vertically the content inside.
P.S. the vertical-align: middle property doesn't really help :)
P.S. the vertical-align: middle property doesn't really help :)
This is actually what you need (applied to .avatar)
article {
max-width: 450px;
background: #eee;
font-family: arial;
font-size: 12px;
text-transform: uppercase;
}
article a {
text-decoration: none;
}
.entry-meta {
list-style: none;
margin: 0;
padding: 0;
}
.entry-meta li {
display: inline;
margin-right: 10px;
background: #ccc;
}
.avatar {
display: inline-block;
width: 25px;
height: 25px;
border-radius: 100%;
background: #FA7059;
vertical-align:middle;
}
.icon {
display: inline-block;
width: 13px;
height: 13px;
background: #BE4C4E;
}
<article>
<ul class="entry-meta">
<li class="author">
<div class="avatar">
</div>
<span>By </span>
Admin
</li>
<li class="date">
<span>On </span>
<time>Feb 22, 2019</time>
</li>
<li>
<span>On </span>
2 Comments
</li>
<li class="categories">
<span class="icon">
</span>
Food
Nature
People
Travel
Trends
Business
Sport
Music
Gadgets
Uncategorized
</li>
<li class="tags">
<span class="icon">
</span>
Red
Blue
Green
Yellow
</li>
</ul>
</article>

equidistant delimiter in between flex items with variable width

I need to design a navigation rendered dynamically by a CMS (so I have no control over HTML markup). The menu has a unknown, variable number of items, and all items have a variable, unknown width. I need to layout those menu items in one line with an equidistant spacing in between (easily done with flexbox) and with a visual separator exactly in the middle of the spacing (this is my unsolved problem).
Below is the code I've got so far. The HTML is just an example output, and I can't modify it. I'm also not allowed to use JS. The CSS is from myself and I can fully control it.
.mainnav {
width: 100%;
display: flex;
justify-content: space-between;
list-style-type: none;
margin: 0;
padding: 0;
border: 1px solid black;
}
.mainnav__item {
flex: 0 0 auto;
background-color: yellow;
position: relative;
white-space: nowrap;
}
.mainnav__item + .mainnav__item::before {
content: '';
width: 1px;
height: 100%;
background-color: red;
position: absolute;
left: -50%;
top: 0;
}
<ul class="mainnav">
<li class="mainnav__item">
<a class="mainnav__link" href="#">Short</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Looooooooooooooong</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Mediuuum</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">whatever else</a>
</li>
</ul>
My problem is the red separator line. It should be exactly in the middle, but I don't know how to calculate the horizontal position.
Can anybody tell me a CSS only solution for the delimiter placement?
The solution should work in common, up-to-date browsers (current version of [Android|Win] Chrome, [macOs|iOs] Safari, Firefox, Edge), but also in IE11
One idea is to consider display:contents;1 within the li element and keep the pseudo element in-flow (remove position:absolute). This will make the pseudo element and the a to be the flex items instead of the li element thus space-between will do the job.
Simply pay attention to the browser support since it's a new feature.
.mainnav {
width: 100%;
display: flex;
justify-content: space-between;
list-style-type: none;
margin: 0;
padding: 0;
border: 1px solid black;
}
.mainnav__item {
flex: 0 0 auto;
position: relative;
white-space: nowrap;
display:contents;
}
.mainnav__item a {
background:yellow;
}
.mainnav__item + .mainnav__item::before {
content: '';
width: 1px;
display:block;
background-color: red;
}
<ul class="mainnav">
<li class="mainnav__item">
<a class="mainnav__link" href="#">Short</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Looooooooooooooong</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Mediuuum</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">whatever else</a>
</li>
</ul>
1 The element itself does not generate any boxes, but its children and
pseudo-elements still generate boxes and text runs as normal. For the
purposes of box generation and layout, the element must be treated as
if it had been replaced in the element tree by its contents (including
both its source-document children and its pseudo-elements, such as
::before and ::after pseudo-elements, which are generated before/after
the element’s children as normal).ref
Another idea is to adjust flex-grow and use border instead of relying on space-between:
.mainnav {
display: flex;
list-style-type: none;
margin: 0;
padding: 0;
border: 1px solid black;
}
.mainnav__item {
flex: 0 0 auto;
position: relative;
white-space: nowrap;
flex-grow:2; /*middle elements need to grow twice that edge element*/
text-align:center;
border-left:1px solid red;
border-right:1px solid red;
}
.mainnav__item a {
background:yellow;
}
.mainnav__item:first-child {
flex-grow:1;
text-align:left;
border-left:none;
}
.mainnav__item:last-child {
flex-grow:1;
text-align:right;
border-right:none;
}
<ul class="mainnav">
<li class="mainnav__item">
<a class="mainnav__link" href="#">Short</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Looooooooooooooong</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">Mediuuum</a>
</li>
<li class="mainnav__item">
<a class="mainnav__link" href="#">whatever else</a>
</li>
</ul>

flexbox container not always centering

I have a flexbox container (ul) that is intermittently centering (this is intended) and not (upon view refresh). Is this a bug or is there something missing from the css?
Live example on the jsFiddle.
HTML:
<div id="lower-half">
<nav>
<ul>
<li>
Menu
</li>
<li>
Catering
</li>
<li>
Gallery
</li>
<li>
About
</li>
</ul>
</nav>
CSS:
nav ul{
display: flex;
height: 100%;
margin: 0 auto;
justify-content: center;
flex-wrap: wrap;
list-style: none;
border: 3px solid;
border-radius: 20px;
width: 360px;
background-color: white;
}
nav ul li {
align-self: center;
flex-grow: 1;
}
It sounds like a weird issue - Is it browser-related? I just tried pasting your code into a Pen and this does indeed center the nav.
However, to perhaps solve your issue, I've changed your CSS a bit, without it affecting your design and positioning - This may perhaps solve your refresh-issue.
Updated Pen
HTML
<nav>
<ul>
<li>
Menu
</li>
<li>
Catering
</li>
<li>
Gallery
</li>
<li>
About
</li>
</ul>
</nav>
CSS
nav {
display: flex;
justify-content: center;
}
nav ul{
display: flex;
flex-wrap: wrap;
width: 360px;
list-style: none;
border: 3px solid;
border-radius: 20px;
background-color: white;
}
nav ul li {
flex-grow: 1;
}

Resources