Spread elements evenly using Flexbox - css

I'm starting with Flexbox and I'm trying to center a row with three columns to the center of the document. I've tried the following code (Written in Sass) but as you can see in the screenshot the three elements are not perfectly centered (they don't have the same space between them):
HTML for the header:
<header>
<nav>
<ul>
<li>ELE 1</li>
<li>ELEMENT 2</li>
<li>ELEMENT 3</li>
</ul>
</nav>
</header>
SCSS for the header:
header {
height: 80px;
width: 100%;
background-color: $dark-grey;
}
nav {
height: inherit;
max-width: 800px;
margin: auto;
text-align: center;
display: flex;
align-items: center;
& ul {
width: 100%;
display: flex;
& li {
flex: 1 0 auto;
& a, a:visited {
color: $white-alpha;
}
& a.active {
color: $white;
}
}
}
}
I've recreated the problem in this CodePen, as you can see the real example. What I'm doing wrong? Thanks.

Related

align-items: center not working in flexbox

I am trying to vertically align the tex of a nav bar that is in display: flex;.
I have used the following code yet the text is still aligned to the top of the nav container and won't move down to the center.
Could you please help me understand what I have done wrong? I have tried moving align-items: center; to the nav{} and nav ul li{} in CSS but it doesn't change anything.
nav {
background-color: aquamarine;
width: 100%;
height: 70px;
}
nav ul {
list-style: none;
display: flex;
justify-content: space-around;
align-items: center;
}
nav ul li {
font-size: 3rem;
}
<nav>
<ul>
<li>About Us</li>
<li>Event Schedule</li>
<li>Contact Us</li>
</ul>
</nav>
Just add height: 100% to nav ul
nav {
background-color: aquamarine;
width: 100%;
height: 70px;
}
nav ul {
list-style: none;
display: flex;
justify-content: space-around;
align-items: center;
height: 100%;
}
nav ul li {
font-size: 3rem;
}
<nav>
<ul>
<li>About Us</li>
<li>Event Schedule</li>
<li>Contact Us</li>
</ul>
</nav>
The items are vertically aligned, or at least they try to be, but you have set the font size so large that they break out of your fixed-height container. Possible solutions:
Don't set the font-size.
Don't fix the height of the parent element.

Vertical tabs with flex box

I would like to accomplish a vertical tabs layout, so on the left would be the tabs and on the right the tab contents. The challenge is that I have to adhere to a specific markup which makes things complicated.
Below is my last attempt, I cant make the tabs fill in the space left by the content:
ul{
list-style-type: none;
padding: 0;
margin: 0;
}
.tabs-container{
display: flex;
flex-direction: column;
max-width: 400px;
}
.tab{
width: 100px;
background-color: gray;
}
.tab-content{
display: none;
}
.tab-content.active{
display: block;
height: 100px;
order: -1;
align-self: flex-end;
}
<ul class='tabs-container'>
<li class='tab'>tab one</li>
<li class='tab-content active'>contents tab one</li>
<li class='tab'>tab two</li>
<li class='tab-content '>contents tab two</li>
<li class='tab'>tab three</li>
<li class='tab-content'>contents tab three</li>
</ul>
Is there a solution for this problem using css flex box?
Thanks
<div class="container">
<div class="obj"><div class="tab">Tab1</div><div class="tab-c">TabContent1</div></div>
<div class="obj"><div class="tab">Tab2</div><div class="tab-c">TabContent2</div></div>
</div>
<style>
.container {
display: flex;
flex-direction: column;
background-color: tomato;
}
.obj {
display: flex;
/*no need of specify the flex direction here it's row by default*/
background-color: orange;
margin: 10px;
}
.tab {
width: 50px;
background-color: yellow;
}
.tab-c {
width: 150px;
background-color: white;
}
</style>
This is probably not the most elegant way to do it but it's work for my needs

CSS: Combining display:flex and overflow:auto kills padding [duplicate]

