354

I would like to have A B and C aligned in the middle.

How can I get D to go completely to the right?

BEFORE:

enter image description here

AFTER:

enter image description here

ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
li:last-child {
  background: #ddd;
  /* magic to throw to the right*/
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>

https://jsfiddle.net/z44p7bsx/

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
hazyred
  • 3,618
  • 3
  • 12
  • 14

11 Answers11

487

Below are five options for achieving this layout:

  • CSS Positioning
  • Flexbox with Invisible DOM Element
  • Flexbox with Invisible Pseudo-Element
  • Flexbox with flex: 1
  • CSS Grid Layout

Method #1: CSS Positioning Properties

Apply position: relative to the flex container.

Apply position: absolute to item D.

Now this item is absolutely positioned within the flex container.

More specifically, item D is removed from the document flow but stays within the bounds of the nearest positioned ancestor.

Use the CSS offset properties top and right to move this element into position.

li:last-child {
  position: absolute;
  top: 0;
  right: 0;
  background: #ddd;
}
ul {
  position: relative;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p {
  text-align: center;
  margin-top: 0;
}
span {
  background-color: aqua;
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>

One caveat to this method is that some browsers may not completely remove an absolutely-positioned flex item from the normal flow. This changes the alignment in a non-standard, unexpected way. More details: Absolutely positioned flex item is not removed from the normal flow in IE11


Method #2: Flex Auto Margins & Invisible Flex Item (DOM element)

With a combination of auto margins and a new, invisible flex item the layout can be achieved.

The new flex item is identical to item D and is placed at the opposite end (the left edge).

More specifically, because flex alignment is based on the distribution of free space, the new item is a necessary counterbalance to keep the three middle boxes horizontally centered. The new item must be the same width as the existing D item, or the middle boxes won't be precisely centered.

The new item is removed from view with visibility: hidden.

In short:

  • Create a duplicate of the D element.
  • Place it at the beginning of the list.
  • Use flex auto margins to keep A, B and C centered, with both D elements creating equal balance from both ends.
  • Apply visibility: hidden to the duplicate D

li:first-child {
  margin-right: auto;
  visibility: hidden;
}
li:last-child {
  margin-left: auto;
  background: #ddd;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p { text-align: center; margin-top: 0; }
span { background-color: aqua; }
<ul>
  <li>D</li><!-- new; invisible spacer item -->
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>

Method #3: Flex Auto Margins & Invisible Flex Item (pseudo-element)

This method is similar to #2, except it's cleaner semantically and the width of D must be known.

  • Create a pseudo-element with the same width as D.
  • Place it at the start of the container with ::before.
  • Use flex auto margins to keep A, B and C perfectly centered, with the pseudo and D elements creating equal balance from both ends.

ul::before {
  content:"D";
  margin: 1px auto 1px 1px;
  visibility: hidden;
  padding: 5px;
  background: #ddd;
}
li:last-child {
  margin-left: auto;
  background: #ddd;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
p { text-align: center; margin-top: 0; }
span { background-color: aqua; }
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>true center</span></p>

Method #4: Add flex: 1 to left and right items

Starting with Method #2 or #3 above, instead of worrying about equal width for the left and right items to maintain equal balance, just give each one flex: 1. This will force them both to consume available space, thus centering the middle item.

You can then add display: flex to individual items in order to align their content.

NOTE about using this method with min-height: Currently in Chrome, Firefox, Edge and possibly other browsers, the shorthand rule flex: 1 breaks down to this:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0%

That percentage unit (%) on flex-basis causes this method to break when min-height is used on the container. This is because, as a general rule, percentage heights on the children require an explicit height property setting on the parent.

This is an old CSS rule dating back to 1998 (CSS Level 2) which is still in effect in many browsers to some degree or another. For complete details see here and here.

Here's an illustration of the problem posted in the comments by user2651804:

#flex-container {
  display: flex;
  flex-direction: column;
  background: teal;
  width: 150px;
  min-height: 80vh;
  justify-content: space-between;
}

#flex-container>div {
  background: orange;
  margin: 5px;
}

#flex-container>div:first-child {
  flex: 1;
}

#flex-container::after {
  content: "";
  flex: 1;
}
<div id="flex-container">
  <div>very long annoying text that will add on top of the height of its parent</div>
  <div>center</div>
</div>

The solution is to not use the percentage unit. Try px or just nothing at all (which is what the spec actually recommends, despite the fact that at least some of the major browsers have appended a percentage unit for whatever reason).

#flex-container {
  display: flex;
  flex-direction: column;
  background: teal;
  width: 150px;
  min-height: 80vh;
  justify-content: space-between;
}

#flex-container > div {
  background: orange;
  margin: 5px;
}


/* OVERRIDE THE BROWSER SETTING IN THE FLEX PROPERTY */

#flex-container > div:first-child {
  flex: 1;
  flex-basis: 0;
}

#flex-container::after {
  content: "";
  flex: 1;
  flex-basis: 0;
}


