Vertical-align doesn't work on flex item - html

I tried to vertically centralize plain text inside a flex box element.
I decided to use property display:table-cell with vertical-align: middle. But it doesn't seem to work properly in flexbox elements.
How can I centralize it vertically, ideally without using a wrapper or positioning, and while still truncating long text with ellipses?
.container {
width: 400px;
height: 400px;
font-weight: 700;
border: 1px solid #d9d9d9;
display: flex;
flex-direction: column;
}
.item {
display: table-cell;
vertical-align: middle;
flex: 1 1;
background-color: cyan;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item:nth-of-type(2n) {
background-color: aliceblue;
}
<div class="container">
<div class="item">Hello, I'm very very long string! Hello, I'm very very long string!</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
</div>
View On CodePen

One solution is to define each flex item as its own flex container in order to vertically center its contents with align-items:center. To keep text-overflow working, add a child element to each flex item, which can then be truncated with ellipses.
I can't offer a succinct explanation as to why text-overflow doesn't work with display:flex, and neither can David Wesst. In his words:
It turns out that there really isn't a clean way to do this. If you're wondering how I came to that conclusion you can stop because I didn't. Those responsible for the specification did, and you can read the full conversation that started with a Mozilla bug report and leads to a whole mail group discussion about why it should (or, in this case, should not) be implemented as part of the spec.
Here's a working example:
.container {
width: 400px;
height: 400px;
font-weight: 700;
border: 1px solid #d9d9d9;
display: flex;
flex-direction: column;
}
.item {
display: flex;
align-items: center;
flex: 1;
background-color: cyan;
}
.item span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item:nth-of-type(2n) {
background-color: aliceblue;
}
<div class="container">
<div class="item"><span>Hello, I'm very very long string! Hello, I'm very very long string!</span></div>
<div class="item"><span>Hello</span></div>
<div class="item"><span>Hello</span></div>
<div class="item"><span>Hello</span></div>
</div>
Also see:
Setting ellipsis on text from a flex container

When you make an element a flex container (with display: flex or display: inline-flex), all in-flow children become flex items.
All flex items have their display value controlled by the container. It doesn't matter what you specify, the container overrides it.
So when you give a flex item display: table-cell, the browser ignores it. Here's what it looks like in Chrome Dev Tools:
Style Tab
Computed Tab
A flex container "blockifies" flex items, causing them to assume many qualities of block-level elements (source).
But the vertical-align property applies only to inline-level and table-cell elements (source).
That's why it doesn't work.
Regardless, vertical-align, even if it worked, is a totally unnecessary hack in this case. There are flex properties designed for aligning content in flex items.
.container {
width: 500px;
height: 500px;
font-weight: 700;
border: 1px solid #d9d9d9;
display: flex;
flex-direction: column;
}
.item {
flex: 1 1;
display: flex;
justify-content: center; /* horizontal alignment, in this case */
align-items: center; /* vertical alignment, in this case */
background-color: cyan;
}
.item:nth-of-type(2n) {
background-color: aliceblue;
}
<div class="container">
<div class="item">Hello</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
</div>
Related posts:
How to vertically align text inside a flexbox?
Setting ellipsis on text from a flex container

Blockquote ...float, clear and vertical-align have no effect on a flex item.
according to https://css-tricks.com/snippets/css/a-guide-to-flexbox/
See How to vertically align text inside a flexbox? for a possible solution.

You have to define those items also as flex containers, using the following CSS for them (no table-cell display...):
display: flex;
flex-direction: column;
justify-content: center;
.container {
width: 400px;
height: 400px;
font-weight: 700;
border: 1px solid #d9d9d9;
display: flex;
flex-direction: column;
}
.item {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1 1;
background-color: cyan;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item:nth-of-type(2n) {
background-color: aliceblue;
}
<div class="container">
<div class="item">Hello, I'm very very long string! Hello, I'm very very long string!</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
<div class="item">Hello</div>
</div>

Related

Get rid of empty space on the right when word-wrap happens [duplicate]

This question already has answers here:
Make container shrink-to-fit child elements as they wrap
(4 answers)
Closed 1 year ago.
Below is simple example illustrating the problem. I have "Stackoverflow Stackoverflow" string and in first case it is displayed as a single line and in the second case word wrap happens. As you can see in the second case width of the div element is wider than a single "Stackoverflow" word. Is there a way to get rid of this empty space on the right? Resulting element has width 200px as specified per max-width but I want element to have actual width which is enough to fit it into 200px after word wrap.
body {
font-size: 30px;
}
.row {
margin-bottom: 10px;
}
.text-no-wrap {
background: yellowgreen;
white-space: nowrap;
display: inline-block;
}
.text-wrap {
max-width: 200px;
background: tomato;
white-space: normal;
display: inline-block;
}
<div class="row">
<div class="text-no-wrap">Stackoverflow Stackoverflow</div>
</div>
<div class="row">
<div class="text-wrap">Stackoverflow Stackoverflow</div>
</div>
You could try adding width: max-content; to the div's insde the .row
Note that width: max-content; isn't supported in Internet Explorer, but is supported on all other browsers.
Check the support of width: max-content; here.
I've added flex-direction: column; to the .row so the children of those
div's will appear underneath each other.
If you need display: flex; on the .row div, then This is the way to go. If you don't need display: flex; on the .row div, just simply remove it. And only use width: max-content; on the children;
body {
font-size: 30px;
}
.row {
display: flex;
flex-direction: column;
margin-bottom: 10px;
}
.text-no-wrap {
width: max-content;
background: yellowgreen;
}
.text-wrap {
width: max-content;
background: tomato;
}
<div class="row">
<div class="text-no-wrap">Stackoverflow1 Stackoverflow2</div>
</div>
<div class="row">
<div class="text-wrap">Stackoverflow1 Stackoverflow2</div>
<div class="text-wrap">Stackoverflow1 Stackoverflow2</div>
</div>
I believe this one is not a text-wrap issue. If you check the following code you will get multiple spaces in between wrapping text. This one is due to the
max-width: 200px;
specified for
.text-wrap.
body {
font-size: 30px;
}
.row {
display: flex;
margin-bottom: 10px;
}
.text-no-wrap {
background: yellowgreen;
white-space: nowrap;
}
.text-wrap {
max-width: 200px;
background: tomato;
white-space: normal;
}
<html>
<body>
<div class="row">
<div class="text-no-wrap">Stackoverflow Stackoverflow</div>
</div>
<div class="row">
<div class="text-wrap">Stackoverflow Stackoverflow testing text wrapping space issue</div>
</div>
</body>
</html>
Go through the demo you can see that after "testing text" multiple spaces is there.

CSS Flexbox how to centre divs which are different size? [duplicate]

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;
}
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>↑<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.
Use three flex items in the container
Set flex: 1 to the first and last ones. This makes them grow equally to fill the available space left by the middle one.
Thus, the middle one will tend to be centered.
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.
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>
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.
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>
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
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>
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>
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
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;
}
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
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>
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>

