2

I use an invisible pseudo-element (::after) that occupies the last slot in the container. But if there is only one element, I'd like to position it in the center.

So in order to do it I need to "remove" the pseudo-element in that case.

How do I do it if it's possible?

.main {
  background: #999;
  margin: 0 auto;
  width: 500px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
.box {
  background: #7ab9d7;
  color: #555;
  height: 30px;
  width: 30%;
  margin-bottom: 30px;
  text-align: center;
  font-size: 30px;
  padding-top: 120px;
}
.main::after {
  height: 0;
  width: 30%;
  content: "";
}
<div class="main">
  <div class="box">1</div>
</div>

P.S. Example was taken from here but decreased number of children to 1.

P.S. P.S. If many divs - looks like this

enter image description here

If one div - looks like this

enter image description here

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Taras Yaremkiv
  • 3,440
  • 7
  • 32
  • 54
  • How about removing width:30%? – Vincent1989 Oct 08 '17 at 09:33
  • If there's only one `.box`, you want to remove the pseudo element or center it? – karthikaruna Oct 08 '17 at 09:36
  • @karthikaruna remove pseudo element – Taras Yaremkiv Oct 08 '17 at 09:37
  • 1
    In posted a universal solution, where if one item only it should be centered, which mean when 2 or more they will be left aligned. I just noticed in a comment below that you now also say _centered one by one_. Does that mean if there is 2 they should also be centered and not left aligned? And what if there is 1 or 2 in a row of its own? – Asons Oct 08 '17 at 11:34
  • @LGSon Generally no matter how many items in a row: 1)if only one row exists then items should be centered in it 2) if two and more rows exist - in last row items should be left aligned – Taras Yaremkiv Oct 08 '17 at 11:38
  • I updated my answer with a 2nd universal solution, where also when 2 items, they will be properly centered – Asons Oct 08 '17 at 13:34

4 Answers4

2

How to not display ::after content if one child in parent?

There is no CSS way (today, but with a parent selector it might some day) to remove the pseudo based on the amount of flex items, even if it is a child and act as a flex item, it still can't be explicit targeted with anything else than through its parent.


A simple workaround would be to use a left margin combined with transform when there is only 1 element, here using the only-child selector.

One could use position: relative; left: 50% as well, though margin-left is one line less

This will work with any number of elements, regardless their size.

Stack snippet