/* OR... JUST SET THE LONG-HAND PROPERTIES INDIVIDUALLY

#flex-container > div:first-child {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
}

#flex-container::after {
  content: "";
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 0;
}
 */
<div id="flex-container">
  <div>very long annoying text that will add on top of the height of its parent</div>
  <div>center</div>
</div>

Method #5: CSS Grid Layout

This may be the cleanest and most efficient method. There is no need for absolute positioning, fake elements or other hackery.

Simply create a grid with multiple columns. Then position your items in the middle and end columns. Basically, just leave the first column empty.

ul {
  display: grid;
  grid-template-columns: 1fr repeat(3, auto) 1fr;
  grid-column-gap: 5px;
  justify-items: center;
}

li:nth-child(1) { grid-column-start: 2; }
li:nth-child(4) { margin-left: auto; }

/* for demo only */
ul { padding: 0; margin: 0; list-style: none; }
li { padding: 5px; background: #aaa; }
p  { text-align: center; }
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
<p><span>| true center |</span></p>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • _"The new item must be the same width as the existing D item, or the middle boxes won't be precisely centered."_ - that sucks that it must be the same width, but thank god for CSS grid :) – Vucko Nov 07 '18 at 14:46
  • Using the Grid technique, the items aren't in the dead centre, is there a way to get the logo element to be dead in the centre? https://imgur.com/a/SVg9xsj – J86 Nov 19 '19 at 11:37
  • 1
    To get the Grid layout method to work on the vertical axis, besides the obvious, it needs only one extra line: https://stackoverflow.com/questions/72440616/all-items-centered-along-the-vertical-axis-except-the-last-which-should-sit-on-t – David Smith May 31 '22 at 23:40
  • Hi @Michael Benjamin I have a question about the BFC - do the flex and grid items stay in the Normal Flow? My doubt arises MAINLY because flex-direction: column stacks the flex items VERTICALLY like a Block Formatting Context does - Thanks in advance, I guess the comment doesn't really fit the question but I need someone amazing in CSS like you. –  Oct 21 '22 at 13:06
  • 1
    @Daniel, Perhaps post a complete question with examples, so you can get a complete answer. But, generally speaking, unless you apply absolute positioning (i.e., `position: absolute` or `position: fixed`) to a flex / grid item, it remains in the normal document flow. – Michael Benjamin Oct 21 '22 at 17:37
  • 1
    Great @Michael Benjamin then it will remain in the normal flow only DISTRIBUTED with a Grid Layout/Flexbox Layout (as the case may be) instead of a Block Layout, right? –  Oct 21 '22 at 17:57
  • How would you use the grid layout, having an element on the left and just one centred element in the middle? The centred element having a max-width and width: 100%. – Bru Mas Ribera Nov 09 '22 at 17:15
48

The simplest solution will be to justify-content center to the parent container and giving margin-left auto to first and last child element.

ul {
  display:flex;
  justify-content:center;
}


.a,.d {
  margin-left:auto;
}
<ul>
  <li class="a">A</li>
  <li>B</li>
  <li>C</li>
  <li class="d">D</li>
</ul>
Suhail Akhtar
  • 1,718
  • 15
  • 29
  • @RedGuy11 can you share what you did? – Suhail Akhtar Aug 23 '21 at 05:29
  • 13
    Unfortunately A/B/C will be centred within the *remaining* space by this method. In other words, even if there's room for them to be centred with respect to the full width, they won't be. – tremby Oct 23 '21 at 00:26
13

Most easy way

.box{
  display:flex;
  justify-content:center;
}
.item1{
   flex:1;
   display: flex;
   justify-content: center;
   transform: translateX(10px);/*D element Width[if needed]*/
}
 <div class="box">
   <div class="item1">
      <div>A</div>
      <div>B</div>
      <div>C</div>
   </div>
   <div class="item2">D</div>
 </div>
Razib Hossain
  • 708
  • 5
  • 13
9

Using the display:grid approach, you can simply put all of the ul children into the same cell and then set justify-self:

.ul {
  display: grid;
}

.ul > * {
  grid-column-start: 1;
  grid-row-start: 1;
  justify-self:center;
}

.ul > *:last-child {
  justify-self: right;
}

/* Make Fancy */
.li {
  display:inline-block;
  margin: 1px;
  padding: 5px;
  background: #bbb;
}
<div class='ul'>
  <span>
     <span class='li'>A</span>
     <span class='li'>B</span>
     <span class='li'>C</span>
  </span>
  <span class='li'>D</span>
</div>
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
0

If you want to make it aligned, you can simply attach an empty span and split the three child spans into them.

Ali Korkmaz
  • 105
  • 6
0

The easiest way:

.wrap {
    display:flex;
}
.full-width {
    width: 100%;
}
.centered {
    display: flex;
    justify-content:center;
}
.btn {
    display: flex;
    justify-content: end;
}
<div class="wrap">
   <div class="full-width"></div>
   <div class="full-width centered">
      <div>A</div>
      <div>B</div>
      <div>C</div>
   </div>
   <div class="full-width btn">D</div>
 </div>
