339

enter image description here

Imagine the following layout, where the dots represent the space between the boxes:

[Left box]......[Center box]......[Right box]

When I remove the right box, I like the center box to still be in the center, like so:

[Left box]......[Center box].................

The same goes for if I would remove the left box.

................[Center box].................

Now when the content within the center box gets longer, it will take up as much available space as needed while remaining centered. The left and right box will never shrink and thus when where is no space left the overflow:hidden and text-overflow: ellipsis will come in effect to break the content;

[Left box][Center boxxxxxxxxxxxxx][Right box]

All the above is my ideal situation, but I have no idea how to accomplish this effect. Because when I create a flex structure like so:

.parent {
    display : flex; // flex box
    justify-content : space-between; // horizontal alignment
    align-content   : center; // vertical alignment
}

If the left and right box would be exactly the same size, I get the desired effect. However when one of the two is from a different size the centered box is not truly centered anymore.

Is there anyone that can help me?

Update

A justify-self would be nice, this would be ideal:

.leftBox {
     justify-self : flex-start;
}

.rightBox {
    justify-self : flex-end;
}
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Mark
  • 16,906
  • 20
  • 84
  • 117
  • 3
    Basically...you can't. That's not how `flexbox` is supposed to work. You might try another methodology. – Paulie_D Sep 03 '15 at 14:58
  • 2
    Would really complete flexbox if it did tho. Cause flexbox is all about distributing space and how the items behave within. – Mark Sep 03 '15 at 15:06
  • 1
    We discussed `justify-self` earlier today, so you might find this interesting: http://stackoverflow.com/questions/32551291/for-css-flexbox-why-are-there-no-justify-items-and-justify-self-properties – Michael Benjamin Sep 13 '15 at 19:47
  • Off topic, how did you make the animation? – SaintPreston Jul 19 '23 at 17:08

12 Answers12

259

If the left and right boxes would be exactly the same size, I get the desired effect. However when one of the two is a different size the centered box is not truly centered anymore. Is there anyone that can help me?

Here's a method using flexbox to center the middle item, regardless of the width of siblings.

Key features:

  • pure CSS
  • no absolute positioning
  • no JS/jQuery

Use nested flex containers and auto margins:

.container {
  display: flex;
}
.box {
  flex: 1;
  display: flex;
  justify-content: center;
}

.box:first-child > span { margin-right: auto; }

.box:last-child  > span { margin-left: auto;  }

/* non-essential */
.box {
  align-items: center;
  border: 1px solid #ccc;
  background-color: lightgreen;
  height: 40px;
}
p {
  text-align: center;
  margin: 5px 0 0 0;
}
<div class="container">
  <div class="box"><span>short text</span></div>
  <div class="box"><span>centered text</span></div>
  <div class="box"><span>loooooooooooooooong text</span></div>
</div>
<p>&#8593;<br>true center</p>

Here's how it works:

  • The top-level div (.container) is a flex container.
  • Each child div (.box) is now a flex item.
  • Each .box item is given flex: 1 in order to distribute container space equally (more details).
  • Now the items are consuming all space in the row and are equal width.
  • Make each item a (nested) flex container and add justify-content: center.
  • Now each span element is a centered flex item.
  • Use flex auto margins to shift the outer spans left and right.

You could also forgo justify-content and use auto margins exclusively.

But justify-content can work here because auto margins always have priority.

8.1. Aligning with auto margins

Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 7
    This solutions doesn't allow `width` to be specified – Alex G Aug 17 '17 at 23:12
  • @AlexG, how so? Can you provide an example? – Michael Benjamin Aug 18 '17 at 02:16
  • 6
    Changing the flexed elements' widths to distribute space evenly misses OP's question entirely. Their question has space between elements. Ex: "Imagine the following layout, where the dots represent the space between the boxes." Centering text in 3 evenly sized elements is trivial. – Leland Sep 01 '17 at 13:56
  • Respectfully disagree. There is space between *content*. Also the OP accepted the answer. – Michael Benjamin Apr 28 '18 at 09:11
  • 1
    The OP also said "When I remove the right box, I like the center box to still be in the center, like so", how would you go about it without using a third empty column? – Fabien Snauwaert Jun 05 '19 at 07:11
  • Could you explain the role of `flex: 1;` in more detail? I tried just setting flex-grow to 100 (100 is my standard value, just because I find it practical to divide if I need to), and it didn't work. I'm guessing it has something to do with flex being shorthand for `flex-shrink` and `flex-basis` as well, but I'm still a little confused. – Ariane Jun 28 '19 at 17:45
  • 1
    This did exactly what I needed for implementing MacOS-style window titlebars. Adding `white-space: nowrap` was the last piece of the puzzle to prevent text from wrapping when the area gets too small. – eritbh Feb 27 '20 at 01:31
