Diamond shape layout with CSS - html

I am attempting to layout an unordered list in a diamond form.
I cannot figure out how to do this without adding hacky <div>'s all over the place.
I'd rather keep it semantically a clean ul.
Code example (I can add id's, that is no problem.)
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>
I want it to look like this:
Perhaps something like this can be achieved with display: flex? Perhaps display: table-cell? I have tried everything so far, I cannot figure it out.

The layout can be achieved with flexbox all the way through:
ul {
display: flex;
flex-direction: column; /* 1 */
flex-wrap: wrap; /* 1 */
height: 200px; /* 2 */
list-style: none;
padding: 0; /* 3 */
}
li {
flex: 0 0 100%; /* 4 */
display: flex;
justify-content: center; /* 5 */
align-items: center; /* 5 */
background-color: lightyellow;
}
li:not(:first-child):not(:last-child) { /* 6 */
flex-basis: 50%;
}
span {
height: 50px;
width: 100px;
background-color: lightgreen;
border: 1px solid black;
display: flex; /* 7 */
justify-content: center; /* 7 */
align-items: center; /* 7 */
}
* { box-sizing: border-box; }
/* grid lines
ul { border: 1px dashed black; }
li { border: 1px solid red; }
*/
<ul>
<li><span>item 1</span></li>
<li><span>item 2</span></li>
<li><span>item 3</span></li>
<li><span>item 4</span></li>
</ul>
jsFiddle
Notes:
Set the container to column wrap.
For flex items to know where to wrap, a height must be defined on the container.
Remove ul default padding.
Make list items consume all column space.
Center spans vertically and horizontally.
Make second and third list items consume half column space, so both fit in one column.
Center text vertically and horizontally.

I'm interested in seeing if anyone comes up with something a little more clever. Here's the simplest route that came to mind - just using absolute positioning.
ul {
list-style: none;
padding: 0;
position: relative;
width: 200px;
height: 80px;
}
li {
border: 2px solid #000;
font-size: 18px;
padding: 4px;
position: absolute;
}
li:nth-child(1) { top: 50%; left: 0; transform: translateY(-50%); }
li:nth-child(2) { top: 0; left: 50%; transform: translateX(-50%); }
li:nth-child(3) { bottom: 0; left: 50%; transform: translateX(-50%); }
li:nth-child(4) { top: 50%; right: 0; transform: translateY(-50%); }
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>

ul li{
position:absolute;
}
#item1{
margin-left:10%;
}
#item2{
margin-top:5%;
}
#item3{
margin-top:5%;
margin-left:20%;
}
#item4{
margin-top:10%;
margin-left:10%;
}
<ul >
<li id=item1>item 1</li>
<li id=item2>item 2</li>
<li id=item3>item 3</li>
<li id=item4>item 4</li>
</ul>
here is my version ... just made it work ... you can find a better way or make it better...

another approach with flex (for info since another one is already given):
ul {
display:inline-flex;/* or flex + margin:auto for the demo*/
flex-flow:column;
flex-wrap:wrap;
height:6.25em;/* an height is required to force wraping into columns , mind basic margin, padding and lines wanted /set for li */*/
padding:0;
margin:0;
width:25em;/* whatever you want*/
background:gray;
}
li {
display:block;/* removes the bullet */
padding:0.25em;
border:solid;
width:32%;
margin:0.5em;/* whatever, just mind for ul height*/
box-sizing:border-box;/* includes padding and borders into height calculation .... */
}
li:first-of-type,li:last-of-type {
margin:2em 0;/* increase at least margin-top */
}
body {
text-align:center;/* to center inline-flex-container and li's text */
}
ul:hover {
font-size:1.25em;/* see behavior when font-size is different */
}
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>