I have a list of items that I'm trying to arrange into a scrollable horizontal layout with flexbox.
Each item in the container has a margin left and right, but the right margin of the last item is being collapsed.
Is there a way to stop this happening, or a good workaround?
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 300px;
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
}
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
Potential Problem #1
The last margin is not being collapsed. It's being ignored.
The overflow property applies only to content. It doesn't apply to padding or margins.
Here's what it says in the spec:
11.1.1 Overflow: the overflow
property
This property specifies whether content of a block container element
is clipped when it overflows the element's box.
Now let's take a look at the CSS Box Model:
source: W3C
The overflow property is limited to the content box area. If the content overflows its container, then overflow applies. But overflow doesn't enter into the padding or margin areas (unless, of course, there is more content that follows).
Potential Problem #2
The problem with Potential Problem #1 is that it appears to fall apart outside of a flex or grid formatting context. For example, in a standard block layout, the last margin doesn't appear to collapse. So maybe overflow is permitted to cover margins / paddings, regardless of what it says in the spec.
div {
height: 150px;
overflow: auto;
width: 600px;
background: orange;
white-space: nowrap;
}
span {
background: blue;
color: #fff;
padding: 50px;
margin: 0 30px;
display: inline-block;
}
<div class="container">
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
</div>
Hence, maybe the problem is instead related to elements that are "over-constrained".
10.3.3 Block-level, non-replaced elements in normal
flow
The following constraints must hold among the used values of the other
properties:
margin-left + border-left-width + padding-left + width +
padding-right + border-right-width + margin-right = width of
containing block
If width is not auto and border-left-width + padding-left +
width + padding-right + border-right-width (plus any of
margin-left or margin-right that are not auto) is larger than
the width of the containing block, then any auto values for
margin-left or margin-right are, for the following rules, treated
as zero.
If all of the above have a computed value other than auto, the values are said to be "over-constrained" and one of the used values
will have to be different from its computed value. If the direction
property of the containing block has the value ltr, the specified
value of margin-right is ignored and the value is calculated so as
to make the equality true. If the value of direction is rtl,
this happens to margin-left instead
(emphasis added)
So, according to the CSS Visual Formatting Model, elements may be "over-constrained" and, as a result, a right margin gets tossed out.
Potential Workarounds
Instead of margin or padding, use a right border on the last element:
li:last-child {
border-right: 30px solid orange;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 100px; /* adjusted for demo */
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
}
li:last-child {
border-right: 30px solid orange;
}
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
Another solution uses a pseudo-elements instead of margins or padding.
Pseudo-elements on a flex container are rendered as flex items. The first item in the container is ::before and last item is ::after.
ul::after {
content: "";
flex: 0 0 30px;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 100px; /* adjusted for demo */
overflow: auto;
width: 600px;
background: orange;
}
ul li {
margin: 0 30px;
background: blue;
color: #fff;
padding: 90px;
white-space: nowrap;
flex-basis: auto;
}
ul::after {
content: "";
flex: 0 0 30px;
}
ul::before {
content: "";
flex: 0 0 30px;
}
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
Your problem is not the margin in itself. It's the scroll bar dimensioning only the visible content of the element.
One hack to solve it would be to create a visible element that occupies the margin
This solution handles this using a pseudo on the last child
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 300px;
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
position: relative;
}
li:last-child:after {
content: "";
width: 30px;
height: 1px;
position: absolute;
left: 100%;
top: 0px;
}
<div class"container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
You can set width and overflow on the div container, and set display: inline-flex rather than flex on the ul, so that the size of the flex box will be calculated based on the items inside, and all padding and margin will apply without any issues.
.container {
width: 600px;
overflow: auto;
}
.container ul {
list-style: none;
padding: 0;
margin: 0;
display: inline-flex;
background: orange;
}
.container li {
padding: 60px;
margin: 0 30px;
white-space: nowrap;
background: blue;
color: #fff;
}
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
See the solution. I remove white-space, flex-basis and margin, to provide you a pure flexbox solution.
It relays on flex-flow: row(horizontal), justify-content: space-around (your margin) and no more!! The width is changed to 1200px since the padding of 90px set the total width of the boxes more than your 600px (defined in your snippet).
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
flex-flow: row ;
justify-content: space-around;
height: 300px;
overflow: auto;
width: 1200px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
}
<div class"container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>

When the flex container overflow, the last item's right margin was cut off [duplicate]