.main {
  background: #999;
  margin: 0 auto;
  width: 500px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.box {
  background: #7ab9d7;
  color: #555;
  height: 30px;
  width: 30%;
  margin-bottom: 30px;
  text-align: center;
  font-size: 30px;
  padding-top: 120px;
}

.main::after {
  height: 0;
  width: 30%;
  content: "";
}

.main div:only-child {                /*  added rule  */
  margin-left: 50%;
  transform: translateX(-50%);
}
<div class="main">
  <div class="box">1</div>
</div>
<br>
<div class="main">
  <div class="box">1</div>
  <div class="box">1</div>
</div>
<br>
<div class="main">
  <div class="box">1</div>
  <div class="box">1</div>
  <div class="box">1</div>
  <div class="box">1</div>
</div>
<br>

Updated based on a comment, where, if there is 2 items, they as well should be centered.

To be able to accomplish that with the existing markup/CSS, we also need to make use of the ::before pseudo.

With a couple of clever CSS selectors, we can count if there is 1 or 2 elements.

This work like that, when 1 or 2 items, auto margin is used, and with the order property the items are positioned after the ::after, which now is 100% wide and will push the items to a new row, and there they will not be affected by either pseudo.

For 3 items or more, they will positioned before both pseudo's, where the ::before now will act as the ::after did in the initial solution, and left align items on the last row.

Fiddle demo

Stack snippet

.main {
  background: #999;
  margin: 0 auto;
  width: 500px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.box {
  background: #7ab9d7;
  color: #555;
  height: 30px;
  width: 30%;
  margin-bottom: 30px;
  text-align: center;
  font-size: 30px;
  padding-top: 120px;
}

.main::before {
  height: 0;
  width: 30%;
  content: "";
  order: 1;
}
.main::after {
  height: 0;
  width: 100%;
  content: "";
  order: 2;
}

/* new rules */

.main div:only-child,                            /* if 1 only */
.main div:first-child:nth-last-child(2) + div {  /* if 2, the 2nd*/
  order: 3;
  margin-left: auto;
  margin-right: auto;
}

.main div:first-child:nth-last-child(2) {         /* if 2, the 1st */
  order: 3;
  margin-left: auto;
}
<div class="main">
  <div class="box">1</div>
</div>
<br>
<div class="main">
  <div class="box">1</div>
  <div class="box">2</div>
</div>
<br>
<div class="main">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
  <div class="box">4</div>
</div>
<br>
<div class="main">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
  <div class="box">4</div>
  <div class="box">5</div>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
1

Try this. Updated the fiddle. Removed the pseudo element that you used as a placeholder. Below is the updated CSS.

.main {
  background: #999;
  margin: 0 auto;
  width: 500px;
  display: flex;
  flex-wrap: wrap;
}
.box {
  background: #7ab9d7;
  color: #555;
  height: 30px;
  width: 30%;
  margin-bottom: 30px;
  text-align: center;
  font-size: 30px;
  padding-top: 120px;
}
.box:not(:nth-child(3n)) {
  margin-right: 5%;
}
.box:only-child {
  margin-right: auto;
  margin-left: auto;
}

With multiple .boxes

With single .box

karthikaruna
  • 3,042
  • 7
  • 26
  • 37
1

There is no simple solution to this problem using flexbox, because flexbox isn't designed for this sort of layout.

Notice that you need a hack – the pseudo-element – to achieve the main layout.

So it's not surprising that you need an even more complex hack – see the other answers – to build a variation of that layout.

Flexbox is designed for flexibility through the distribution of free space. As soon as you start to confine this flexibility (e.g., forcing a flex item to stay in a column by bringing in a fake item to hold a position), flexbox starts to break.

This concept is further explained here: Targeting flex items on the last row

This is a problem / limitation that the people at the W3C are aware of, which is one reason they rolled out CSS Grid Layout, which solves this problem cleanly and easily:

multiple items

.main {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 150px 150px;
  grid-row-gap: 30px;
  grid-column-gap: 3%;
  width: 500px;  
  background: #999;
  margin: 0 auto;
}

.box {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  background: #7ab9d7;
  color: #555;
  font-size: 30px;
}

.box:only-child {
  grid-column: 2 / 3;
}
<div class="main">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
  <div class="box">4</div>
  <div class="box">5</div>
</div>

single item

.main {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 150px 150px;
  grid-row-gap: 30px;
  grid-column-gap: 3%;
  width: 500px;  
  background: #999;
  margin: 0 auto;
}

.box {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  background: #7ab9d7;
  color: #555;
  font-size: 30px;
}

.box:only-child {
  grid-column: 2 / 3;
}
<div class="main">
  <div class="box">1</div>
</div>

jsFiddle demo

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

To achieve this first you need to remove the ::after, then you will need to justify-content: center; by default and target for every last box to left center using margin: 0 auto 30px 0;

However the last box can also be the first box, to overwrite this use:

   .box:first-child {
        margin: 0 auto 30px auto !important;
    }

To get the extra padding within each box you will need to add extra div and add the blue background separately.

.main {
  background: #999;
  margin: 0 auto;
  width: 500px;
  display: flex;
  flex-wrap: wrap;
  /*justify-content: space-between;*/
  justify-content: center;
  align-items: center;

}
.box {
    background: #7ab9d7;
    color: #555;
    height: 30px;
    /* width: 30%; */
    margin-bottom: 30px;
    text-align: center;
    font-size: 30px;
    padding-top: 120px;
    /* flex: auto; */
    /* flex: 1; */
    flex-basis: 33.33%;
}

.box:last-child{
   margin: 0 auto 30px 0;
}

.box:first-child {
    margin: 0 auto 30px auto !important;
}
/*
.main::after {
  height: 0;
  width: 30%;
  content: "";
}
*/
<div class="main">
  <div class="box">1</div>
  <div class="box">2</div>
  <div class="box">3</div>
  <div class="box">4</div>
  <div class="box">5</div>
</div>
Vincent1989
  • 1,593
  • 2
  • 13
  • 25
  • https://jsfiddle.net/3m9ad0sd/ it's looks like this, and I'd like to look this as a rows - like this https://jsfiddle.net/9nw1bxoz/ – Taras Yaremkiv Oct 08 '17 at 09:50