You can just change the 2nd and 3rd <li>'s into <span>'s and wrap them in a <li>:
ul {
list-style: none;
}
li {
display: inline-block;
padding: 0;
vertical-align: middle;
}
li > span {
display: block;
}
li:not(:nth-of-type(2)),
li > span {
border: 2px solid black;
margin: 4px;
}
<ul>
<li>item 1</li>
<li>
<span>item 2</span>
<span>item 3</span>
</li>
<li>item 4</li>
</ul>
It's simple, quick, and doesn't require any sort of weird positioning.
To be fully semantically correct, you should technically use an ol, since you have an order to your items:
Usage note: The <ol> and <ul> elements both represent a list of items. They differ in that, with the <ol> element, the order is meaningful. As a rule of thumb to determine which one to use, try changing the order of the list items; if the meaning is changed, the <ol> element should be used, otherwise you can use <ul>.

Related

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>

Link to wrap around li with padding

I have an unordered list with a lot of list items. Some of the list items are links, some are not. I want to add padding to the list items so the appearance is consistent regardless of whether it is a link or not, i.e. I do not want to add padding to the anchor, but to the list and for the anchor to wrap around the link AND padding.
Despite using display:block on hover the background color is only around the text inside the link. It ignores the padding. Is there a way to get the link to include the padding (without putting padding on the link)?
ul li {
float: left;
line-height: 5em;
padding: 0 2em;
}
a:link {
display: block;
}
a:visited {
display: block;
}
a:hover {
display: block;
background-color: rgb(245, 245, 245);
}
a:active {
display: block;
background-color: rgb(245, 245, 245);
}
<ul>
<li>Item 1
</li>
<li>Item 2
</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5
</li>
</ul>
You can use the hover the li instead of the a to correct the background-color applied on hover by using:
li:hover a {
display: block;
}
li:hover {
background-color: rgb(245, 245, 245);
}
instead of:
a:hover {
display:block;
background-color:rgb(245,245,245);
}
See demo below:
ul li {
float: left;
line-height: 5em;
padding: 0 2em;
}
a:link {
display: block;
}
a:visited {
display: block;
}
li:hover a {
display: block;
}
li:hover {
background-color: rgb(245, 245, 245);
}
a:active {
display: block;
background-color: rgb(245, 245, 245);
}
<ul>
<li>Item 1
</li>
<li>Item 2
</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5
</li>
</ul>
Here you go, my comments should explain everything but feel free to ask.
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
ul{
list-style-type: none; /* Feel free to remove this, just easier without bullets */
}
ul li {
float: left;
line-height: 5em; /* Should be the same as height */
padding: 0 2em;
position: relative; /* Make sure a & a:before can stay contained */
height: 5em; /* Should be same as line-height */
white-space: nowrap; /* Ensure that your items don't wrap and mess up width */
z-index: -1; /* So that a:before is able to trigger with :hover */
}
a{
position: relative; /* Make sure a:before can stay contained */
display: block; /* Ensures that a can expand to full size of container */
}
a:before{
content: ""; /* Necessary for :before element to be created */
position: absolute; /* Vital - allows positioning */
top: 0;
right: -2em; /* Minus same padding as li has */
bottom: 0;
left: -2em; /* Minus same padding as li has */
z-index: -1; /* Makes sure :before doesn't go above anchor text */
}
a:hover:before,
a:active:before {
background-color:rgb(245,245,245);
}

Fully justified horizontal menu with image and separators

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

CSS Flexbox - Set item width to largest one

I have a list of items which should display in a row. I dynamically add some items with different text lengths to the list. Is it possible to align the items on the left side and set the width of each item to the width of the largest one?
I added an image to clarify it :
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item with long text added dynamically</li>
</ul>
To get the width of your element using javascript :
document.getElementById('yourID').clientWidth
Then you can use this value to adjust your other items
I don't think this is possible with just the flex-box model and no javascript. A solution (depending on your real use case) would be to use display: table :
ul {
display: table;
list-style-type: none;
table-layout: fixed;
padding: 10px;
position: relative;
}
ul::before {
position: absolute;
z-index:-1;
content: " ";
left:0px;
top:0px;
bottom:0;
width: 500px;
background: #999;
}
li {
display: table-cell;
color: white;
text-align: center;
background: black;
padding: 5px;
height: 30px;
line-height: 30px;
width: 33%;
white-space: nowrap;
}
li+li {
border-left: 10px solid #999;
}
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item with long text</li>
</ul>
Note that it's still tricky but I don't see any better css based solution.