CSS style first element depending on number of siblings - css

I receive an dynamic amount of items from the backend.
In case it's an odd number of items the first should be the only one in the first row.
All others should be displayed two per each row. Same if it's an even number of items - no single item in any row then.
I strongly think there is some way to do this with CSS only (i would easily solve this with JS but would prefer a CSS approach) i just hadn't been able to find the right combination.
HTML:
<div class="row">
<div class="col">A</div>
<div class="col">B</div>
<div class="col">C</div>
<div class="col">D</div>
</div>
CSS:
.row {
display: flex;
flex-wrap: wrap;
}
.col {
flex: 0 0 50%;
background: gray;
text-align: center;
}
.col:first-child { // should only hit for an odd amount of items
flex: 0 0 100%;
}
Codepen

Yes, this is possible with the following CSS:
.col:first-child:nth-last-child(odd) {
flex: 0 0 100%;
}
.row {
display: flex;
flex-wrap: wrap;
}
.col {
flex: 0 0 50%;
background: gray;
text-align: center;
}
.col:first-child:nth-last-child(odd) {
flex: 0 0 100%; // should only hit for an odd amount of items
}
<p>Even</p>
<div class="row">
<div class="col">A</div>
<div class="col">B</div>
<div class="col">C</div>
<div class="col">D</div>
</div>
<p>Odd</p>
<div class="row">
<div class="col">A</div>
<div class="col">B</div>
<div class="col">C</div>
<div class="col">D</div>
<div class="col">E</div>
</div>
Also se this related post: Can CSS detect the number of children an element has?

Related

Centering the horizontal on the column right so that the text is in the middle of the screen

<div className={css.title} >
<div className={css.row}>
<div className={css.columnLeft}>
<div className={css.header}>Images</div>
</div>
<div className={css.columnRight}>
<strong>Image 1 of 2</strong>
</div>
</div>
<Icon />
</div>
css
.row {
width: 100%;
display: flex;
align-items: center;
}
.columnLeft {
flex: 1 0 25%;
text-align: left;
}
.columnRight {
flex: 1 0 75%;
}
Currently does
I would like it to be aligned horizontally more so in between left and center.
To keep "Image 1 of 2" (center column) in the middle of the screen:
give left and right 1 columns flex: 1 1 50%
give center column flex: 0 0 auto
This will make the left and right columns start flex calculation from a base of 50% (which gives them equal weight, regardless of unequal content width). The center column will push back against both left and right columns and take an equal amount of empty space from each, to render its contents 2.
If you want the center column to start wrapping at a particular width, give it a max-width and text-align: center.
That's about it.
<div className={css.title}>
<div className={css.row}>
<div className={css.columnLeft}>
<div className={css.header}>Images</div>
</div>
<div className={css.columnCenter}>
<strong>Image 1 of 2</strong>
</div>
<div className={css.columnRight}>
<Icon />
</div>
</div>
</div>
CSS:
.row {
display: flex;
}
.columnLeft,
.columnRight {
flex: 1 1 50%;
}
.columnCenter {
flex: 0 0 auto;
display: flex;
justify-content: center;
}
.columnRight {
display: flex;
flex-direction: row-reverse;
}
HTML demo:
.row {
display: flex;
justify-content: center;
}
.columnLeft, .columnRight {
flex: 0 1 50%;
border: 1px solid red;
}
.columnCenter {
flex: 0 0 auto;
display: flex;
justify-content: center;
}
.columnRight {
display: flex;
flex-direction: row-reverse;
}
.middle {
width: 1px;
height: 2rem;
background-color: red;
}
<div class="row">
<div class="columnLeft">
<div class="header">More content on left side</div>
</div>
<div class="columnCenter">
<strong>I am centered</strong>
</div>
<div class="columnRight">Icon</div>
</div>
<div class="row">
<div class="middle" />
</div>
Notes:
1 - I gave middle flex element columnCenter class and placed a .columnRight wrapper around the <Icon />, because in your example the center element had the class of columnRight, which was confusing (e.g: improper naming), IMHO.
2 - Keep in mind flex is flexible (hence the name). If one of the columns has significantly more content than the other, the equal pushing of center column against the sides won't be so equal anymore. Sides only concede an equal amount of space as long as they have more room than needed to render their contents. If you want to keep the center text centered no matter what, place max-width on the side columns.