How to make sure a flex item is affected by its children's width?

I have the following flex and it's children (img):
<div class="flex">
<img>
</div>
.flex {
position: relative
-webkit-box-pack: center!important
display: flex!important
box-sizing: inherit
}
img {
max-height: 124px
height: 100%
vertical-align: middle
box-sizing: inherit
}
Sometimes .flex doesn't have width ... other times it has.
How to make sure .flex always have the children's width? (In this case that of an image?)
As fit-content, which one set as the parents width, lack reasonable browser support, use inline-flex.
.flex {
display: inline-flex;
box-sizing: inherit;
border: 1px dotted blue;
align-items: center; /* vert. align img */
}
.flex img {
display: block;
max-height: 124px;
height: 100%;
box-sizing: inherit;
}
<div class="flex">
<img src="http://placehold.it/500x350/">
</div>
<div class="flex">
<img src="http://placehold.it/500x50/">
</div>
Note, because of inconsistency between the browsers, an img as a flex item might render differently and a wrapper normally fixes that.
What you're looking for is to apply width: fit-content to the flex element.
Without this, .flex in the following example will stretch to the edge of the container.With this, .flex only stretches as wide as the content contained within (in this case, the image):
.flex {
display: flex;
border: 1px solid red;
width: fit-content;
}
<div class="flex">
<img src="http://placehold.it/100">
</div>
Hope this helps! :)

text-overflow ellipsis not working in nested flexbox

