Fully justified horizontal menu with image and separators - html

I would like to achieve this fully justified horizontal menu:
Justifying is done with flexbox and works, but I could not get the separating mid-dots justified, too; they are made by using css-content via pseudo-class. Also, I am wondering if there's a better way to vertically center the items than faking it by adding a padding as I have done it.
Here's my code and the fiddle:
ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-between;
}
li.home {
padding: 0;
}
li {
vertical-align: middle;
padding-top: 10px;
}
nav {
border-bottom: 1px solid black;
border-top: 1px solid black;
height: 40px;
}
li::after {
//padding: 0em 0.4em;
content: '\00b7';
pointer-events: none;
}
li.home::after,
li.last::after {
content: none;
text-align: justify;
}
<nav id="main-menu">
<ul>
<li class="home">
<img src="http://dummyimage.com/40x40/000/fff">
</li>
<li class="second">Item 1</li>
<li>Item 2</li>
<li>One more Item</li>
<li>Another Item</li>
<li class="last">Contact</li>
</ul>
</nav>

body { margin: 0; } /* 1 */
nav {
height: 40px;
border-bottom: 1px solid black;
border-top: 1px solid black;
}
ul {
display: flex;
justify-content: space-between; /* 2 */
align-items: center; /* 2 */
height: 100%;
list-style: none;
padding: 0;
margin: 0;
}
li:not(.home) {
flex: 1; /* 3 */
height: 100%;
border: 1px dashed red; /* 4 */
background-color: lightgreen; /* 4 */
}
li:not(.home) > a { /* 5 */
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
li img { vertical-align: bottom; } /* 6 */
li { position: relative; } /* 7 */
li:not(.home):not(:last-child)::before { /* 8 */
position: absolute;
content: '\26AB'; /* 4 */
left: 100%;
top: 50%;
transform: translate(-50%,-50%);
z-index: 1;
pointer-events: none;
}
<nav id="main-menu">
<ul>
<li class="home">
<img src="http://dummyimage.com/40x40/000/fff">
</li>
<li class="second">Item 1</li>
<li>Item 2</li>
<li>One more Item</li>
<li>Another Item</li>
<li class="last">Contact</li>
</ul>
jsFiddle
Notes:
Remove default margins on body element
Methods for Aligning Flex Items
Consume all remaining space with flex-grow property
Borders, background colors, and larger bullets for illustration purposes only
Enable anchor elements to fully cover list item space and align text with flex properties
Remove baseline alignment (i.e., whitespace underneath image)
Establish nearest positioned ancestor for absolute positioning
Use absolute positioning to align bullets

You can vertically center the items with align-self: center; but the dot separators are in my opinion impossible to achieve with pseudo elements like :before or :after.
I would recommend to use separate <li> tags for separators like below:
Note that your image element needs display: block; to have a proper height.
ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-between;
}
img {
display: block;
}
li.home {
padding: 0;
}
li {
align-self: center;
}
nav {
border-bottom: 1px solid black;
border-top: 1px solid black;
height: 40px;
}
<nav id="main-menu">
<ul>
<li class="home">
<img src="http://dummyimage.com/40x40/000/fff">
</li>
<li class="second">Item 1</li>
<li class="separator">·</li>
<li>Item 2</li>
<li class="separator">·</li>
<li>One more Item</li>
<li class="separator">·</li>
<li>Another Item</li>
<li class="separator">·</li>
<li class="last">Contact</li>
</ul>
</nav>
Fiddle version

Related

How do I center a UL/LI in the same row as left-aligned text?

