Keep wrapped flex items on left while others centred - css

I'm trying to display a list of flex items with fixed width in the center of flexbox with wrap by using margin: auto. When wrap happens, the wrapped item also centers in its own container:
Is there a way to keep the wrapped item on the left while everything else is centred?
.parent {
display: flex;
flex-wrap: wrap;
height: 100px;
}
.children {
flex: 1;
border: 1px solid black;
max-width: 300px;
min-width: 300px;
margin: auto;
height: 100px;
margin-top: 1rem;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>

You're saying you want the items centered, but when there is only one item that wraps, you want it left-aligned.
The problem is that there is really no left-alignment in the flex container. Everything is centered, based on available space in the row. So the single item in the last row has no concept of what's going on above, and nothing to align with.
Here's what happens if you left-align the last item (on wider screens):
.parent {
display: flex;
flex-wrap: wrap;
}
.children {
border: 1px solid black;
max-width: 300px;
min-width: 300px;
margin: auto;
height: 100px;
margin-top: 1rem;
}
.children:last-child {
margin-left: 0;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>
What you need is a nested grid structure.
A top-level grid to establish the centering.
A nested grid for the wrapping.
In the demo below, you'll find a three-column grid. The left and right columns are empty (spacer) items, created with CSS pseudo-elements, and set to equal widths. This centers the middle item.
The middle item is also a grid container. Using the repeat() and auto-fill functions, the items can wrap, with individual items aligning left.
body {
display: grid;
grid-template-columns: auto 1fr auto;
}
body::before,
body::after {
content: ''; /* in grid (and flex) layouts, pseudo-elements on the container
are treated as items */
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: 100px;
grid-gap: 10px;
justify-content: center; /* centers the columns (not the items, like in flex) */
}
.children {
border: 1px solid black;
}
<div class="parent">
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
<div class="children"></div>
</div>
jsFiddle demo

Related

Full-height flex item with height set to 0

I have the following code as working on development with flexbox.
#container {
display: flex;
justify-content: space-around;
width: 100%;
}
.content {
border: 1px solid black;
display: flex;
flex-direction: column;
height: 400px;
width: 400px;
}
#item1 {
background-color: red;
flex-grow: 1;
height: 0;
}
#item2 {
background-color: green;
flex-grow: 1;
height: 100px;
}
#item3 {
background-color: blue;
flex-grow: 1;
height: 900px;
}
<div id="container">
<div class="content">
<div id="item1"></div>
</div>
<div class="content">
<div id="item2"></div>
</div>
<div class="content">
<div id="item3"></div>
</div>
</div>
I know that setting flex-grow: 1 would take the remaining space of its parent. However, the property height seems to have no effect whatever its value is.
Reason being your flex-direction is set to column, which mean the flex-grow reacts from top to bottom, so the flex-grow responding to the height instead of width.
another question is, why flex-direction is column, but width is filled up, because it is a <div> displayed as block, the width is auto filled by display: block;
you are using flex-grow that’s why. have a look on this https://www.w3schools.com/cssref/css3_pr_flex-grow.asp
https://stackoverflow.com/a/64748435/1095913 (down here) is right, solution is: flex-grow: 0;
Here's another reference https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow

Centering grid content while left-aligning each row's content, with arbitrary item/column widths