Radim Šafrán
  • 463
  • 7
  • 16
-1

Inspired by the Method #5: CSS Grid Layout of @Michal Benjamin's solution and because I'm using Tailwind and as of now still don't have access to all the grid options by default. This seems to work:

ul {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

li {
  align-self: center;
}

li:nth-child(1) {
  justify-content: flex-start; /* OR margin-right: auto */
}

li:nth-child(3) {
  justify-content: flex-end; /* OR margin-left:auto */
}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
</ul>

PS: Not sure if mixing up flex and grid like this is a good idea!

isherwood
  • 58,414
  • 16
  • 114
  • 157
Saba Ahang
  • 578
  • 9
  • 24
  • 2
    you are not mixing grid and flex, justifty-content is a CSS grid property AND flexbox property (same for align-self) – Temani Afif May 29 '20 at 10:45
  • Also grid and flex are designed to work together. – TylerH Jan 19 '21 at 19:18
  • 1
    I attempted to make sense of your CSS in a snippet demo. You had invalid comment markup and a stray colon. Feel free to revise if it's not right. – isherwood Feb 11 '22 at 14:11
-2

Very clear question. I couldn't help but post the answer after a few hours of digging. We Could of solved this with tables, table-cell, absolute positions, transforms but we just had to do it with flexbox :)

.parent {
  display: flex;
  justify-content: flex-end;
}

.center {
  margin: auto;
}

http://codepen.io/rgfx/pen/BLorgd

rgfx
  • 330
  • 2
  • 11
  • 8
    This doesn't truly center the middle items because it only factors in the *remaining* space, not the *entire* space. http://codepen.io/anon/pen/QKGrRk – Michael Benjamin Sep 20 '16 at 23:35
  • @Michael_B, your right didn't notice that, my bad. Back to using absolute positions :( Maybe with your high rep you should set a bounty get this resolved for good. I would but I have a very low rep, even lower now trying to help you. – rgfx Sep 22 '16 at 18:29
  • No bounty is going to add a new behavior to the flexbox model, the, by Michael_B, given answer is as good as it gets...today. – Asons May 16 '17 at 12:07
-2

The accepted answer can be changed a bit because you can use grid template areas and do it without fake element

grid-template-areas '. b c'
grid-template-columns: 1fr 1fr 1fr
repo
  • 748
  • 1
  • 8
  • 19
  • Feel free to copy the snippet and make your changes. Also, a link to a specific answer is better than a potentially obsolete reference to the accepted answer. – isherwood Feb 11 '22 at 14:12
-3

I expanded on Michael_B's answer

.center-flex__2-of-3 > :nth-child(1), .center-flex__2-of-3 > :nth-child(3) {
  flex: 1;
}
.center-flex__2-of-3 > :nth-child(1) {
  justify-content: flex-start;
}
.center-flex__2-of-3 > :nth-child(3) {
  justify-content: flex-end;
}
.center-flex__1-of-2 > :nth-child(1) {
  margin: auto;
}
.center-flex__1-of-2 > :nth-child(2) {
  flex: 1;
  justify-content: flex-end;
}
.center-flex__2-of-2 > :nth-child(1) {
  flex: 1;
  justify-content: flex-start;
}
.center-flex__2-of-2 > :nth-child(2) {
  margin: auto;
}
.center-flex__1-of-2:before, .center-flex__1-of-1:before {
  content: '';
  flex: 1;
}
.center-flex__1-of-1:after, .center-flex__2-of-2:after {
  content: '';
  flex: 1;
}

[class*=center-flex] {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  border: 10px solid rgba(0, 0, 0, 0.1);
}
[class*=center-flex] > * {
  display: flex;
}

li {
  padding: 3px 5px;
}
2 of 3
<ul class="center-flex__2-of-3">
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
  </span>
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
</ul>

<br><br>
1 of 2
<ul class="akex center-flex__1-of-2">
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
</ul>

<br><br>
2 of 2
<ul class="akex center-flex__2-of-2">
  <span>
    <li>Accusamus</li>
    <li>Porro</li>
    <li>Culpa</li>
    <li>Sit</li>
  </span>
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
</ul>

<br><br>
1 of 1
<ul class="center-flex__1-of-1">
  <span>
    <li>Center</li>
    <li>this</li>
  </span>
</ul>

Here with the help of SASS as a codepen

yunzen
  • 32,854
  • 11
  • 73
  • 106
-4
ul { 
  padding: 0; 
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
  padding: 5px;
  background: #aaa;
}
li:last-child {
  background: #ddd;
  position:absolute;
  right:10px;

}
<ul>
  <li>A</li>
  <li>B</li>
  <li>C</li>
  <li>D</li>
</ul>
SUDHIR KUMAR
  • 381
  • 4
  • 10