82
  1. Use three flex items in the container
  2. Set flex: 1 to the first and last ones. This makes them grow equally to fill the available space left by the middle one.
  3. Thus, the middle one will tend to be centered.
  4. However, if the first or last item has a wide content, that flex item will also grow due to the new min-width: auto initial value.

    Note Chrome doesn't seem to implement this properly. However, you can set min-width to -webkit-max-content or -webkit-min-content and it will work too.

  5. Only in that case the middle element will be pushed out of the center.

.outer-wrapper {
  display: flex;
}
.item {
  background: lime;
  margin: 5px;
}
.left.inner-wrapper, .right.inner-wrapper {
  flex: 1;
  display: flex;
  min-width: -webkit-min-content; /* Workaround to Chrome bug */
}
.right.inner-wrapper {
  justify-content: flex-end;
}
.animate {
  animation: anim 5s infinite alternate;
}
@keyframes anim {
  from { min-width: 0 }
  to { min-width: 100vw; }
}
<div class="outer-wrapper">
  <div class="left inner-wrapper">
    <div class="item animate">Left</div>
  </div>
  <div class="center inner-wrapper">
    <div class="item">Center</div>
  </div>
  <div class="right inner-wrapper">
    <div class="item">Right</div>
  </div>
</div>
<!-- Analogous to above --> <div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item animate">Center</div></div><div class="right inner-wrapper"><div class="item">Right</div></div></div><div class="outer-wrapper"><div class="left inner-wrapper"><div class="item">Left</div></div><div class="center inner-wrapper"><div class="item">Center</div></div><div class="right inner-wrapper"><div class="item animate">Right</div></div></div>
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Excellent solution. However, I believe the style on the "Center" div could be removed since there's no matching CSS for "center inner-wrapper". @Oriol – Sufian Mar 02 '21 at 11:34
  • 2
    This is a much better solution than the approved one. Setting ```flex: 1;``` on the left and right elements is all you need to do the trick. – Bjarke Apr 12 '21 at 11:15
75

The key is to use flex-basis. Then the solution is simple as:

.parent {
    display: flex;
    justify-content: space-between;
}

.left, .right {
   flex-grow: 1;
   flex-basis: 0;
}

CodePen is available here.

gamliela
  • 3,447
  • 1
  • 35
  • 41
23

Here's an answer that uses grid instead of flexbox. This solution doesn't require extra grandchild elements in the HTML like the accepted answer does. And it works correctly even when the content on one side gets long enough to overflow into the center, unlike the grid answer from 2019.

The one thing this solution doesn't do is show an ellipsis or hide the extra content in the center box, as described in the question.

section {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}

section > *:last-child {
  white-space: nowrap;
  text-align: right;
}

/* not essential; just for demo purposes */
section {
  background-color: #eee;
  font-family: helvetica, arial;
  font-size: 10pt;
  padding: 4px;
}

section > * {
  border: 1px solid #bbb;
  padding: 2px;
}
<section>
  <div>left</div>
  <div>center</div>
  <div>right side is longer</div>
</section>

<section>
  <div>left</div>
  <div>center</div>
  <div>right side is much, much longer</div>
</section>

<section>
  <div>left</div>
  <div>center</div>
  <div>right side is much, much longer, super long in fact</div>
</section>
Brian Morearty
  • 2,794
  • 30
  • 35
  • This works. I changed the `
    ` tag to `
    ` and use the class name in the CSS file instead of the `.section` tag name.
    – Alan Wells Jan 21 '21 at 15:41
11

Instead of defaulting to using flexbox, using grid solves it in 2 lines of CSS without additional markup inside the top level children.

HTML:

<header class="header">
  <div class="left">variable content</div>
  <div class="middle">variable content</div>
  <div class="right">variable content which happens to be very long</div>
</header>

CSS:

.header {
  display: grid;
  grid-template-columns: [first] 20% auto [last] 20%;
}
.middle {
  /* use either */
  margin: 0 auto;
  /* or */
  text-align: center;
}

Flexbox rocks but shouldn't be the answer for everything. In this case grid is clearly the cleanest option.

Even made a codepen for your testing pleasure: https://codepen.io/anon/pen/mooQOV

Johan
  • 390
  • 4
  • 11
  • 2
    +1 for admitting that there may be other options than `flex` for everything. My only objection is that vertical alignment becomes easier using flex (`align-items: center`) in case your items are different heights and need to be aligned – Felipe Apr 17 '19 at 18:44
  • 1
    I like this solution and the idea of using grid for this problem, but I think your solution has some notable drawbacks. First, if either of the outer cells' contents are larger than 20%, the content is either going to wrap or overflow into the center cell. Second, if the center is larger than 60%, it's going to grow outside of its content box and either wrap or push the last cell out. Finally, the center doesn't shift to make room for the outer cells. It just forces them to wrap/overflow. It's not a bad solution, but IMO, it's not "clearly the cleanest option". – Chris Oct 10 '19 at 22:37