I have a row of items of arbitrary width. They are centered within the container (note white space on the left and right sides of the red container):
Sometimes the container gets smaller than the width of all items:
When this happens, I want the items in the end to wrap to the next row like this:
It is very imporant for me that each row's content must be left-aligned, but the grid as a whole must be centered:
Initially, I tried implementing it with FlexBox. After a lot of frustration and hair pulling, I've learned that this is impossible witn FlexBox: https://stackoverflow.com/a/32811002/901944
Another answer on the same page suggests using CSS grid instead of flexbox.
CSS grid produces a slightly different result, but that also suits me:
Here's the code that makes it work:
.red-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
justify-content: center;
}
This code contains a lot of keywords that I don't understand: grid-template-columns, repeat, auto-fit, minmax and max-content. I tried reading up on them and failed. None of guides and API docs explicitly explain how this particualr combination works. MDN docs are way too short and cryptic.
What I specifically struggle with is this 210px magic number. Why is it necessary? (Erm, I know it's necessary because how the spec is designed, but this does not help me understand.)
The sizes of items in my grid are arbitrary, so I can't use a fixed value. Also, setting this fixed value makes the result slightly off: small items grow and large items overflow the container.
What I essentially want is:
grid-template-columns: repeat(auto-fit, minmax(min-content, max-content));
but that rule is recognized by browsers as faulty.
I've stumbled upon this answer that explains that using both min-content and max-content together is forbidden by the spec in this context. The answer's suggested solution is... to use Flexbox!
The loop has closed. I'm back to where I started, expect that I'm now lacking hair on my head for another round.
How to do I center my grid while left-aligning each row's content, with items having arbitrary widths?
Here's a boilerplate to fiddle with for your convenience: https://jsbin.com/vuguhoj/edit?html,css,output
The container can be resized by dragging it by the bottom-right corner.
PS No display: inline and float: left please.
.page {
border: 1px solid black;
overflow: hidden;
resize: horizontal;
max-width: 500px;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max-content, 50px));
justify-content: center;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">
Foofoofoo
</div>
<div class="item">
Bar
</div>
<div class="item">
BazBaz
</div>
<div class="item">
QuuxQuuxQuux
</div>
</div>
</div>
CSS grid approach-
root answer - joe82
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(179px, max-content));
grid-gap: 10px;
justify-content: center;
background: #999;
overflow: hidden;
padding: 10px;
}
.background {
width: 179px;
height: 64px;
background: #99d9ea;
}
.background .child {
border: 2px solid #000;
}
.background:nth-child(1) .child {
width: 110px;
height: 50px;
}
.background:nth-child(2) .child {
width: 120px;
height: 60px;
}
.background:nth-child(3) .child {
width: 50px;
height: 55px;
}
.background:nth-child(4) .child {
width: 175px;
height: 40px;
}
<div class="container">
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
<div class="background"><div class="child"></div></div>
</div>
Newbie here!
I admit that the above one isn't the output you desired, but it is my best attempt towards it, with the use of CSS grids. Here, you can understand that if we want to make it responsive then a minimum width is required after which, the column(content) will get carried to the next line, and that width is defined here(grid-template-columns: repeat(auto-fit, minmax(204px, max-content));) 204px. But because of it each column will take that much width at least, that's why I represented the actual dimension of a column with blue background and the actual content within the border.I just post it for your acknowledgment and approach so that you can get closer to the actual answer.
By the way, Flex Approach-
root idea - random COSMOS
.container {
min-width: 130px;
max-width: 340px;
overflow: auto;
background: #999;
padding: 10px 40px;
resize: horizontal;
border: 1px solid #000;
}
.main-content {
display: flex;
flex-wrap: wrap;
background: #fff;
max-width: 340px;
overflow: hidden;
}
.child {
margin: 5px;
padding: 5px;
background: #99d9ea;
border: 1px solid black;
}
<div class="container">
<div class="main-content">
<div class="child">Foofoofoo</div>
<div class="child">Bar</div>
<div class="child">BazBaz</div>
<div class="child">QuuxQuuxQuux</div>
</div>
</div>
Resize the window or the above div to see the results
The above tells that the div is centered and the content is at left but not resizing according to content.
My personal opinion -
You should use #media for making it responsize, just as the way you want it to be, It is just like coding a lot for a simple output but it can give you the best and satisfying results out of your hard work and time!
Kindly inform me if you want me to make it responsize for you, I mean just like a demo-
Regard,
Om Chaudhary
If we go for center we don't get the left-align thing:
.page {
border: 1px solid black;
max-width: 500px;
resize: horizontal;
overflow: hidden;
display: flex;
justify-content: center;
padding: 0 50px;
}
.grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
Now with left-align (as in #Michael Benjamin answer) it doesn't do the center stuff:
.page {
border: 1px solid black;
max-width: 500px;
resize: horizontal;
overflow: hidden;
display: flex;
justify-content: center;
padding: 0 50px;
}
.grid {
display: flex;
flex-wrap: wrap;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
Why?
Because in the second code and in #Michael Benjamin code the .grid is technically centered. Visual:
See the whole div is centered. The problem is that the .grid div doesn't change its width according to the content inside it. But, according to the width of its parent div (.page).
I know it doesnt solve your problem, but I am just trying to make sure that now you understand the main problem. So, maybe you can find the solution in another way.
For this case, I see the only solution is to use JavaScript.
In this code we get each .item width. Then we set .flex width = total .item width. If the total .item width is smaller then the .page width - we set .flex width = ((total .item width) - (last .item width)). I hope the JS is pretty readable. If you need more explanation - can give it in comments.
Note that this is not wary flexible and universal solution, But it works good in this particular case, because it was written for it.
This snippet you can test only on browser window size change. Better to check in using Snippet Full page and Chrome console with Devise toolbar mode. Or here https://jsfiddle.net/focusstyle/sh6dnLvt/1/
window.addEventListener("resize", function() {
flexWidth();
});
function flexWidth() {
let page = document.querySelector('.page');
let flex = document.querySelector('.flex');
let totalWidth = biggesWidth = itemWidth = lastWidth = 0;
let flexItem = document.querySelectorAll('.item');
for (let i = 0; i < flexItem.length; i += 1) {
itemWidth = flexItem[i].offsetWidth;
if (biggesWidth<itemWidth) {
biggesWidth = itemWidth;
}
biggesWidth = flexItem[i].offsetWidth;
totalWidth += itemWidth;
lastWidth = itemWidth;
}
if (totalWidth > page.clientWidth) {
totalWidth = totalWidth - lastWidth;
}
totalWidth += 1;
flex.style.cssText = "min-width: "+biggesWidth+"px; max-width: "+totalWidth+"px;";
}
.page {
border: 1px solid black;
overflow: hidden;
text-align: center;
}
.flex {
display: inline-flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.item {
border: 1px solid black;
}
<div class="page">
<div class="flex">
<div class="item">
Foofoofoo
</div>
<div class="item">
Bar
</div>
<div class="item">
BazBaz
</div>
<div class="item">
QuuxQuuxQuux
</div>
</div>
</div>
Let's understand the 210px first. When you write the below code:
grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
The browser knows when to wrap the items around. It ensures that your grid items will always be wider than 210px or at least equal to 210px.
If the browser has 420px width available, it will put 2 items in a row.
If the browser has 630px width available, it will put 3 items in a row, and so on...
You can learn about CSS grids here
If you still don't want to have a min-content of 210px, you can always wite media queries in CSS.
Another thing that may suit your requirements is giving a min-width and max-width to your grid-items.
Hopefully, it saves some of your hair.
You have two containers available – .page and .grid.
This enables you to distribute the two tasks – centering and left-alignment.
Use the top-level container for centering.
Use the nested container for wrapping and left alignment.
Here's a code concept:
.page {
display: grid;
grid-template-columns: 50px 1fr 50px;
border: 1px solid black;
max-width: 500px;
}
.grid {
grid-column: 2;
justify-self: center;
display: flex;
flex-wrap: wrap;
}
.item {
border: 1px solid black;
margin: 1px;
}
<div class="page">
<div class="grid">
<div class="item">Foofoofoo</div>
<div class="item">Bar</div>
<div class="item">BazBaz</div>
<div class="item">QuuxQuuxQuux</div>
</div>
</div>
jsFiddle demo
I don't know if I'm missing something but I think this is possible to achieve this with flexboxes.
You have to get this structure :
<div class="container">
<div class="row">
<div class="item item-1"></div>
<div class="item item-2"></div>
<div class="item item-3"></div>
<div class="item item-4"></div>
</div>
</div>
where the row has a max size and margin: 0 auto (to be centered)
Here is my example on Codepen

Why does space-around allow flex items to overflow on the left side?

It seems that Chrome doesn't handle justify-content: space-around correctly when the content overflows the flex container, and the container is not set up to allow wrapping, but instead horizontal scrolling.
Some of the content overflows on the left side of the flex container, and is cut off. I want it to overflow on the right side, so that I can reach it by scrolling horizontally.
Here is an example:
#container {
display: flex;
width: 500px;
justify-content: space-around;
border: solid black;
overflow: auto;
}
.item {
min-width: 200px;
margin: 10px;
background-color: red;
display: table;
font-size: 48pt;
text-align: center;
}
<div id="container">
<div class="item">1</div><div class="item">2</div>
<div class="item">3</div><div class="item">4</div>
<div class="item">5</div><div class="item">6</div>
</div>
That's because when there isn't enough space, space-around behaves like center:
If the leftover free-space is negative or there is only a single flex
item on the line, this value is identical to center.
And center behaves like you describe:
If the leftover free-space is negative, the flex items will overflow
equally in both directions.
Instead, you can use space-between:
If the leftover free-space is negative or there is only a single flex
item on the line, this value is identical to flex-start.
Of course, then you won't have half-size spaces on neither end of the flex line. You can insert pseudo-element to have full-size spaces, though.
#container {
display: flex;
justify-content: space-between; /* Instead of space-around */
}
#container::before, #container::after {
content: ''; /* Insert space before the first item and after the last one */
}
.container {
display: flex;
width: 500px;
border: solid black;
justify-content: space-between;
overflow: auto;
margin: 10px 0;
}
.container::before, .container::after {
content: '';
}
.item {
margin: 10px;
background-color: red;
display: table;
font-size: 48pt;
text-align: center;
}
.big > .item {
min-width: 200px;
}
<div class="container big">
<div class="item">1</div><div class="item">2</div>
<div class="item">3</div><div class="item">4</div>
<div class="item">5</div><div class="item">6</div>
</div>
<div class="container">
<div class="item">1</div><div class="item">2</div>
<div class="item">3</div><div class="item">4</div>
<div class="item">5</div><div class="item">6</div>
</div>
Since the container is limited in width and you want overflowing flex items to be accessed via horizontal scrolling, why use justify-content: space-around?
Try justify-content: flex-start:
Revised Codepen
To understand why overflowing flex items may be inaccessible via scroll, see this answer.
If you're interested in a Javascript workaround for the original code, see this post:
When centering horizontally, li's get cut off