I've been trying to write a menubar that has two groupings in the same row across the top of a webpage: on the left is the site name and in the center should be the menu options (a ul/li). So far, following similar issues, I've written the following, which appears on first glance to do exactly what I'm seeking.
HTML:
<div class="menubar">
SITE NAME
<ul class="ul">
<li>MENU 0</li>
<li>MENU 1</li>
<li>MENU 2</li>
<li>MENU 3</li>
</ul>
</div>
CSS:
.menubar {
width: 100%;
height: 50px;
line-height: 50px;
vertical-align: middle;
background-color: #fff;
border-bottom: 1px solid #f5f5f5;
position: fixed;
top: 0;
display: flex;
flex-flow: row nowrap;
}
.logo {
width: 33.33%;
float: left;
margin-left: 20px;
font-size: 24px;
}
.ul {
font-size: 18px;
list-style: none;
padding: 0;
margin: 0;
}
.ul li {
margin-left: 15px;
margin-right: 15px;
display: inline-block;
}
However, if you look carefully in the JSFiddle (more apparent when widening browser windows or shrinking the window down just before the items begin wrapping), the 'centered' ul/li is not actually centered—it's closer to the left side of the browser window than the right. How do I fix this so that the ul/li remains truly centered in the menubar (as if the site name doesn't exist) with the left-aligned site name, regardless of what the browser window's width is? (I'm assuming within non-wrapping reason, since I plan to adjust sizes and behavior for smaller devices.)
JSFiddle
You're using a lot of margins, width and stuff. Check out flex here and you can get the same thing, properly aligned using flex and directions.
<!-- NEW CODE -->
<nav>
<div class="logo">
<span>Your Company</span>
</div>
<ul class="nav-items">
<li class="nav-item"> Menu 1 </li>
<li class="nav-item"> Menu 2 </li>
<li class="nav-item"> Menu 3 </li>
<li class="nav-item"> Menu 4 </li>
</ul>
</nav>
<!-- OLD CODE -->
<nav>
<div class="logo">
<img src="https://placehold.it/200x200" alt="logo">
</div>
<div class="menu-items">
<div class="menu-item"> Menu 0 </div>
<div class="menu-item"> Menu 1 </div>
<div class="menu-item"> Menu 2 </div>
<div class="menu-item"> Menu 3 </div>
</div>
</nav>
and the css
// MORE PROPERTIES
nav {
align-items: center;
}
nav div.logo {
position: absolute;
}
// OLD-NEW CSS
nav {
display: flex;
border: 1px solid pink;
}
nav div.logo {
border: 1px solid green;
display: flex;
align-items: center;
}
nav div.logo span {
padding: 0 0.5rem;
}
ul.nav-items {
border: 1px solid red;
display: flex;
flex-direction: row;
list-style-type: none;
margin: 0 auto;
padding: 0;
}
ul.nav-items li {
margin: 0 0.25rem;
padding: 0.5rem;
border: 1px solid blue;
}
// OLD CSS
nav {
display: flex;
}
nav div.menu-items {
display: flex;
flex-direction: row;
margin: 0 auto;
}
nav div.menu-items div.menu-item {
margin-left: 0.25rem;
margin-right: 0.25rem;
}
Fiddle:
NEW: https://jsfiddle.net/vzgn0Lju/1/
OLD: https://jsfiddle.net/kp9nsmah/1/
I added some margins between menu options and you can tweak a little bit more but flex is way easier than using lists and lots of things. You could use spans instead of div.menu items, can remove the container for items and such. But the general idea is there.

Align content between flex items using generated content

