Single row layout with multine line child which wraps to minimum width - html

Question
Can you produce such layout with CSS?
I want to have a single row with 2 children:
Child A has multiple rows,
Child B has a single one.
I want the the layout in which:
Child A accomodates on a single line as many children as possible for A and B to still fit on a single line.
Child A has the smallest width possible to achieve #1.
Child B then takes all the remaining space.
My HTML:
<div class="Container">
<div class="ChildA">
<input type="text" />
<input type="text" />
<input type="text" />
<input type="text" />
</div>
<div class="ChildB">
<button>
Click me please
</button>
</div>
</div>
My desired layout:
Sketch of a solution using Flexbox
So far I came up with the following CSS using Flexbox:
.Container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
border: 4px solid black;
}
.ChildA {
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
border: 4px solid red;
}
.ChildB {
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
border: 4px solid blue;
}
It produces layout pictured below. The problem is it violates rule #2 - child A does not have the smallest width possible, but instead takes all the remaining space, which I want to be taken by child B instead.
CSS Grid?
It seems the solution may be possible using CSS Grid (and it's auto-fit value), but I haven't been able to produce a fully working one myself yet. The closes I've come is that pen, which works fine on some window widths, but I cannot work out how to remove max-width: 50vw constraint.
Note
I want a fully dynamic/flexible solution, which would work with content of any size in the containers.
Children content in my example are inputs and button, but that's just a random example - they can be <div>s or any other elements, if it makes the layout easier to implement.
Fiddle
https://codepen.io/anon/pen/mjdmXG?editors=1100

Related

How to align the children of two flex divs? [duplicate]

This question already has answers here:
Targeting flex items on the last or specific row
(10 answers)
Closed 2 years ago.
I'm creating a nav menu using flex. I want all of the items in my menu to display in a single row when the screen is wide enough to support that, and to snap to two rows of items when it needs to wrap. I have this mostly working:
.content {
width: 400px;
height: 150px;
border: thin solid black;
}
.outer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.inner {
display: flex;
justify-content: space-around;
flex-grow: 1;
}
span {
font-size: 24pt;
}
<div class="content">
<div class="outer">
<div class="inner">
<span>one</span>
<span>two</span>
<span>three</span>
</div>
<div class="inner">
<span>four</span>
<span>five</span>
<span>six</span>
</div>
</div>
</div>
CodePen here.
This works perfectly when the page is wide enough:
And it works mostly perfectly when the page is narrow (try changing the width of .content to 250px):
However, now I'm trying to make it so the items in each row line up with each other. I'm going for something like this:
I've tried every combination of flex-grow, flex-shrink, and justify-content that I can think of, but I can't get the items to align.
I know I could probably use a media query and swap out the content for a grid when the window gets too narrow, but I'd like to simplify this as much as possible. Is there a way to align the children of two flex divs?
Alternatively, is there a way to use a grid layout that shows as 1 row until it needs to wrap, and then it shows as 2 rows?
It causes by span width.
if span width not fixed, span will have dynamic width;
set width on span;
Try this
Add to te span
span {
flex: 33%;
}
Or change the porcent acording to the amount of items the div has

Force flex item to span full row width

I'm trying to retain the first 2 child elements on the same row while the third element is in its own below at full width, all while using flex.
I'm particularly interested in using the flex-grow and flex-shrink properties on the first 2 elements (which is one of my reasons for not using percentages) however the third element really must be full width and below the first two.
The label element is added programmatically after the text element when there's an error and I can't change the code.
How do I force the label element to span a 100% width below the other two elements which are positioned using flex?
.parent {
display: flex;
align-items: center;
width: 100%;
background-color: #ececec;
}
.parent * {
width: 100%;
}
.parent #text {
min-width: 75px;
flex-shrink: 2.25;
}
<div class="parent">
<input type="range" id="range">
<input type="text" id="text">
<label class="error">Error message</label>
</div>
When you want a flex item to occupy an entire row, set it to width: 100% or flex-basis: 100%, and enable wrap on the container.
The item now consumes all available space. Siblings are forced on to other rows.
.parent {
display: flex;
flex-wrap: wrap;
}
#range, #text {
flex: 1;
}
.error {
flex: 0 0 100%; /* flex-grow, flex-shrink, flex-basis */
border: 1px dashed black;
}
<div class="parent">
<input type="range" id="range">
<input type="text" id="text">
<label class="error">Error message (takes full width)</label>
</div>
More info: The initial value of the flex-wrap property is nowrap, which means that all items will line up in a row. MDN
Depends if it's
Flex-direction:row (it's by default row) / on parent container
Try using justify-self: stretch / on the child that you want full width
Flex-direction-column / on a parent container
-Try using align-self: stretch / on the child that you want full width
Hopefully will help someone in some situations, usually helps me

Make flexbox fill up all space [duplicate]

This question already has answers here:
How to Create Grid/Tile View? [duplicate]
(8 answers)
Closed 6 years ago.
I have boxes in which I'm using flexbox for their layout. Flexbox makes the rows 'organized'. Meaning, if 1 box's height is larger than all the others, all the boxes on the second row get pushed down, and there is space under the boxes first row that have a smaller height.
Here's an image of what I mean:
There's space under box #01 because box #2 has a larger height. I want box #4 to go right under box #1.
How can I make a all boxes to fill up space right above them?
JSFiddle
#wrapper {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
width: 400px;
}
.tile {
background-color: lightblue;
border: 1px solid black;
height: 100px;
width: 100px;
margin: 10px;
}
#n2 {
height: 200px;
}
<div id="wrapper">
<div class="tile" id="n1">01</div>
<div class="tile" id="n2">02</div>
<div class="tile" id="n3">03</div>
<div class="tile" id="n4">04</div>
<div class="tile" id="n5">05</div>
</div>
You can achieve this with flex-direction: column along with flex-wrap: wrap, but you won't be able to preserve the order of the elements.
A JavaScript library like Masonry might be worth looking into.
Like Darryl says, you want to do this with flex-flow: column wrap; and a fixed height on the parent element. This page on CSS-Tricks is invaluable to understanding the syntax, but basically changing the flex-direction flips it sideways. You can specify the order of children by setting the order: XX on the child tiles.

