I've managed to get CSS3 to almost do what I want:
The grey .top-middle container is of arbitrary width, and must always remain flush with the top edge of the parent container (the <main>).
The purple left and right containers are also of arbitrary width, and must always remain flush with their respective edges of the parent container.
When the parent container is sufficiently wide, the three top containers should sit side-by-side; otherwise, the left and right containers should sit just beneath the .top-middle container. (It would be nice if the two purple containers dropped at the same time, but I'll live with one of them remaining next to the middle container, when there's space.)
The minimum width of the <main> container should essentially be the width of the .top-middle container
The .top-middle container should be centred, ideally relative to the parent container (<main>), but at least relative to the horizontal space available to it (between the left and right containers)
It's that last requirement, #5, that I haven't managed.
I'd prefer not to resort to JavaScript, and of course, it needs to be a cross-browser solution. (I don't really care about IE < 11, though—people using that cruft have bigger worries than whether my CSS looks pretty!)
N.B. This issue cannot (AFAIK) be tested in jsfiddle, StackOverflow's code snippet feature, or any other fixed-width environment. Please copy my code and paste it into a file, and watch what happens when you widen or narrow the browser window.
Here's my complete code, for your copying and pasting pleasure:
main div {
white-space: nowrap;
color: white;
}
.top-container {
background: olive;
}
.top-middle {
background: grey;
}
.top-left,
.top-right {
background: purple;
}
.bottom-container {
background: blue;
text-align: center;
}
/* The crux of the layout starts here */
main {
height: 300px;
display: flex;
flex-direction: column;
}
.top-container>* {
display: inline-block;
padding: 1em;
}
.top-left {
float: left;
}
.top-right {
float: right;
}
.spacer {
background: silver;
flex-grow: 1;
}
<main>
<div class="top-container">
<div class="top-middle">
This should be centred, and always stay on top.
<em>Left</em> and <em>right</em> should drop when the window contracts.
</div>
<div class="top-left">left</div>
<div class="top-right">right</div>
</div>
<div class="spacer"></div>
<div class="bottom-container">This is the bottom.</div>
</main>
Your fifth requirement can be achieved by making the following change:
Add text-align: center; to .top-container
This works because text-align effects the alignment of inline elements:
The text-align CSS property describes how inline content like text is aligned in its parent block element. text-align does not control the alignment of block elements itself, only their inline content.
text-align (https://developer.mozilla.org/en-US/docs/Web/CSS/text-align)
.top-middle is set to be inline-block due to .top-container>* { display: inline-block; padding: 1em; } so will be centered within .top-container.
main div {
white-space: nowrap;
color: white;
}
.top-container {
background: olive;
text-align: center;
}
.top-middle {
background: grey;
}
.top-left,
.top-right {
background: purple;
}
.bottom-container {
background: blue;
text-align: center;
}
/* The crux of the layout starts here */
main {
height: 300px;
display: flex;
flex-direction: column;
}
.top-container>* {
display: inline-block;
padding: 1em;
}
.top-left {
float: left;
}
.top-right {
float: right;
}
.spacer {
background: silver;
flex-grow: 1;
}
<main>
<div class="top-container">
<div class="top-middle">
This should be centred, and always stay on top.
<em>Left</em> and <em>right</em> should drop when the window contracts.
</div>
<div class="top-left">left</div>
<div class="top-right">right</div>
</div>
<div class="spacer"></div>
<div class="bottom-container">This is the bottom.</div>
</main>
Unfortunately, there is a drawback with this method as a bug in Firefox will always force .top-left and .top-right onto a new line:
Also affecting Bugzilla itself, as glob pointed out on IRC. https://bugzilla.mozilla.org/show_bug.cgi?id=677757#c4 has the
"comment 4" text hidden
The cause is this code in nsLineLayout::ReflowFrame:
if (psd->mNoWrap) {
// If we place floats after inline content where there's
// no break opportunity, we don't know how much additional
// width is required for the non-breaking content after the float,
// so we can't know whether the float plus that content will fit
// on the line. So for now, don't place floats after inline
// content where there's no break opportunity. This is incorrect
// but hopefully rare. Fixing it will require significant
// restructuring of line layout.
// We might as well allow zero-width floats to be placed, though.
availableWidth = 0;
}
I wonder whether the right thing to do is:* not manipulate the
available width at all, or* make the available width infinite, since
the nowrap content is never going to wrap around the float anyway
(In theory, the correct solution is not to try placing the float until
the following break opportunity. I wonder if other browsers do that.)
Bug 488725 - float pushed down one line with white-space: nowrap; (https://bugzilla.mozilla.org/show_bug.cgi?id=488725)
You could also use flex to center .top-middle although this wont push both .top-left and .top-right down to a new line when the space is taken up. To achieve this make the following changes:
Add display: flex; to .top-container to make its children use the flexbox model
Add flex-wrap: wrap; to .top-container to make its children wrap onto new lines when they run out of space
Add justify-content: space-between; to .top-container to add space around the child elements which will center .top-middle
main div {
white-space: nowrap;
color: white;
}
.top-container {
background: olive;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.top-middle {
background: grey;
}
.top-left,
.top-right {
background: purple;
}
.bottom-container {
background: blue;
text-align: center;
}
/* The crux of the layout starts here */
main {
height: 300px;
display: flex;
flex-direction: column;
}
.top-container>* {
display: inline-block;
padding: 1em;
}
.top-left {
float: left;
}
.top-right {
float: right;
}
.spacer {
background: silver;
flex-grow: 1;
}
<main>
<div class="top-container">
<div class="top-left">left</div>
<div class="top-middle">
This should be centred, and always stay on top.
<em>Left</em> and <em>right</em> should drop when the window contracts.
</div>
<div class="top-right">right</div>
</div>
<div class="spacer"></div>
<div class="bottom-container">This is the bottom.</div>
</main>
To get both .top-left and .top-right moving onto a new line when they run out of space you will need to use JavaScript. The following is using vanilla JavaScript but should provide a good starting point. In principle:
We check to see if the combined width of .top-left, .top-middle and .top-right is equal to or bigger than .top-container
If it is then we change the order of .top-left and .top-right to place them after .top-middle. We add margin to .top-left to force it onto a new line and margin to .top-middle to center it (as there are no elements in the same line for justify-content: space-between; to work)
If it isn't we set the styles back to the defaults
var topContainer = document.getElementsByClassName('top-container')[0];
var topLeft = document.getElementsByClassName('top-left')[0];
var topMiddle = document.getElementsByClassName('top-middle')[0];
var topRight = document.getElementsByClassName('top-right')[0];
var topContainerWidth;
var topLeftWidth = topLeft.offsetWidth;
var topMiddleWidth = topMiddle.offsetWidth;
var topRightWidth = topRight.offsetWidth;
var moveDivs;
(moveDivs = function(){
topContainerWidth = topContainer.offsetWidth;
if ((topLeftWidth + topMiddleWidth + topRightWidth) >= topContainerWidth) {
topLeft.style.order = 1;
topLeft.style.marginRight = topRightWidth + 'px';
topMiddle.style.margin = '0 auto';
topRight.style.order = 2;
} else {
topLeft.style.order = 0;
topLeft.style.marginRight = 0;
topMiddle.style.margin = 0;
topRight.style.order = 2;
}
})();
window.addEventListener('resize', function(event){
moveDivs();
});
main div {
white-space: nowrap;
color: white;
}
.top-container {
background: olive;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.top-middle {
background: grey;
}
.top-left,
.top-right {
background: purple;
}
.bottom-container {
background: blue;
text-align: center;
}
/* The crux of the layout starts here */
main {
height: 300px;
display: flex;
flex-direction: column;
}
.top-container>* {
display: inline-block;
padding: 1em;
}
.top-left {
float: left;
}
.top-right {
float: right;
}
.spacer {
background: silver;
flex-grow: 1;
}
<main>
<div class="top-container">
<div class="top-left">left</div>
<div class="top-middle">
This should be centred, and always stay on top.
<em>Left</em> and <em>right</em> should drop when the window contracts.
</div>
<div class="top-right">right</div>
</div>
<div class="spacer"></div>
<div class="bottom-container">This is the bottom.</div>
</main>
I rearranged your markup and used display: table-cell. Here's the fiddle example http://jsfiddle.net/k108vt58/. Hope this helps and is what you're looking for.
Related
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>
In a container with property display: flex; and a fixed size, is it possible to only shrink child elements which would take more than the space available if we evenly distributed it? It would be trivial knowing the content sizes beforehand but I'm looking for a solution which works for dynamic content without resorting to Javascript.
Here's an example:
.container {
display: flex;
width: 200px;
}
.item {
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Making it prettier -- unrelated to the question */
.container {
background-color: #cde;
padding: 5px;
}
.item {
margin: 0;
}
.item:not(:last-of-type) {
margin-right: 5px;
}
.item:nth-child(1) {
background-color: #dec;
}
.item:nth-child(2) {
background-color: #ced;
}
.item:nth-child(3) {
background-color: #dce;
}
<div class="container">
<p class="item">sm</p>
<p class="item">paragraph with large content</p>
<p class="item">another large content</p>
</div>
We can see that for the first item the letter m has been partly cut out (it shrunk). Given that this item is small enough (I'll properly define this below) I would want to prevent it from shrinking, like this:
The logic I'm aiming for is this: if we distribute the container width evenly between n child elements each one would have childWidth = parentWidth/n; then those that have width (based on its content) less than or equal to this value don't shrink while the other ones do shrink if necessary (it may be the case that the available space allows for all items to be fully displayed).
Is this possible with CSS only?
Ok, I have tested out my comment and it works! Hurray!
This works because it sets the minimum width of the element to the minimum width that can fit the content.
I have set it for only the first item, like you wanted:
.container {
display: flex;
width: 200px;
}
.item {
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Making it prettier -- unrelated to the question */
.container {
background-color: #cde;
padding: 5px;
}
.item {
margin: 0;
}
.item:not(:last-of-type) {
margin-right: 5px;
}
.item:nth-child(1) {
background-color: #dec;
min-width: min-content;
}
.item:nth-child(2) {
background-color: #ced;
}
.item:nth-child(3) {
background-color: #dce;
}
<div class="container">
<p class="item">sm</p>
<p class="item">paragraph with large content</p>
<p class="item">another large content</p>
</div>
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>
This question already has answers here:
Center one and right/left align other flexbox element
(11 answers)
Closed 3 years ago.
according to W3Schools
The align attribute is not supported in HTML5. Use CSS instead.
I am wondering how on earth, can I simply align two paragraphs within one line.
I am open to flex box solutions, but, I do not need a third dummy paragraph to behave as a starting point. Also, producing a row and columns seems to overcomplicating this ?simple? task?
This is what I have come up with so far, thus text-align does nothing...:
p {
display: inline-block;
}
.center {
text-align: center;
}
.right {
text-align: right;
}
<div class="container">
<p class="center">Center</p>
<p class="right">Right</p>
</div>
You can do it with the Flexbox and position property:
.container {
position: relative; /* needs to be on the parent */
display: flex; /* displays flex-items (children) inline */
justify-content: center; /* centers them horizontally */
align-items: center; /* and vertically */
}
.container > .right {
position: absolute; /* needs to be on the child */
right: 0;
}
#media (max-width: 480px) { /* adjust */
.container {
flex-direction: column; /* stacks flex-items vertically */
}
.container > .right {
position: static; /* back to default */
align-self: flex-end; /* aligns to the horizontal end */
}
}
<div class="container">
<p class="center">Center</p>
<p class="right">Right</p>
</div>
p {
display: inline-block;
}
.center {
margin-left:50%;
}
.right {
float: right;
}
If I understood your question. You should add some percentage width to your two paragraphs and then add float:right; to both of them. Also, don't forget to keep the .right paragraph above the .center paragraph in your html.
See the below snippet for reference.
p {
display: inline-block;
}
.center {
text-align: center;
width: 33.3333%;
float: right;
}
.right {
text-align: right;
width: 33.33333%;
float: right;
}
<div class="container">
<p class="right">Right</p>
<p class="center">Center</p>
</div>
Hope this help.
With the litlle description you gave, there is little to answer.
to start:
With flex I would do :
.container {
display:flex;
}
.center {
flex:1;
text-align: center;
}
.right {
text-align: right;
}
<div class="container">
<p class="center">Center</p>
<p class="right">Right</p>
</div>
With the table-layout (IE8 & +) , I would do
.container {
display: table;
width: 100%;
}
p {
display: table-cell;
}
.center {
text-align: center;/* will use whole space avalaible*/
}
.right {
text-align: right;
width: 5em;/* a small value to shrink it on its content */
}
<div class="container">
<p class="center">Center</p>
<p class="right">Right</p>
</div>
with grid, I would do
.container {
display:grid;
grid-template-columns:1fr auto;
}
.center {
text-align: center;
}
.right {
text-align: right;
}
<div class="container">
<p class="center">Center</p>
<p class="right">Right</p>
</div
I would not use float nor inline-block where elements can wrap to the next line any time (unless max-width is set for both elements) ;)
One simple solution:
.container {
position: relative;
text-align: center;
}
.center {
display: inline-block;
}
.right {
display: inline-block;
position: absolute;
top: 0;
right: 0;
}
With position: absolute applied on .right, you can move the paragraph wherever you want within the next parent element that has position: relative (If no parent has position set, the element will relate to the window).
The reason why text-align is not working in your case, is because of display: inline-block.
Some basics you need to know:
A paragraph element is just a form of a container. The text in it is basically another element itself.
text-align aligns the content within an element. Hence, the text gets aligned to the borders of the paragraph element.
The default value for a paragraph element is display: block. That means its width spans over the entire width of its parent element (or the window in case no container is declared). Hence, text-align will have a visible effect.
display: inline-block will make the borders of the element it is applied on shrink to the size of its content. Since text-align affects the content, not the element itself, your paragraph element won't move.
.container {
display:grid;
}
p {
display: inline-block;
}
.center {
text-align: center;
}
.right {
text-align: right;
}
.left {
text-align: left;
}
<div class="container">
<p class="center">Center</p>
<br>
<p class="right">Right</p>
<br>
<p class="left">Left</p>
</div>
Well, here on this fiddle I have two divs aligned with display:flex and flex-grow: https://jsfiddle.net/h7bm23ts/2/
window.longer = function() {
document.getElementsByClassName("child2")[0].innerHTML += "like this and ";
};
* {
font-family: sans-serif;
}
.wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.box1 {
background-color: beige;
height: 300px;
flex-grow: 2;
}
.box2 {
background-color: cyan;
height: 300px;
flex-grow: 1;
}
.child1 {
background-color: green;
}
.child2 {
background-color: gray;
}
<div class="wrapper">
<div class="box1">
some nonsense
</div>
<div class="box2">
longer nonsense <button onclick="window.longer();"> even longer </button>
<div class="child1">
this child should be able to expand this div
</div>
<div class="child2">
this child should wrap when too much content is appended
</div>
</div>
</div>
However, the content in one of the divs is dynamic and when more content is appended onto a child div of it, the parent div expands and makes the boxes wrap, ruining the layout with a few button clicks.
How do I keep the gray div from expanding on more content and make it simply accept its parent's width as "strict" and wrap in it?
EDIT: I settled on https://jsfiddle.net/h7bm23ts/12/.
Try this css to achive what you need:
.wrapper {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.box1 {
background-color: beige;
min-height: 300px;
flex: 2 1 0; /* 2 - flex grow, 1 - flex shrink, 0 - flex basis */
}
.box2 {
background-color: cyan;
min-height: 300px;
flex: 1 0 0;
}
More about this You can find here:
https://css-tricks.com/flex-grow-is-weird/ and here: https://drafts.csswg.org/css-flexbox/#flex-grow-property, and
https://drafts.csswg.org/css-flexbox/#flexibility