I have some cosmetic content which I would like to align between flex items which are justified with space-between.
The items have dynamic width.
The result in the following demo is what i'm looking for, except that since the content is purely cosmetic - I want to use generated content for them rather than actual elements.
ul {
display: flex;
justify-content: space-between;
align-items: center;
list-style: none;
padding: 0;
}
li:nth-child(odd) {
background-color: lime;
padding: 10px 20px
}
li:nth-child(even) {
margin: 0 auto;
}
<ul class="wpr">
<li>this is item 1</li>
<li>X</li>
<li>item 2</li>
<li>X</li>
<li>item 3 is considerably wider</li>
<li>X</li>
<li>item 4</li>
</ul>
Codepen demo (resize to see the effect)
I have attempted to do this by using absolute positioning - but I don't know if there is a mechanism to center the content between the items this way:
ul {
display: flex;
justify-content: space-between;
align-items: center;
list-style: none;
padding: 0;
}
li {
background-color: lime;
padding: 10px 20px;
position: relative;
}
li:after {
content: 'X';
position: absolute;
right: -50%; /* this obviously won't produce the correct centering */
}
<ul class="wpr">
<li>this is item 1</li>
<li>item 2</li>
<li>item 3 is considerably wider</li>
<li>item 4</li>
</ul>
Is it possible with CSS to add this content using generated content instead of actual elements?
Here is an idea with a small drawback because I had to omit the item in the last li:
ul {
display: flex;
justify-content: space-between;
align-items: center;
list-style: none;
padding: 0;
}
li {
display: flex;
align-items:center;
flex:1;
}
li span {
background-color: lime;
padding: 10px 20px;
white-space:nowrap;
}
li:after {
content: "X";
flex: 1;
text-align:center;
}
li:last-child {
flex:initial;
}
li:last-child::after {
content:none;
}
<ul class="wpr">
<li><span>this is item 1</span></li>
<li><span>item 2</span></li>
<li><span>item 3 is considerably wider</span></li>
<li><span>item 4</span></li>
</ul>
You can do it cleanly and efficiently with absolute positioning:
ul {
display: flex;
align-items: center;
list-style: none;
padding: 0;
}
li {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 10px 20px;
border: 1px dashed red;
position: relative;
}
li + li::before { /* #1 */
content: "X";
position: absolute;
right: 100%;
transform: translateX(50%); /* #2 */
}
<ul class="wpr">
<li>this is item 1</li>
<li>item 2</li>
<li>item 3 is considerably wider</li>
<li>item 4</li>
</ul>
revised codepen
Notes:
The adjacent sibling combinator (+) targets an element that immediately follows another element. In this case, the pseudo-element will only apply to li elements that follow another li. This will naturally exclude the ::before from the first li.
For an explanation of how this centering method works, see this post: Element will not stay centered, especially when re-sizing screen
One improvement i can suggest is replacing
li:after {
content: "X";
flex: 1;
text-align:center;
}
li:last-child {
flex:initial;
}
li:last-child::after {
content:none;
}
with
li:not(:last-child)::after {
content: "X";
flex: 1;
text-align:center;
}

CSS flex layout: wrapping when adding padding or margin

I'm trying to create a navigation bar, a logo at the left and the links centered in the remaining space. I followed the instructions in this thread:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
and used margin:auto; for the ul in order to get it centered. This worked fine so far but when I add margin or padding anywhere inside the ul the list of links wraps. I tried box-sizing:border-box; for the ul but no success. How can I fix this?
Fiddle: http://jsfiddle.net/30sy5dmy/5/
nav img {
height: 60px;
}
nav {
display: flex;
align-items: center;
justify-content: space-between;
}
.links {
margin: auto;
}
nav ul li {
display: inline-block;
list-style: none;
margin: 0 2%;
}
<nav>
<img src="https://teststein.000webhostapp.com/Logo.png">
<ul class="links">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 2</li>
<li>Link 2</li>
</ul>
</nav>
The list items are wrapping because you're using percentage margins.
When you give the items horizontal margins of 2%, the browser calculates the length of that 2% after the size of the container has been determined. In other words, the percentages are not factored into the container width.
Therefore, when the 2% length is added to the items, the total length exceeds that of the container and wrapping occurs.
Solution #1: white-space: nowrap
One way to fix the problem is by suppressing line breaks in the container using the white-space property. This forces all items to stay on the same line, overflowing the container if necessary.
nav {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px dashed black; /* for demo only */
}
nav img {
height: 60px;
}
.links {
margin: auto;
white-space: nowrap; /* NEW */
padding: 0; /* optional; remove default indentation on list elements */
border: 1px dashed red; /* for demo only */
}
nav ul li {
display: inline-block;
list-style: none;
margin: 0 2%;
}
<nav>
<img src="http://lorempixel.com/output/nature-q-c-60-60-1.jpg">
<ul class="links">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 2</li>
<li>Link 2</li>
</ul>
</nav>
Solution #2: Don't use percentage values
If you use any value other than percentages, the list items won't wrap because, unlike percentage values, they can expand the container for accommodation.
nav {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px dashed black; /* for demo only */
}
nav img {
height: 60px;
}
.links {
margin: auto;
padding: 0; /* optional; remove default indentation on list elements */
border: 1px dashed red; /* for demo only */
}
nav ul li {
display: inline-block;
list-style: none;
margin: 0 1em; /* adjustment */
}
<nav>
<img src="http://lorempixel.com/output/nature-q-c-60-60-1.jpg">
<ul class="links">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 2</li>
<li>Link 2</li>
</ul>
</nav>
Solution #3: Use flexbox
An initial setting of a flex container is flex-direction: row and flex-wrap: nowrap. This means that flex items will line up horizontally and cannot wrap.
nav {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px dashed black; /* for demo only */
}
nav img {
height: 60px;
}
.links {
margin: auto;
padding: 0; /* optional; remove default indentation on list elements */
border: 1px dashed red; /* for demo only */
display: flex; /* new */
}
nav ul li {
display: inline-block;
list-style: none;
margin: 0 1em; /* adjustment; avoid percentage margins on flex items;
see this post:
https://stackoverflow.com/q/36783190/3597276 */
}
<nav>
<img src="http://lorempixel.com/output/nature-q-c-60-60-1.jpg">
<ul class="links">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 2</li>
<li>Link 2</li>
</ul>
</nav>
Solution #4: Clean and Efficient Method
This solution attempts to use the least possible code to achieve the goal. Hope it helps.
nav { display: flex; align-items: center; }
nav img { height: 60px; }
a:first-of-type { margin-left: auto; }
a:last-of-type { margin-right: auto; }
a + a { margin-left: 1em; }
<nav>
<img src="http://lorempixel.com/output/nature-q-c-60-60-1.jpg">
Link 1
Link 2
Link 2
Link 2
</nav>
Simply add display: flex to the links, and then, as using percent for margins on flex items doesn't render the same cross browser, use i.e. viewport units instead.
nav img {
height: 60px;
}
nav {
display: flex;
align-items: center;
}
.links {
display: flex;
margin: auto;
}
nav ul li {
list-style: none;
margin: 0 2vw;
}
<nav>
<img src="http://lorempixel.com/output/nature-q-c-60-60-1.jpg">
<ul class="links">
<li>Link 1</li>
<li>Link 2</li>
<li>Link 2</li>
<li>Link 2</li>
</ul>
</nav>