Combine auto, fixed, and percentage width in same HTML table row

I would like to create a table that has 4 columns. 2 of the columns in the table are labels and should auto-size to the widest element. One of the other columns is fixed width and the last should fill the remaining space. My first attempt to create this table looks like this:
HTML
<table style="width:100%">
<tr>
<td class="lbl">lbl:</td> <td class="fluidContent">Expandable Content 1</td>
<td class="lbl">longer label:</td> <td class="fixedContent">Me Fixed</td>
</tr>
<tr>
<td class="lbl">longer label:</td> <td class="fluidContent">Expandable Content 2</td>
<td class="lbl">lbl:</td> <td class="fixedContent">Me Fixed</td>
</tr>
</table>
CSS
table {
white-space: nowrap;
}
td {
border: 1px solid orange;
}
.lbl {
text-align: right;
font-style: italic;
width: auto;
}
.fluidContent {
width: calc(100% - 120px);
}
.fixedContent {
width: 120px;
}
https://jsfiddle.net/vf5p0m82/
Unfortunately this markup yields label columns with fluid width. The "lbl" class cells are treated as though they had a width of 27% specified with the "fluidContent" cells having a width of 46%. What I want is for the "lbl" cells to be as small as possible while still fitting the largest string they contain.
Is there any way to do this how I want simply with tables or is it time to employ the new CSS3 flex-boxes?
Edit: A Solution
Whilst adding another example to this question for clarity I stumbled upon a hack to make it work. If you just give the auto-sized columns a width of 1px everything works out. See here:
https://jsfiddle.net/vf5p0m82/2/
Is there an established/canonical way to do this or is this it?
There might be a more official way to do this, but the thing I've found works is to just set the cell width to 1px. The content size overrides it, so it will just collapse to content size.
.lbl {
text-align: right;
font-style: italic;
width: 1px;
}
By the way, the calc seems to be overridden by table formatting, but it doesn't seem like you need it from what you described.
See it in action: https://jsfiddle.net/vf5p0m82/1/
Ok a flexbox solution: https://jsfiddle.net/vf5p0m82/4/
HTML
<div class="fTbl">
<div class="fluidWrapper">
<div class="shrinkCol">
<div class="lbl">lbl:</div>
<div class="lbl">long lbl:</div>
</div>
<div class="stretchCol">
<div class="fluidCell">Expand Content</div>
<div class="fluidCell">Expand Content</div>
</div>
</div>
<div class="fixedWrapper">
<div class="shrinkCol">
<div class="lbl">longer label:</div>
<div class="lbl">lbl:</div>
</div>
<div class="shrinkCol">
<div class="fixCell" style="width:100px;">Fixed</div>
<div class="fixCell" style="width:100px;">Fixed</div>
</div>
</div>
</div>
CSS
.fTbl {
border: solid magenta 2px;
display: flex;
flex-flow: row wrap;
width: 100%;
align-items: flex-start;
}
.fixedWrapper {
display: flex;
flex: 0 0 auto;
flex-flow: row nowrap;
}
.fluidWrapper {
display: flex;
flex: 1 1 auto;
flex-flow: row nowrap;
}
.shrinkCol {
display: flex;
flex: 0 0 auto;
flex-flow: column nowrap;
}
.stretchCol {
display: flex;
flex: 1 1 auto;
flex-flow: column nowrap;
}
.fluidCell {
border: 2px solid orange;
min-width: 140px;
}
.fixCell {
border: 2px solid orange;
}
.lbl {
text-align: right;
font-style: italic;
border: 2px solid orange;
}
Ok, so here is how you use this CSS generally:
Tables must be divided into separate columns and each column filled independently row by row.
Rows of separate columns must be explicitly given equal heights if they contain items of different heights.
Label/content column pairs are placed in the fixed/fluidWrapper classes so that the labels and content (usually text boxes) wrap together and don't get separated.
It is not possible to have a "colspan" within 1 flex table without decoupling the rows from one another and explicitly setting widths.
I like this approach because it is easy to have the flex items stretch and compress vertically and horizontally to fill a page and wrapping items to respond to narrow screens is fairly simple. It also doesn't require hacky CSS like the table does. I find that HTML tables can be quite cumbersome and unintuitive at times.
I dislike this approach because it is impossible (or perhaps just difficult) to make a row of the table span multiple columns. If there is a wider item it must be placed in a new container and will therefore not share its label width with the items above it. However, it seems that these complaints arise from inherent differences in the table and flex box design objectives and that you must simply choose which approach best suits a given layout.
A search for generating automatic table column widths approximately equal to those of the widest column in any row comes here to this question. If that is what you want to do, try th,td {width: max-content;} in your CSS. Works for me.