I have a list of items that I'm trying to arrange into a scrollable horizontal layout with flexbox.
Each item in the container has a margin left and right, but the right margin of the last item is being collapsed.
Is there a way to stop this happening, or a good workaround?
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 300px;
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
}
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
Potential Problem #1
The last margin is not being collapsed. It's being ignored.
The overflow property applies only to content. It doesn't apply to padding or margins.
Here's what it says in the spec:
11.1.1 Overflow: the overflow
property
This property specifies whether content of a block container element
is clipped when it overflows the element's box.
Now let's take a look at the CSS Box Model:
source: W3C
The overflow property is limited to the content box area. If the content overflows its container, then overflow applies. But overflow doesn't enter into the padding or margin areas (unless, of course, there is more content that follows).
Potential Problem #2
The problem with Potential Problem #1 is that it appears to fall apart outside of a flex or grid formatting context. For example, in a standard block layout, the last margin doesn't appear to collapse. So maybe overflow is permitted to cover margins / paddings, regardless of what it says in the spec.
div {
height: 150px;
overflow: auto;
width: 600px;
background: orange;
white-space: nowrap;
}
span {
background: blue;
color: #fff;
padding: 50px;
margin: 0 30px;
display: inline-block;
}
<div class="container">
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
</div>
Hence, maybe the problem is instead related to elements that are "over-constrained".
10.3.3 Block-level, non-replaced elements in normal
flow
The following constraints must hold among the used values of the other
properties:
margin-left + border-left-width + padding-left + width +
padding-right + border-right-width + margin-right = width of
containing block
If width is not auto and border-left-width + padding-left +
width + padding-right + border-right-width (plus any of
margin-left or margin-right that are not auto) is larger than
the width of the containing block, then any auto values for
margin-left or margin-right are, for the following rules, treated
as zero.
If all of the above have a computed value other than auto, the values are said to be "over-constrained" and one of the used values
will have to be different from its computed value. If the direction
property of the containing block has the value ltr, the specified
value of margin-right is ignored and the value is calculated so as
to make the equality true. If the value of direction is rtl,
this happens to margin-left instead
(emphasis added)
So, according to the CSS Visual Formatting Model, elements may be "over-constrained" and, as a result, a right margin gets tossed out.
Potential Workarounds
Instead of margin or padding, use a right border on the last element:
li:last-child {
border-right: 30px solid orange;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 100px; /* adjusted for demo */
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
}
li:last-child {
border-right: 30px solid orange;
}
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
Another solution uses a pseudo-elements instead of margins or padding.
Pseudo-elements on a flex container are rendered as flex items. The first item in the container is ::before and last item is ::after.
ul::after {
content: "";
flex: 0 0 30px;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 100px; /* adjusted for demo */
overflow: auto;
width: 600px;
background: orange;
}
ul li {
margin: 0 30px;
background: blue;
color: #fff;
padding: 90px;
white-space: nowrap;
flex-basis: auto;
}
ul::after {
content: "";
flex: 0 0 30px;
}
ul::before {
content: "";
flex: 0 0 30px;
}
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
Your problem is not the margin in itself. It's the scroll bar dimensioning only the visible content of the element.
One hack to solve it would be to create a visible element that occupies the margin
This solution handles this using a pseudo on the last child
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
height: 300px;
overflow: auto;
width: 600px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
margin: 0 30px;
white-space: nowrap;
flex-basis: auto;
position: relative;
}
li:last-child:after {
content: "";
width: 30px;
height: 1px;
position: absolute;
left: 100%;
top: 0px;
}
<div class"container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
You can set width and overflow on the div container, and set display: inline-flex rather than flex on the ul, so that the size of the flex box will be calculated based on the items inside, and all padding and margin will apply without any issues.
.container {
width: 600px;
overflow: auto;
}
.container ul {
list-style: none;
padding: 0;
margin: 0;
display: inline-flex;
background: orange;
}
.container li {
padding: 60px;
margin: 0 30px;
white-space: nowrap;
background: blue;
color: #fff;
}
<div class="container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
See the solution. I remove white-space, flex-basis and margin, to provide you a pure flexbox solution.
It relays on flex-flow: row(horizontal), justify-content: space-around (your margin) and no more!! The width is changed to 1200px since the padding of 90px set the total width of the boxes more than your 600px (defined in your snippet).
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
flex-flow: row ;
justify-content: space-around;
height: 300px;
overflow: auto;
width: 1200px;
background: orange;
}
ul li {
background: blue;
color: #fff;
padding: 90px;
}
<div class"container">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>

Problems with a header menu