Center middle flexbox item and justify the rest [duplicate]

This question already has an answer here:
Keep one element centered between two elements of different widths in flexbox
(1 answer)
Closed 6 years ago.
For an odd number of flex items I want the middle one to be in perfect center and other items just flow around it. The middle item has fixed width, all the rest are fluid and must stick to the middle item so the paddings are fixed.
/* CSS */
.flex-holder {
display: flex;
justify-content: center;
}
.middle-item {
width: 75px;
}
<!-- HTML -->
<ul class="flex-holder">
<li>Item 1</li>
<li>Some item</li>
<li class="middle-item">Middle</li>
<li>Very long text item</li>
<li>Test</li>
</ul>
Is it actually possible with flexbox? If no, please suggest another solution.
One solution would be to use position: absolute on middle element and center it with transform: translate() but there will be overflow of elements on small window size which you can fix with media queries.
.flex-holder {
display: flex;
justify-content: space-around;
position: relative;
list-style: none;
padding: 0;
}
.middle-item {
width: 75px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
<ul class="flex-holder">
<li>Item 1</li>
<li>Some item</li>
<li class="middle-item">Middle</li>
<li>Very long text item</li>
<li>Test</li>
</ul>
Another solution that will get result close to desired result is to wrap li's left and right of middle li in ul's and set flex: 1 on them so they take equal size and set middle div always in center.
ul, .wrap {
display: flex;
justify-content: space-around;
position: relative;
list-style: none;
flex: 1;
padding: 0;
}
.middle-item {
width: 75px;
background: lightblue;
text-align: center;
}
<ul class="flex-holder">
<li class="wrap">
<ul>
<li>Item 1</li>
<li>Some item</li>
</ul>
</li>
<li class="middle-item">Middle</li>
<li class="wrap">
<ul>
<li>Very long text item</li>
<li>Test</li>
</ul>
</li>
</ul>
The simpliest way if the middle-item is in the middle (same number of items at rigth and left):
.flex-holder {
display: flex;
justify-content: center;
list-style: none;
padding:0;
}
.flex-holder li {
flex: 1;
border: 1px solid red;
}
li.middle-item {
flex: 0 0 100px;
background-color: #ccc;
}
<ul class="flex-holder">
<li>Item 1</li>
<li>Some text</li>
<li class="middle-item">Middle</li>
<li>Very long text item bla bla bla bla bla bla.</li>
<li>Test</li>
</ul>
Also you can force to the middle nesting elements. For example:
body {
background: url(http://placehold.it/1x200) no-repeat center;
}
.flex-holder {
display: flex;
list-style: none;
padding: 0;
}
.flex-holder li {
display: flex;
flex: 1;
border: 1px solid red;
justify-content: flex-end;
padding: 0 1em;
}
.flex-holder li div {
border: 1px solid green;
padding: 0 1em;
}
li.middle-item {
flex: 0 0 auto;
background-color: #ccc;
text-align: center;
justify-content: center;
}
li.middle-item ~ li {
justify-content: flex-start;
}
<ul class="flex-holder">
<li>
<div>Item 1 </div>
<div> Some item</div>
</li>
<li class="middle-item">Middle</li>
<li>
<div>
Very long text item </div>
</li>
</ul>

How can I expand li elements to container size?

I have the following HTML:
#main-menu {
background-color: blue;
border: 1px solid black;
width: 600px;
}
.menu {
list-style: none outside none;
text-align: center;
}
.menu-item {
float: left;
}
.menu-item a {
border: 1px solid red;
}
<div id="main-menu">
<ul class="menu">
<li class="menu-item">Item #1</li>
<li class="menu-item">Item #2</li>
<li class="menu-item">Item #3</li>
<li class="menu-item">Item #4</li>
</ul>
</div>
How do I make the li elements automatically expand euqally to the fixed width of the container?
Thanks in advance! :-)
CodePen link: http://codepen.io/anon/pen/JoKgXz
I've updated you codepen codes..
CSS
#main-menu {
background-color: blue;
border: 1px solid black;
width: 600px;
overflow: hidden;
}
ul, li{
margin:0;
padding:0;
}
.menu {
list-style: none outside none;
text-align: center;
}
.menu-item {
float: left;
width:25%;
}
.menu-item a {
border: 1px solid red;
}
Demo
Ensure you have a proper CSS reset and use the box-sizing:border-box property.
This option has the virtue of not requiring set widths on the li
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#main-menu {
background-color: blue;
border: 1px solid black;
width: 600px;
}
.menu {
list-style: none outside none;
text-align: center;
display: table;
width: 100%;
}
.menu-item {
display: table-cell;
}
.menu-item a {
border: 1px solid red;
color: white;
display: block;
}
<div id="main-menu">
<ul class="menu">
<li class="menu-item">Item #1
</li>
<li class="menu-item">Item #2
</li>
<li class="menu-item">Item #3
</li>
<li class="menu-item">Item #4
</li>
</ul>
</d
First remove all margin and padding from the .menu. As you have four items in the menu, add width: 25% to the .menu-item. I've added a display: block to the <a> tag to make it fill the entire width of the .menu-item. As you use float: left the menu-items won't make the .menu container grow. The .menu:after adds a clearfix to have the menu contain all menu items.
Instead of float: left you could also have opted for a display: inline-block. In this case the clearfix wouldn't be necessary, but you need to make sure that the menu items don't have any whitespace (e.g. a newline) between them. Put them on one line like ...</li><li>... otherwise there will be some space between the menu items.
If you need some padding on the menu item make sure to add box-sizing: border-box as otherwise the width will refer to the content only. This means that after adding the padding the menu item will take up more than 25% of the width, which makes the last menu item wrap to a new line.
#main-menu {
background-color: blue;
border: 1px solid black;
width: 600px;
}
.menu {
list-style: none;
text-align: center;
margin: 0;
padding: 0;
}
.menu:after {
content: '';
display: block;
clear:both;
}
.menu-item {
float: left;
width: 25%;
}
.menu-item a {
display: block;
border: 1px solid red;
}
<div id="main-menu">
<ul class="menu">
<li class="menu-item">Item #1</li>
<li class="menu-item">Item #2</li>
<li class="menu-item">Item #3</li>
<li class="menu-item">Item #4</li>
</ul>
</div>