How to display wrapping flex items as space-between with last row aligned left?

I have a fixed-width container into which several variable-width elements must appear in a row, which can spill over into additional rows as necessary.
However, the beginning of each element must be aligned with the one on top of it, so in ASCII art it would look like so (say, padding of 1):
/----------------------------------\
| |
| # One # Two # Three # Four |
| # Five # Six |
| |
\----------------------------------/
In other words:
The first element of every row must be left-aligned
The last element of every row (except for the final row) must be right-aligned
Every element must be left-aligned to the element above it
I'm trying to use flexbox for this without success.
This is the best I've come so far, using flex-wrap: wrap for the container and flex-grow: 1 for the elements.
Problem is that the last row fills out to the edge.
justify-content: flex-start; // this does nothing
If I take away flow-grow: 1 then the elements aren't distributed equally. I also tried fiddling around with last-of-type on the elements but it's also not enough.
Is this even possible with flexbox, or am I going about it the wrong way?
After trying the suggestions here (thanks!) and searching the web long and wide, I've reached the conclusion that this is simply not possible with flexbox. Any by that I mean that others have reached this conclusion while I stubbornly tried to make it work anyway, until finally giving up and accepting the wisdom of wiser people.
There are a couple of links I came across that might explain it better, or different aspects of the requirements, so I'm posting them here for... posterity.
How to keep wrapped flex-items the same width as the elements on the previous row?
http://fourkitchens.com/blog/article/responsive-multi-column-lists-flexbox
There is no easy way to do this with flexbox. But if you are willing to sacrifice IE then you can do it with css grid, add this to the container:
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
And if you want to have some space between the elements then add
grid-gap: 10px;
I know I am kind of late, but I have been looking for solution for this in past hour or so, and I think I sort of figured it out. Put empty div on the end of your container, and set flex-grow: 1 to it, and nothing else. Also set justify-content: space-between on container, and don't use flex-grow on other items. This will always make last line align left because this div will stretch through all remaining space.
However the problem of this is that it ALWAYS makes last line align left - even if it is the only line, which makes this solution unusable for me, but it might be usable for someone who can expect more than one line of items.
If the width of your items is fixed, you can add several empty divs to the end of your list of items:
<div class="item">meaningful content</div>
<div class="item">meaningful content</div>
<div class="item">meaningful content</div>
<div class="empty-div"></div>
<div class="empty-div"></div>
<div class="empty-div"></div>
and then:
.item, .empty-div{ width: 150px; } // make them the same width
Works perfectly well.
I was able to achieve the desired result with a combination of positive and negative margins.
If each element in the container defines a margin to create a space between them:
.container .element {
flex: 1;
margin: 0px 5px;
}
recover the pixels from the edges of each row in the container with a negative margin of the same amount:
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0px -5px;
}
This should result in 10px between each element in the row with the first and last of each row at the edge of the container.
One solution that will work in many cases is simply applying padding to the items. Then you can use flex-start, and get spacing in between the cards.
For example
.container {
display: flex;
justify-content: center;
}
.parent {
width: 420px;
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
}
.child {
flex: 0 30%;
min-width: 100px;
padding-left: 10px;
margin-bottom: 10px;
}
.child-content {
background-color: yellow;
border: 1px solid black;
height: 100px;
}
<div class="container">
<div class="parent">
<div class="child">
<div class="child-content">
Box
</div>
</div>
<div class="child">
<div class="child-content">
Box
</div>
</div>
<div class="child">
<div class="child-content">
Box
</div>
</div>
<div class="child">
<div class="child-content">
Box
</div>
</div>
<div class="child">
<div class="child-content">
Box
</div>
</div>
</div>
</div>
CSS Flex 4 columns always starting at left. I cant see wy this is impossible? If the columns should be equal in with, this working for us using calc() and relative units:
/* parent */
.ua-flex {
display: flex;
flex-wrap: wrap;
justify-content: start;
}
/* children */
.ua-flex > * {
flex: 0 0 calc(25% - 1em);
margin: 1em 0 0 0;
}
.ua-flex > :not(:nth-child(4n+4)) {
margin-right: 1.333333em;
}
Im I missing something here? Its all abouth math, the subtracted calc() in this case 1em, gives 3 space of gap 1.333em with margin-right on 3 of 4 columns, and 0.5em subtracted calc() should give 0.666em gap with margin-right on 3 of 4 columns.
Hope this can be useful...
I just stumbled across the same problem and came up with another solution. I can't decide whether it feels kind of dirty or elegant, but decide for yourself.
Add as many empty divs as your maximum number of items per row to the container, assign them the same class as row items but remove any margin or padding from them (basically anything which gives them a height). That'll result in the row behaving as expected because after the last row item, there'll always be enough invisible "spacers" to pad the row. Those being wrapped to the next row have no height, so they shouldn't affect the rest of your page.
Example here:
https://jsfiddle.net/amknwjmj/
.products {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.product {
/* set top and bottom margin only, as left and right
will be handled by space-between */
margin: 0.25rem 0;
/* account your desired margin two times and substract
it from the base width of the row item */
flex-basis: calc(25% - (0.25rem * 2));
}
.product.spacer {
/* remove all properties increasing the element height
from the spacers to avoid them being visible */
margin: 0;
padding: 0;
height: initial;
}
/* start demo styles (purely eye-candy not required for this to work) */
* {
box-sizing: border-box;
font-family: sans-serif;
}
.products {
padding: .25rem .5rem;
background: powderblue;
}
.product {
height: 75px;
line-height: 75px;
padding: .25rem;
text-align: center;
background: steelblue;
color: #fff;
}
.product.spacer {
background: none;
/* of course, spacers should NOT have a border
but they are helpful to illstrate what's going on. */
border: 1px dashed gray;
}
/* end demo styles */
<div class="products">
<article id="product-1" class="product">P1</article>
<article id="product-2" class="product">P2</article>
<article id="product-3" class="product">P3</article>
<article id="product-4" class="product">P4</article>
<article id="product-5" class="product">P5</article>
<div class="product spacer"></div>
<div class="product spacer"></div>
<div class="product spacer"></div>
<div class="product spacer"></div>
</div>
so, you have a container and some stuff inside?
<div class="container">
<span>msg1</span>
<span>msg1</span>
<span>msg1</span>
<span>msg1</span>
<span>msg1</span>
</div>
This is how it works, when you declare display: flex for your container all of its direct children will become flex too, with some default config (align-items:strech).
I guess you already got that, so, maybe you can try using justify-content: space-between which will align your items in the row leaving them equally spaced plus flex-basis: 25% (to certify that there will be always 4 items in a row, change de % as you wish) that is supposed to work for all your lines except the last one. For the last one you can use a css selector (like last-child) and set its property to flex-grow: 0 / flex-shrink:0 (solving one of your problems, if you used flex: 1 1 25% instead of flex-basis) and also align-self: flex-start or whatever you like
You could try it with a fixed-with-pseudo-element:
.container {
display:flex;
justify-content:space-between;
}
.container:after {
content: '';
flex-grow: 0;
width: 25%;
}
.container .element {
width: 25%;
}
You can specify margin-right on every item except the last one in the row of flex-wrap by doing the following:
.item:not(:nth-child(4n)) {
margin-right: 20px;
}
I was facing the same issue and in fact it's really simple. No need to put some
SCSS and or jQuery.
You just need to specify a maximum number of "square", and make a modulo to know if that match with your maxNumber. If its not, you just have to define a quick function that increment your number until that modulo return 0.
When you have your number you just have to loop your html.
For me I was coding on ReactNative:
const RenderInvisibleSquare = (nb) => {
let tab = [];
let i = 1;
for (; (nb + i) % 3 !== 0; i++) {}
for (let n = 0; n < i; n++) {
tab.push(<View style={{ width: 120, height: 120, backgroundColor: 'red' }}/>);
}
return tab;
};
<ScrollView>
{
data.map(item => (
<View>
<View style={{ marginVertical: 5 }}>
<Text style={{ textAlign: 'center', color: '#7E8BF5' }}>{item.title}</Text>
</View>
<View style={{ flex: 1, flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-around', flexDirection: 'row' }}>
{
item.data.map(elem => (
<View style={{ width: 120, height: 120, marginBottom: 10, backgroundColor: 'red' }} />
))
}
{ item.data.length % 3 !== 0 && RenderInvisibleSquare(item.data.length)}
</View>
</View>
))
}
</ScrollView>
If you dont want content (backgroundColor: 'red') in my case you juste have to make it 'transparent'.