2 columns layout in css (ordering items)

Is there any way to show 2 columns layout with this html code using only css? (column1 class should be in first column, and column2 class in the second one)
<div class="container">
<div class="column1">1a</div>
<div class="column2">1b</div>
<div class="column2">2b<br>zz</div>
<div class="column1">2a</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column2">5b</div>
<div class="column1">3a</div>
<div class="column1">4a</div>
</div>
The result I expect:
1a 1b
2a 2b
3a zz
4a 3b
4b
5b
float seems the best option here if you manage the Block formatting context
example :
* {
box-sizing: border-box;
}
div div {
box-shadow: inset 0 0 0 1px;/* see me */
width: 50%;
line-height: 1.4em; /* because of that 2 lines element , to hide the gap */
background: lightblue;/* see me too */
}
.column1:first-child {
float: left; /* let another float stand aside */
}
.column2 {
float: right;
clear: right;/* pile us to the far right */
}
div div:nth-child(odd) {
background: lightgreen;/* see me different */
}
<div class="container">
<div class="column1">1a</div>
<div class="column2">1b</div>
<div class="column2">2b<br>zz</div>
<div class="column1">2a</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column2">5b</div>
<div class="column1">3a</div>
<div class="column1">4a</div>
</div>
It matches your expected layout here, but will it also with real content ?
One way you can do this would be to float the classes of .column1 and .column2 like this:
.container {
width: 100%;
}
.column1 {
float: left;
width: 50%;
}
.column2 {
float: right;
width: 50%;
}
<div class="container">
<div class="column1">1a</div>
<div class="column2">1a</div>
<div class="column1">2b</div>
<div class="column1">3a</div>
<div class="column2">2b</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column1">4a</div>
<div class="column1">5a</div>
</div>
You could also maybe simplify this by having only two inner divs representing each column like:
<div class="container">
<div class="column1">
...
</div>
<div class="column2">
...
</div>
</div>
You can also achieve the same thing using minimal code via using flex. Just add following code to your CSS.
.container {
display: flex;
flex-wrap: wrap;
}
.container div {
width: 50%;
}
Explanation :- You add display type flex to the container, so all the elements will be arranged next to each other inside this div.
Now when you give width of 50% to internal divs and give flex-wrap: wrap to the container, it ensures that only 2 divs are next to each other since there is no space left to accommodate more.
Alternatively if you want the items closer to each other you can reduce the width of the outer container.
.container {
display: flex;
flex-wrap: wrap;
width: 10%;
}
.container div {
width: 50%;
}
This would be closer to what you are expecting.

flex not respected by Input why?

I've noticed the 'input' element does not stretch or shrink to fill a flex container. Does anyone know why and if there is a solution?
My example below shows the container class c being used on Div elements (which stretch properly). And a single class e, used for the right-justified fixed-length end. The row of divs stretch and shrink as expected, the row of inputs do not.
div.c {
display: flex;
flex-direction: row;
align-content: stretch;
}
div.d {
flex: 1;
}
div.e {
display: inline-block;
flex: 0 0 30px;
background-color: red;
}
<div class='c'>
<div class='d'>A</div>
<div class='d'>B</div>
<div class='d'>C</div>
<div class='d'>D</div>
<div class='e'>E</div>
</div>
<div class='c'>
<input class='d'></input>
<input class='d'></input>
<input class='d'></input>
<input class='d'></input>
<div class='e'></div>
</div>
note i am aware of this link:
input / button elements not respecting flex-grow
However using min-width: 0; box-sizing: border-box; has no effect for me.
Because you specified div.d not input.d or div .d or just .d. Also, no need to close the input tag.
div.c {
display: flex;
flex-direction: row;
align-content: stretch;
}
.d {
flex: 1;
}
div.e {
display: inline-block;
flex: 0 0 30px;
background-color: red;
}
<div class='c'>
<div class='d'>A</div>
<div class='d'>B</div>
<div class='d'>C</div>
<div class='d'>D</div>
<div class='e'>E</div>
</div>
<div class='c'>
<input class='d'>
<input class='d'>
<input class='d'>
<input class='d'>
<div class='e'></div>
</div>

