Nicholas
Nicholas

Reputation: 5520

Add dividing line between flex items with equal space distribution

I have a list with different items which have auto widths (no fixed width can be given in my case). I use justify-content: space-between because my first item has to start at the beginning of the container and my last item at the end.

All of the above works fine, but whenever I try to add a line between these list items, the problems start to emerge. I have no way to determine how many px or % I have to position these lines. Is there any way to 'dynamically' position the lines between the different list-items or not?

The html we are using is not editable as it is rendered by the CMS we are using.

This is what I have:

This is what I have

This is what I try to achieve

Here is what I would like to achieve

Here is the code I currently have

html {
    box-sizing: border-box;
}

.Container {
    max-width: 70%;
    margin-right: auto;
    margin-left: auto;
    background: blue;
    padding-top: 20px;
    padding-bottom: 20px;
}

.Flex {
    display: flex;
    flex-flow: row wrap;
    justify-content: space-between;
    list-style: none;
    margin: 0;
    padding: 0;
}

.Flex-item {
    background: red;
    position: relative;
}

.Flex-item:after {
    content: "";
    position: absolute;
    background: white;
    width: 1px;
    height: 40px;
    top: 50%;
    transform: translateY(-50%);
}
<div class="Container">
    <ul class="Flex">
         <li class="Flex-item">Lorem</li>
         <li class="Flex-item">consectetur</li>
         <li class="Flex-item">vestibulum</li>
         <li class="Flex-item">nec</li>
         <li class="Flex-item">condimentum</li>
    </ul>
</div>

Upvotes: 47

Views: 79139

Answers (7)

You can use this solution:

.Container {
  max-width: 70%;
  margin-right: auto;
  margin-left: auto;
  background: blue;
  padding-top: 20px;
  padding-bottom: 20px;
}

.Flex {
  display: flex;
  justify-content: space-between;

  list-style: none;
  margin: 0;
  padding: 0;
}

.Flex-item {
  /* here's part of the trick (content-box) */
  background: red content-box;
}

/* here's part of the trick */
.Flex-item + .Flex-item {
  border-left: 1px solid white;
  margin-left: 1rem;
  padding-left: 1rem;
}
<div class="Container">
    <ul class="Flex">
         <li class="Flex-item">Lorem</li>
         <li class="Flex-item">consectetur</li>
         <li class="Flex-item">vestibulum</li>
         <li class="Flex-item">nec</li>
         <li class="Flex-item">condimentum</li>
    </ul>
</div>

Upvotes: 2

Roko C. Buljan
Roko C. Buljan

Reputation: 206151

Instead of :after use a stylistic, ARIA representational LIs

<li class="separator" aria-hidden="true" role="presentation"></li>

like:

.Container {
  max-width: 70%;
  margin: 0 auto;
  background: blue;
}

.Flex {
  display: flex;
  justify-content: space-between;
  list-style: none; padding: 20px 0; margin:0;
}

.Flex-item {
  background: red;
}

.separator {
  background: white; width: 1px;
}
<div class="Container">
  <ul class="Flex">
    <li class="Flex-item">Lorem</li>
    <li class="separator" aria-hidden="true" role="presentation"></li>
    <li class="Flex-item">consectetur</li>
    <li class="separator" aria-hidden="true" role="presentation"></li>
    <li class="Flex-item">vestibulum</li>
    <li class="separator" aria-hidden="true" role="presentation"></li>
    <li class="Flex-item">nec</li>
    <li class="separator" aria-hidden="true" role="presentation"></li>
    <li class="Flex-item">condimentum</li>
  </ul>
</div>

P.S: yes, I tried using display: list-item initially on the :after pseudo but naah.

Upvotes: 9

Stefan Lindberg
Stefan Lindberg

Reputation: 603

I'm using this solution on a project I'm working on.

It sets justify-content: space-between; on the flex container and flex: 1 1 auto; on the children with a left border on all childrens except first.

I modified your example CSS so you can have a look. I wasn't sure if you were going to have background color on the children so I just used line-height to get larger borders.

html {
    box-sizing: border-box;
}