I have a two-column layout created with flexboxes.
In the right column, I have two rows, the first containing a header and the second containing the page content.
Within the header I have three columns, a button, some text, and a button.
I want the buttons to sit on the left and right of the column with the text taking up any additional room.
Finally, I want the text to have white-space:nowrap and text-overflow:ellipsis properties to truncate long titles.
My problem is this: I cannot get the text wrapping to work correctly in a flexbox that is nested in another flexbox, as shown in this fiddle:
http://jsfiddle.net/maxmumford/rb4sk3mz/3/
.container {
display: flex;
height: 100vh;
}
.left {
width: 200px;
background-color: yellow;
}
.right {
flex: 1;
background-color: blue;
color: white;
}
.header {
background: red;
color: white;
display: flex;
flex-direction: row;
}
.header .content {
white-space: nowrap;
flex: 0 1 auto;
text-overflow: ellipsis;
overflow: hidden;
min-width: 0;
}
.header .buttons {
background: green;
flex: 1 0 auto;
}
.header .content:hover {
white-space: normal;
}
<div class="container">
<div class="left">
content left
</div>
<div class="right">
<div class="header">
<div class="buttons">buttons</div>
<div class="content">
This content is really long and should wrap with ellipses, but for some reason it doesn't work when it's nested in a container with display: flex
</div>
<div class="buttons">buttons</div>
</div>
content right
</div>
</div>
However, the exact same code works when the header is not nested within a flex box:
http://jsfiddle.net/maxmumford/p7115f2v/1/
.header {
background: red;
color: white;
display: flex;
flex-direction: row;
}
.header .content {
white-space: nowrap;
flex: 0 1 auto;
text-overflow: ellipsis;
overflow: hidden;
min-width: 0;
}
.header .buttons {
background: green;
flex: 1 0 auto;
}
<div class="header">
<div class="buttons">buttons</div>
<div class="content">
This content is really long and is wrapped correctly... This content is really long and is wrapped correctly... This content is really long and is wrapped correctly... This content is really long and is wrapped correctly... This content is really long
and is wrapped correctly... This content is really long and is wrapped correctly... This content is really long and is wrapped correctly...
</div>
<div class="buttons">buttons</div>
</div>
How can I achieve what I want in the first fiddle?
Thanks
There are two issues in your code preventing the ellipsis from working:
div.right contains div.header, which in turn contains the button and text.
div.right is a flex item in the main container (.container).
By default, a flex item cannot be smaller than the size of its content. The initial setting on flex items is min-width: auto.
This means that the length of your text, which is not wrapping, will set a minimum size for parent flex items. One way to enable flex items to shrink past their content is to set a flex item to min-width: 0.
You've done this already for the .content flex item, but it needs to be set on the .right item, as well.
You've got the .content element set to flex: 0 1 auto.
This tells the flex item to use the size of the content (flex-basis: auto). The text sets the size.
Instead, set the width to 0 and let the flex container distribute space, as necessary. You can do this with flex: 1 1 0, which is the same as flex: 1.
.container {
display: flex;
height: 100vh;
}
.left {
width: 200px;
background-color: yellow;
}
.right {
flex: 1;
background-color: blue;
color: white;
min-width: 0; /* NEW */
}
.header {
background: red;
color: white;
display: flex;
flex-direction: row;
}
.header .content {
white-space: nowrap;
flex: 1; /* ADJUSTMENT */
text-overflow: ellipsis;
overflow: hidden;
min-width: 0;
}
.header .buttons {
background: green;
flex: 1 0 auto;
}
.header .content:hover {
white-space: normal;
}
<div class="container">
<div class="left">
content left
</div>
<div class="right">
<div class="header">
<div class="buttons">buttons</div>
<div class="content">
This content is really long and should wrap with ellipses, but for some reason it doesn't work when it's nested in a container with display: flex
</div>
<div class="buttons">buttons</div>
</div>
content right
</div>
</div>
revised fiddle
The values of :
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
Will work only if the content of your element exceeds it's width.
You can set the width of .header .content:
.header .content {
width: 50%;
}
And it will work as you expect it to:
http://jsfiddle.net/t82pqks4/
I hope this solution helps someone as nothing else I found worked because of how my page was a giant nested combination of flexbox. so min-width 0 didn't work, nothing I tried worked except this.
Inside the flex column that has the copy you want to wrap, you need to do this.
.row {
display: flex;
}
.col1 {
position: relative;
flex: 1 1 70%;
overflow: hidden;
}
.nested {
width: 100%;
position: absolute;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.col2 {
flex: 1 1 30%;
padding-left: 1rem;
}
<div class="row">
<div class="col1">
<div class="nested">This is my really long copy that will push it out of the screen, and will not respect the elippse no matter what I do!!!</div>
</div>
<div class="col2">
Last Text
</div>
</div>

Keep the middle item centered when side items have different widths

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;
}
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>↑<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.
Use three flex items in the container
Set flex: 1 to the first and last ones. This makes them grow equally to fill the available space left by the middle one.
Thus, the middle one will tend to be centered.
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.
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>
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.
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>
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
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>
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>
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
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;
}
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
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>
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>