Is there a way of writing CSS that lets you re-organize the order of how HTML components appear?

I am working on a responsive site in which the mobile/tablet view differs from the desktop view in the way it re-orders the DIVs.
Is there a way to write maintainable CSS that let's you re-organize the order of how HTML DIVs appear?
For example, the code below controls the order of how DIVs would appear on a desktop device:
<div class="container">
<div class="row1">
<div class="col1A">Sample content</div>
<div class="col2A">Sample content</div>
<div class="col3A">Sample content</div>
</div>
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
</div>
However, for mobile/tablet view, I want to display the DIVs in different order using CSS, like the example below:
Show row2, col2B
Then row1, col1A
Then row1, col3A
Then row2, col1B
Is this possible using CSS ?
As a proof-of-concept, you can use the flex CSS property to reorder how elements are visually rendered.
In your example, I had to keep the child elements within a single container
and then I could control the order using the order property.
If you want to hide some items on the small screen view, use display: none on the specific items.
Note: For a wide screen, you would need some CSS rules to get the items to look like two rows. (Please specify what you need.)
If you combine this with media queries, you can get a workable solution.
.container {
display: flex;
flex: center;
flex-direction: column;
align-items: center;
border: 1px dotted blue;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
width: auto;
}
.col1A {
order: 2;
}
.col2A {
display: none;
}
.col3A {
order: 3;
}
.col1B {
order: 4;
}
.col2B {
order: 1;
}
.col3B {
display: none;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
If you want to simulate two rows of three elements, you can still use flex with some adjustments. The following may be helpful.
.container {
display: flex;
flex: center;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
border: 1px dotted blue;
padding: 20px 0;
}
.container .row1 {
margin-bottom: 20px;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
flex-basis: calc(33% - 20px);
}
.col1B {
background-color: yellow;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
Set a screen size for mobile device detection in the css and add the following
#media screen and (max-width: SIZE) {
.row2{
display: flex; flex-flow: column;
}
.col1B{
order: 1;
}
.col2B{
order: 2;
}
.col3B{
order: 3;
}
}
And then add the classes to the DIVs
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
Change order: 1/2/3; to your needs.

Switching from 3 columns to 2

I have a responsive layout set up for 3 columns. In each column is a product that we are trying to sell. Sometimes there could be 3 items, 2 items and 1 item. Currently when I remove 1 item it will display on 2/3 of the page and leave the 1/3 empty. How do I create it so that when there are 2 or 1 item being displayed for them to be centered and they would have a max-width of 640px?
/* -------- HTML ------------*/
<div class="item">Item1</div>
<div class="item">Item1</div>
<div class="item">Item1</div>
/* -------- CSS ------------*/
.item {
display: inline;
float: left;
width: 33.33%;
}
With flexbox, the container will fill 100% of the space based on how many items you have.
HTML
<div class="flex-row">
<div class="item">Item1</div>
<div class="item">Item2</div>
<div class="item">Item3</div>
</div>
<div class="flex-row">
<div class="item">Item1</div>
<div class="item">Item2</div>
</div>
<div class="flex-row">
<div class="item">Item1</div>
</div>
CSS
.flex-row {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.item {
flex: 1;
}
Here is an example
A flex box would better suit your need. Remove/add item to see how the columns adapt to your new content:
.container{
display: flex;
}
.item{
flex-grow: 1;
background-color: red;
border: 1px solid black;
}
<div class="container">
<div class="item">Item1</div>
<div class="item">Item2</div>
<div class="item">Item3</div>
</div>

Resources