.Container {
    max-width: 70%;
    margin-right: auto;
    margin-left: auto;
    background: blue;
    padding-top: 20px;
    padding-bottom: 20px;
}

.Flex {
    display: flex;
    flex-flow: row wrap;
    justify-content: space-between;
    list-style: none;
    margin: 0;
    padding: 0;
}

.Flex-item {
    flex: 1 1 auto;
    background: red;
    position: relative;
    text-align: center;
    line-height: 40px;
}

.Flex-item + .Flex-item {
    border-left: solid 1px white;
}

/** Optional for OPs exact layout */

.Flex-item:first-child {
    text-align: left;
}

.Flex-item:last-child {
    text-align: right;
}
<div class="Container">
    <ul class="Flex">
        <li class="Flex-item">Lorem</li>
        <li class="Flex-item">consectetur</li>
        <li class="Flex-item">vestibulum</li>
        <li class="Flex-item">nec</li>
        <li class="Flex-item">condimentum</li>
    </ul>
</div>

No modification to HTML.

Upvotes: 40

G-Cyrillus
G-Cyrillus

Reputation: 105903

if you imbricate flex, you might get something close to what you try to do without any extra markup.

It involves pseudos, order & css3 selectors(fine aside flex).

http://codepen.io/gc-nomade/pen/BpzYWP

body {
  margin:0;
}

.Flex, .Flex-item {
  display:flex;
  padding:0.5em 0;
  margin:0;
}
.Flex-item  {
  flex:1;/*spray them evenly*/
  
}
.Flex-item:before,
.Flex-item:after {/* add pseudos to fill extra space */
  content:'';
  flex:1;/* thats where it takes room not use by the text */
  margin:-1em 0;/* grow them taller */
  border-right:solid 1px white;
}
.Flex-item:first-child:before
{
  order:2;/* put both pseudo after text*/ 
  flex:.5;/* shrink that one can be .75 to .25 */
  border:none; /* remove the border useless for the show */
}

.Flex-item:last-child:after {
  order:-1;/* put both pseudos before text */
  flex:0.5;/* shrink that one can be .75 to .25 */
}
.Flex-item:first-child + .Flex-item:before ,
.Flex-item:nth-last-child(1):after{
  border:none; /* remove the border useless for the show */
}
body, :after, :before {
  background:tomato;/* pseudo will hide li background */
}
li {
  background:turquoise;
}

/* give some space around the text */
.Flex-item:first-child {
  padding-left:1em;
}
.Flex-item:last-child {
  padding-right:1em;
}
.Flex-item:before {
  border:none;/* we do not need those after all */
  margin-right:1em;
}
.Flex-item:after {
  margin-left:1em;
}
.Flex-item:first-child:before {
  margin:-1em 0;
}.Flex-item:last-child:after {
  margin:-1em 0;
}
<div class="Container">
    <ul class="Flex">
         <li class="Flex-item">Lorem</li>
         <li class="Flex-item">consectetur</li>
         <li class="Flex-item">vestibulum</li>
         <li class="Flex-item">nec</li>
         <li class="Flex-item">condimentum</li>      
    </ul>
</div>

Upvotes: 1

Michael Benjamin
Michael Benjamin

Reputation: 371271

I think the only way to accomplish this with flexbox is to wrap the text in a new element, as @Kukkuz has done in another answer.

Without that extra wrapper, you can still get the equal spaced dividers, but the red background isn't confined to the length of the text.

Below is an example with:

  • No changes to the HTML.
  • No need for pseudo elements.
  • No need for absolute positioning.

If a background color for the text isn't necessary, then remove it and this should be all you need.

.Flex {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}
.Flex-item {
  flex: 1 1 auto;
  background: red;
}
.Flex-item {
  text-align: center;
}
.Flex-item:first-child {
  text-align: left;
}
.Flex-item:last-child {
  text-align: right;
}
.Flex-item + .Flex-item {
  border-left: 1px solid white;
}
.Container {
  max-width: 70%;
  margin-right: auto;
  margin-left: auto;
  background: blue;
  padding-top: 20px;
  padding-bottom: 20px;
}
<div class="Container">
  <ul class="Flex">
    <li class="Flex-item">Lorem</li>
    <li class="Flex-item">consectetur</li>
    <li class="Flex-item">vestibulum</li>
    <li class="Flex-item">nec</li>
    <li class="Flex-item">condimentum</li>
  </ul>