8

You can do this like so:

.bar {
    display: flex;    
    background: #B0BEC5;
}
.l {
    width: 50%;
    flex-shrink: 1;
    display: flex;
}
.l-content {
    background: #9C27B0;
}
.m { 
    flex-shrink: 0;
}
.m-content {    
    text-align: center;
    background: #2196F3;
}
.r {
    width: 50%;
    flex-shrink: 1;
    display: flex;
    flex-direction: row-reverse;
}
.r-content { 
    background: #E91E63;
}
<div class="bar">
    <div class="l">
        <div class="l-content">This is really long content.  More content.  So much content.</div>
    </div>
    <div class="m">
        <div class="m-content">This will always be in the center.</div>
    </div>
    <div class="r">
        <div class="r-content">This is short.</div>
    </div>
</div>
Mara Morton
  • 4,429
  • 1
  • 21
  • 12
7

Here is another way to do it, using display: flex in the parents and childs:

.Layout{
    display: flex;
    justify-content: center;
}
.Left{
    display: flex;
    justify-content: flex-start;
    width: 100%;
}
.Right{
    display: flex;
    justify-content: flex-end;
    width: 100%;
}
<div class = 'Layout'>
    <div class = 'Left'>I'm on the left</div>
    <div class = 'Mid'>Centered</div>
    <div class = 'Right'>I'm on the right</div>
</div>
Erik Martín Jordán
  • 4,332
  • 3
  • 26
  • 36
6

I wanted the exact result shown in the question, I combined answers from gamliela and Erik Martín Jordán and it works best for me.

.parent {
    display: flex;
    justify-content: space-between;
}

.left, .right {
   flex-grow: 1;
   flex-basis: 0;
}

.right {
   display: flex;
   justify-content: flex-end;
}
taipignas
  • 79
  • 1
  • 6
4

A slightly more robust grid solution looks like this:

.container {
  overflow: hidden;
  border-radius: 2px;
  padding: 4px;
  background: orange;
  display: grid;
  grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr);
}

.item > div {
  display: inline-block;
  padding: 6px;
  border-radius: 2px;
  background: teal;
}

.item:last-child > div {
  float: right;
}
<div class="container">
  <div class="item"><div contenteditable>edit the text to test the layout</div></div>
  <div class="item"><div contenteditable>just click me and</div></div>
  <div class="item"><div contenteditable>edit</div></div>
</div>

And here you can see it in Codepen: https://codepen.io/benshope2234/pen/qBmZJWN

enter image description here

benshope
  • 2,936
  • 4
  • 27
  • 39
0

you can also use this simple way to reach exact center alignment for middle element :

.container {
    display: flex;
    justify-content: space-between;
}

.container .sibling {
    display: flex;
    align-items: center;
    height: 50px;
    background-color: gray;
}

.container .sibling:first-child {
    width: 50%;
    display: flex;
    justify-content: space-between;
}

.container .sibling:last-child {
    justify-content: flex-end;
    width: 50%;
    box-sizing: border-box;
    padding-left: 100px; /* .center's width divided by 2 */
}

.container .sibling:last-child .content {
    text-align: right;
}

.container .sibling .center {
    height: 100%;
    width: 200px;
    background-color: lightgreen;
    transform: translateX(50%);
}

codepen: https://codepen.io/ErAz7/pen/mdeBKLG

Erfan Azary
  • 488
  • 6
  • 11
0

Althought I might be late on this one, all those solutions seems complicated and may not work depending on the cases you're facing.

Very simply, just wrap the component you want to center with position : absolute, while letting the other two with justify-content : space-between, like so :

CSS:

.container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: lightgray;
}

.middle {
  position: absolute;
  margin-left: auto;
  margin-right: auto;
  /* You should adapt percentages here if you have a background ; else, left: 0 and right: 0 should do the trick */
  left: 40%;
  right: 40%;
  text-align: center;
}

/* non-essential, copied from @Brian Morearty answer */
.element {
  border: 1px solid #ccc;
  background-color: lightgreen;
}

p {
  margin: 5px;
  padding: 5px;
}
<div class="container">
  <p class="element">First block</p>
  <p class="middle element">Middle block</p>
  <p class="element">Third THICC blockkkkkkkkk</p>
</div>
Denis Lebon
  • 953
  • 1
  • 7
  • 13
-1

Michael Benjamin has a decent answer but there is no reason it can't / shouldn't be simplified further:

.container {
  display: flex;
}

.box {
  flex: 1;
  display: flex;
  justify-content: center;
}

.box:first-child { justify-content: left; }

.box:last-child  { justify-content: right;  }

And html

<div class="container">
  <div class="box">short text</div>
  <div class="box">centered tex</div>
  <div class="box">loooooooooooooooong text</div>
</div>
wynx
  • 243
  • 2
  • 14