How to justify a single flexbox item (override justify-content) [duplicate]

This question already has answers here:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
(6 answers)
Closed 1 year ago.
You can override align-items with align-self for a flex item.
I am looking for a way to override justify-content for a flex item.
If you had a flexbox container with justify-content:flex-end, but you want the first item to be justify-content: flex-start, how could that be done?
There doesn't seem to be justify-self, but you can achieve similar result setting appropriate margin to auto¹. E. g. for flex-direction: row (default) you should set margin-right: auto to align the child to the left.
.container {
height: 100px;
border: solid 10px skyblue;
display: flex;
justify-content: flex-end;
}
.block {
width: 50px;
background: tomato;
}
.justify-start {
margin-right: auto;
}
<div class="container">
<div class="block justify-start">justify-start</div>
<div class="block"></div>
</div>
¹ This behaviour is defined by the Flexbox spec.
AFAIK there is no property for that in the specs, but here is a trick I’ve been using:
set the container element ( the one with display:flex ) to justify-content:space-around
Then add an extra element between the first and second item and set it to flex-grow:10 (or some other value that works with your setup)
Edit: if the items are tightly aligned it's a good idea to add flex-shrink: 10; to the extra element as well, so the layout will be properly responsive on smaller devices.
If you aren't actually restricted to keeping all of these elements as sibling nodes you can wrap the ones that go together in another default flex box, and have the container of both use space-between.
.space-between {
border: 1px solid red;
display: flex;
justify-content: space-between;
}
.default-flex {
border: 1px solid blue;
display: flex;
}
.child {
width: 100px;
height: 100px;
border: 1px solid;
}
<div class="space-between">
<div class="child">1</div>
<div class="default-flex">
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
</div>
</div>
Or if you were doing the same thing with flex-start and flex-end reversed you just swap the order of the default-flex container and lone child.
I solved a similar case by setting the inner item's style to margin: 0 auto.
Situation: My menu usually contains three buttons, in which case they need to be justify-content: space-between. But when there's only one button, it will now be center aligned instead of to the left.
For those situations where width of the items you do want to flex-end is known, you can set their flex to "0 0 ##px" and set the item you want to flex-start with flex:1
This will cause the pseudo flex-start item to fill the container, just format it to text-align:left or whatever.
To expand on Pavlo's answer https://stackoverflow.com/a/34063808/1069914, you can have multiple child items justify-content: flex-start in their behavior but have the last item justify-content: flex-end
.container {
height: 100px;
border: solid 10px skyblue;
display: flex;
justify-content: flex-end;
}
.container > *:not(:last-child) {
margin-right: 0;
margin-left: 0;
}
/* set the second to last-child */
.container > :nth-last-child(2) {
margin-right: auto;
margin-left: 0;
}
.block {
width: 50px;
background: tomato;
border: 1px solid black;
}
<div class="container">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block" style="width:150px">I should be at the end of the flex container (i.e. justify-content: flex-end)</div>
</div>