</div>

If you can add a span around the text, which will allow you to limit the red background to the length of the text, then you're all set:

.Flex {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}
.Flex-item {
  flex: 1 1 auto;
  display: inline-flex;
  justify-content: center;
}
.Flex-item span {
    background-color: red;
}
.Flex-item:first-child span {
  margin-right: auto;
}
.Flex-item:last-child span {
  margin-left: auto;
}
.Flex-item + .Flex-item {
  border-left: 1px solid white;
}
.Container {
  max-width: 70%;
  margin-right: auto;
  margin-left: auto;
  background: blue;
  padding-top: 20px;
  padding-bottom: 20px;
}
<div class="Container">
  <ul class="Flex">
    <li class="Flex-item"><span>Lorem</span></li>
    <li class="Flex-item"><span>consectetur</span></li>
    <li class="Flex-item"><span>vestibulum</span></li>
    <li class="Flex-item"><span>nec</span></li>
    <li class="Flex-item"><span>condimentum</span></li>
  </ul>
</div>

Upvotes: 2

kukkuz
kukkuz

Reputation: 42352

You can make it work by using a nested flexboxes - I understand you can't change the markup, but at least you have to wrap the contents of the li into a span like I have here:

  1. Make .flex-item also a flexbox with the text in a span (this would have the red background now) and the separator as an :after element

  2. Apply flex-grow and flex-shrink to 1 and flex-basis to auto for the Flex-item.

  3. The flex: 0 to the last Flex-item and margin-auto to the :after also contributes to the effect.

A demo may explain it better - see below:

html {
  box-sizing: border-box;
}
.Container {
  max-width: 70%;
  margin-right: auto;
  margin-left: auto;
  background: blue;
  padding-top: 20px;
  padding-bottom: 20px;
}
.Flex {
  display: flex;
  justify-content: space-between;
  list-style: none;
  margin: 0;
  padding: 0;
}
.Flex-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex: 1 1 auto;
}
.Flex-item span {
  background: red;
}
.Flex-item:not(:last-child):after {
  content: "";
  border: 1px solid white;
  height: 40px;
  margin: auto;
}
.Flex-item:last-child {
  flex: 0;
}
<div class="Container">
  <ul class="Flex">
    <li class="Flex-item">
      <span>Lorem</span>
    </li>
    <li class="Flex-item">
      <span>consectetur</span>
    </li>
    <li class="Flex-item">
      <span>vestibulum</span>
    </li>
    <li class="Flex-item">
      <span>nec</span>
    </li>
    <li class="Flex-item">
      <span>condimentum</span>
    </li>
  </ul>
</div>

Upvotes: 7

vedankita kumbhar
vedankita kumbhar

Reputation: 1490

Try this

html {
    box-sizing: border-box;
}

.Container {
    max-width: 90%;
    margin-right: auto;
    margin-left: auto;
    background: blue;
    padding-top: 11px;
    padding-bottom: 50px;
}

.Flex {
    list-style: none;
    margin: 0;
    padding: 0;
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    float: left;
}

.Flex-item {
    position: relative;
    float: left;
    padding: 5px 10px;
    border-right:1px solid #fff;
}
.Flex-item:last-child{border-right:none;}

.Flex-item >div{
  margin:0 5px;
  background:red;
  padding:5px;
}
<div class="Container">
    <ul class="Flex">
         <li class="Flex-item"><div>
         Lorem
         </div></li>
         <li class="Flex-item"><div>
         consectetur
         </div></li>
         <li class="Flex-item"><div>
         vestibulum
         </div></li>
         <li class="Flex-item"><div>
         nec
         </div></li>
         <li class="Flex-item"><div>
         condimentum
         </div></li>
    </ul>
</div>

Upvotes: 0

Related Questions