I am trying to make a header menu work as I like. I have only minor experience with CSS.
I have only two main menu items, each with a dropdown. I want for it to look something like this when hover on first menu item:
And something like this when hovering on second menu item:
Note: I want the "Menu 1" and "Menu 2" to be positioned as they are in the images, including this: Dropdown for Menu 1 is to the right of Menu 1, dropdown of Menu 2 is to the left of Menu 2. I forgot to include "Logo" in the images, but it is centered in the middle.
I have a fiddle of what I have managed so far (check it out here).
I have two problems:
The width is not 100%. The blue background is 100%, but I tried for the two menu items to be 25% each and then there is a logo-div in the middle that should fill out the rest (with margin: auto), so it is 100% all together.
The active main menu item gets pushed down on hover
How to fix it to work as intended?
body {
margin: 0px;
}
.header {
background-color: royalblue;
width: 100%;
margin-bottom: 1em;
height: 3.15em;
color: yellow;
box-shadow: 0px 0px 5px black;
}
.nav h3 {
display: inline-block;
margin-left: 0.3em;
}
section {
display: inline-block;
vertical-align: text-top;
}
.nav:first-of-type h3 {
text-align: right;
}
.nav ul {
display: inline-block;
padding: 0;
top:36px;
list-style-type: none;
width: 15em;
margin-top: 4em;
display: none;
}
.nav:nth-of-type(2) ul {
text-align: right;
}
.nav:hover ul {
display: inline-block;
}
.width-25 {
width: 25%;
}
.fill-width {
margin: auto;
}
<div class="header">
<section class="nav">
<h3 class="width-25">Menu item 1</h3>
<ul>
<li>Dropdown 1 item 1</li>
<li>Dropdown 1/2</li>
<li>item 1 # 3</li>
<li>Dropdown 1 item 4 long</li>
<li>DD 1-5</li>
<li>Dropdown 1 i. 6</li>
</ul>
</section>
<span class="fill-width">Logo</span>
<section class="nav">
<ul>
<li>Dropdown 2 item 1</li>
<li>Dropdown 2/2</li>
<li>item 2 # 3</li>
<li>Dropdown 2 item 4 long</li>
<li>DD 2-5</li>
<li>Dropdown 2 i. 6</li>
</ul>
<h3 class="width-25">Menu item 2</h3>
</section>
</div>
I updated your fiddle with a way of how you could do this.
JSFiddle
It probably isn't the best or the cleanest way, but I did this in about 10 mins quickly. It should give you a base on how to go further.
The main problem was pretty much not giving your h3 seperate classes like so:
<h3 class="right">
I hope this helps you with your problem :).
Some few things:
Add position: relative to .nav.
Give position: absolute to .nav ul.
Now check this out:
body {
margin: 0px;
}
.header {
background-color: royalblue;
width: 100%;
margin-bottom: 1em;
height: 3.15em;
color: green;
box-shadow: 0px 0px 5px black;
}
.nav h3 {
display: inline-block;
margin-left: 0.3em;
}
section {
display: inline-block;
body {
margin: 0px;
}
.header {
background-color: royalblue;
width: 100%;
margin-bottom: 1em;
height: 3.15em;
color: green;
box-shadow: 0px 0px 5px black;
}
.nav h3 {
display: inline-block;
margin-left: 0.3em;
}
section {
display: inline-block;
}
.nav:first-of-type h3 {
text-align: right;
}
.nav ul {
display: inline-block;
padding: 0;
top:36px;
list-style-type: none;
width: 15em;
margin-top: 4em;
display: none;
}
.nav:nth-of-type(2) ul {
text-align: right;
}
.nav:hover ul {
display: inline-block;
}
.width-25 {
width: 25%;
}
.fill-width {
margin: auto;
}
}
.nav:first-of-type h3 {
text-align: right;
}
.nav ul {
display: inline-block;
padding: 0;
top: 36px;
list-style-type: none;
width: 15em;
margin-top: 4em;
display: none;
}
.nav:nth-of-type(2) ul {
text-align: right;
}
.nav:hover ul {
display: inline-block;
}
.width-25 {
width: 25%;
}
.fill-width {
margin: auto;
}
.nav {
position: relative;
}
.nav ul {
position: absolute;
}
<div class="header">
<section class="nav">
<h3 class="width-25">Toolbox</h3>
<ul>
<li>ECMAScript 2015 (ES6)</li>
<li>TypeScript/ES7</li>
<li>Observables</li>
<li>Building/Deploying</li>
<li>Components</li>
<li>View/Input Data</li>
</ul>
</section>
<span class="fill-width"></span>
<section class="nav">
<ul>
<li>Forms</li>
<li>Pipes</li>
<li>Services</li>
<li>Directives</li>
<li>Dependency Injection</li>
<li>Testing</li>
</ul>
<h3 class="width-25">Angular 2</h3>
</section>
</div>
<div>
<h1>Title</h1>
<p>Content</p>
</div>
You need to set the position to absolute on the <ul> element to fix the issue of active menu getting pushed on hover.
position: absolute;
Check out this updated jsFiddle.
Also, what do you mean by the width is not 100%?

Resources