multiple divs, some left, some right, all vertically aligned with CSS

What would be the best way, given any number of elements in a container, to align some elements to the left and some to the right while centering both left and right content vertically?
For example, given this markup:
<div class="action">
<div class="message">This is our message</div>
<div class="comment">Comments for the message</div>
<div class="person">John Doe</div>
<div class="date">01/18/2013</div>
<div class="time">12:35 PM</div>
</div>
Can the message and comment be left aligned in the action container while the person, date and time are right aligned with both left and right content vertically centered? Can this be done without new markup and with any content length for each element?
Thanks.
Some CSS would do the trick. Have the containing div position: relative, and float the children div's either left or right. Also center-align the text.
.container{
position: relative;
}
.left{
float: left;
width: 50%;
background-color: Silver;
text-align: center;
}
.right{
float: right;
width: 50%;
background-color: Yellow;
text-align: center;
}
Fiddle Example
With a minimal change to existing markup (introduction of two div tags, one for each column), this becomes rather trivial if you use Munawwar's flex-helper.
HTML:
<div class="hbox flex">
<div class="left vbox main-center">
<div class="message">
<p>This is our message.</p>
<p>It spans many lines.</p>
<p>Or rather, paragraphs.</p>
<p>Additional waffle.</p>
<p>Syrup, bacon, a banana.</p>
<p>Tall glass of milk.</p>
</div>
<div class="comment">Comments for the message.</div>
</div>
<div class="right vbox main-center">
<div class="person">John Doe</div>
<div class="date">01/18/2013</div>
<div class="time">12:35 PM</div>
</div>
</div>
CSS:
/*Example-specific CSS*/
/*left column*/
.left {
width: 80%;
background-color: Silver;
text-align: center;
}
/*right column*/
.right {
width: 20%;
background-color: Yellow;
text-align: center;
}
/*Minimal use of Munawwar's flex-helper*/
/* https://github.com/Munawwar/flex-helper */
/*Stack child items vertically*/
.vbox {
display: flex;
/*Align children vetically*/
flex-direction: column;
align-content: flex-start;
/*Prevent extending beyond boundaries*/
overflow: hidden;
}
/*Stack child items horizontally*/
.hbox {
display: flex;
/*Align children horizontally*/
flex-direction: row;
align-content: flex-start;
/*Wrap items to next line on main-axis*/
flex-wrap: wrap;
/*Prevent extending beyond boundaries*/
overflow: hidden;
}
/*Stretch item along parent's main-axis*/
.flex {
flex: 1;
}
/*Stack child items to the main-axis center*/
.main-center {
justify-content: center;
}
JSFiddle demo
No waffles were harmed in the production of this answer, though some may have been eaten.

Resources