CSS Flex columns layout with overflow - css

Using pure CSS, is it possible to make the data flow in columns (like flex-direction: column) and also overflow the entire column layout? Like here:
Examples seem to focus on specifying the number of columns upfront, or, using media queries to achieve this - but with the caveat that it hinges on page width and not on the size of the content (so you have to set the content to be e.g. 33% width when 3 columns).

I think your best option is columns
.container {
background:lightblue;
padding:10px;
column-count:3;
column-gap:10px;
}
#media (max-width:800px) {
.container {column-count:2}
}
#media (max-width:400px) {
.container {column-count:1}
}
.container div {
padding:10px;
margin:5px 0;
display:inline-block;
width:100%;
box-sizing:border-box;
background:grey;
}
/* irrelevant CSS*/
.container {
counter-reset:num;
}
.container div:before {
content:counter(num);
counter-increment:num;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
Like below if you want to control the width:
.container {
background:lightblue;
padding:10px;
column-width:max(200px,100vw/4);
column-gap:10px;
}
.container div {
padding:10px;
margin:5px 0;
display:inline-block;
width:100%;
box-sizing:border-box;
background:grey;
}
/* irrelevant CSS*/
.container {
counter-reset:num;
}
.container div:before {
content:counter(num);
counter-increment:num;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
The max(200px,100vw/4) will get you a maximum number of columns equal to 3 and the 200px will control when we fall to 2 columns then 1.

Related

Responsive checkerboard grid in CSS?

This snippet contains a responsive grid. As you change the screen width the number of columns varies automatically. Sometimes there is an even number of columns, sometimes there is an odd number. When there is an odd number of columns, the cells alternate colours like a checkerboard, but when there is an even number of columns, they do not. Is there any way to achieve the checkerboard effect for even numbers of columns as well as odd numbers? Does it require Javascript, or can it be done using CSS alone?
body {
margin: 0;
}
.checkers {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));
padding: 1em;
gap: 1em;
}
.checkers>div {
background-color: red;
aspect-ratio: 1/1;
}
.checkers>div:nth-child(even) {
background-color: blue;
}
<div class="checkers">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
If you can omit the use of 1fr and consider fixed width elements, you can do this using some background trick:
.checkers {
display: grid;
grid-template-columns: repeat(auto-fit, 8em);
padding: 1em;
gap: 1em;
position: relative; /* Relative on the main container */
z-index:0;
}
.checkers > div {
aspect-ratio: 1;
clip-path: inset(0); /* clip the pseudo element to element*/
}
.checkers > div:before {
content:"";
position: absolute;
z-index:-1;
inset: 1em; /* same as padding */
/* create a checkboard pattern using gradient
18em = (8em + 1em[gap])*2
*/
background: repeating-conic-gradient(red 0 25%,blue 0 50%) 0 0/18em 18em;
}
body {
margin: 0;
background: pink;
counter-reset: num;
}
.checkers div:after {
content: counter(num);
counter-increment: num;
color:#fff;
font-weight:900;
font-size: 25px;
}
<div class="checkers">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
As far as I can tell, this cannot be done for an even number of columns. What you can do is ensure there is always an odd number of columns. You will need to explicitly set an odd number of columns for grid-template-columns, which can be adjusted with media queries.
In this snippet I have borrowed the breakpoints from Bootstrap as a starting point. I have initially set the grid to have 3 columns minimum (grid-template-columns: repeat(3, 1fr);), then increment by 2 (next odd number) at each breakpoint up to 13 columns:
body {
margin: 0;
}
.checkers {
display: grid;
grid-template-columns: repeat(3, 1fr);
padding: 1em;
gap: 1em;
overflow: hidden;
}
.checkers>div {
background-color: red;
aspect-ratio: 1/1;
}
.checkers>div:nth-child(even) {
background-color: blue;
}
#media (min-width: 576px) {
.checkers {
grid-template-columns: repeat(5, 1fr);
}
}
#media (min-width: 768px) {
.checkers {
grid-template-columns: repeat(7, 1fr);
}
}
#media (min-width: 992px) {
.checkers {
grid-template-columns: repeat(9, 1fr);
}
}
#media (min-width: 1200px) {
.checkers {
grid-template-columns: repeat(11, 1fr);
}
}
#media (min-width: 1400px) {
.checkers {
grid-template-columns: repeat(13, 1fr);
}
}
<div class="checkers">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
You can adjust the breakpoint sizes to be multiples of your grid square sizes to get it dialed in to your liking.

How do you (dynamically) justify cells to the right with CSS Grid?

I have a 2 col 2 row grid with 3 of the items/cells occupied.
I would like it to appear like:
A
B
C
I know I can use grid-column: 2; on the 3rd item but that doesn't really help me if I have a dynamic number of rows and items.
I'm also aware of direction: rtl; on the wrapping element, but this obviously reverses the order which is not idea, eg:
B
A
C
I've tried justify-content: right/end/flex-end but no luck there either.
I am aware I can achieve the above with flex-box but was just curious if there was a way with CSS grid?
Since you only have two columns you can write a specific selector and target the last item
.box {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 5px;
border: 1px solid;
margin: 10px;
}
.box div {
height: 50px;
background: red;
}
/* place the last item at last column */
.box div:last-child {
grid-column-end: -1;
}
<div class="box">
<div></div>
<div></div>
</div>
<div class="box">
<div></div>
<div></div>
<div></div>
</div>
<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="box">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>

Elements with rounded corners - how to detect specific elements to give them a special style?

I have kind of a strange layout - the boxes should have rounded corners as if they were one big element (see image with 4 examples). Problem is the boxes are made dynamically so the rows and columns can vary. And so the fun starts. I started with giving the first and the last box a rounded corner, after this counting (nth) - but I can't wrap my head around how to do this with different rows. Tried everything like "tnh-last-child(3)" (does not work if last "row" has only 2 boxes) or "nth-child(3n+1)" but then there is a problem when i have more than 2 "rows" (I mean there are no "rows" [would be great] - only columns). Any idea?
// First and last
&:first-of-type {
border-top-left-radius: 30px;
}
&:last-of-type {
border-bottom-right-radius: 30px;
}
&:nth-of-type(3) {
border-top-right-radius: 30px;
}
Here is a fiddle: https://codepen.io/herrfischer/pen/eYEyRQp
section {
display: flex;
flex-wrap: wrap;
justify-content: left;
width: 400px;
}
section div {
width: 30%;
margin: 5px;
height: 100px;
background-color: grey;
}
section div:first-of-type {
border-top-left-radius: 30px;
}
section div:last-of-type {
border-bottom-right-radius: 30px;
}
section div:nth-of-type(3) {
border-top-right-radius: 30px;
}
.red {
background-color: red;
}
<h1>Red box should always have a rounded corner in the bottom left.</h1>
<h2>Example A</h2>
<section class="a">
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
<div></div>
</section>
<h2>Example B</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
</section>
<h2>Example C</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
</section>
<h2>Example D</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
<div></div>
</section>
You can combine nth-child selectors. This will match with only one item.
&:nth-child(3n + 1):nth-last-child(3),
&:nth-child(3n + 1):nth-last-child(2),
&:nth-child(3n + 1):last-child {
border-bottom-left-radius: 30px;
}
section {
display: flex;
flex-wrap: wrap;
justify-content: left;
width: 400px;
}
section div {
width: 30%;
margin: 5px;
height: 100px;
background-color: grey;
}
section div:first-child {
border-top-left-radius: 30px;
}
section div:last-child {
border-bottom-right-radius: 30px;
}
section div:nth-child(3) {
border-top-right-radius: 30px;
}
section div:nth-child(3n+1):nth-last-child(3),
section div:nth-child(3n+1):nth-last-child(2),
section div:nth-child(3n+1):last-child {
border-bottom-left-radius: 30px;
}
.red {
background-color: red;
}
<h1>Red box should always have a rounded corner in the bottom left.</h1>
<h2>Example A</h2>
<section class="a">
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
<div></div>
</section>
<h2>Example B</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
</section>
<h2>Example C</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
</section>
<h2>Example D</h2>
<section class="b">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="red"></div>
<div></div>
<div></div>
</section>

CSS: How to style last grid row of a dynamic number of items?

I have my first row as 22px and using grid-auto-rows to make the subsequent rows 70px. Is there a way to make the last row also 22px without using template since I won't know how many div's are in it at the time?
.cont {
background: grey;
height: 600px;
display: grid;
grid-template-rows: 22px;
grid-auto-rows: 70px;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 3px;
}
.cont div {
background: red;
}
<div class="cont">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
If you will always have 3 columns keep the template row auto and define the height on the elements. All should have 70px except the first 3 and the last 3 under certain conditions.
.cont {
background: grey;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 3px;
margin:10px;
}
.cont div {
background: red;
height:70px;
}
.cont div:nth-child(1), /* 1st */
.cont div:nth-child(2), /* 2nd */
.cont div:nth-child(3), /* 3rd */
.cont div:nth-last-child(1), /* last one */
.cont div:nth-last-child(2):not(:nth-child(3n + 3)), /* before the last if not the last one of a row*/
.cont div:nth-last-child(3):nth-child(3n + 1){ /* before the two last only if the fisrt one of a row*/
height:22px;
}
<div class="cont">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="cont">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="cont">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
If you know how many columns your layout is going to have (in my example, it has 3 like yours), you can use the :nth-child() and :last-child() psuedo-class CSS selectors and do something like this:
.cont > div:nth-child(3n+1):nth-last-child(-n+3),
.cont > div:nth-child(3n+1):nth-last-child(-n+3) ~ div {
height:22px;
}
This CSS will always target the individual divs in the last row of your 3 column grid.
Good article on this technique here: https://keithclark.co.uk/articles/targeting-first-and-last-rows-in-css-grid-layouts/

Add border depending on last elements

I have a parent div#main that contains unknown number of divs in rows. Every row has 3 divs that are display: inline-block. Now, because of that, the last row can contain 1, 2 or 3 divs, depending on their number.
If the last row has only 1 div, then I want do add border-left and border-right to it.
If the last row has 2 divs, then I want do add border-right to the first div in that row, or border-left to the second div.
And if the last row has 3 divs, then I want to add border-left and border-right to to the second div (the middle one).
(You'll get the full picture when you look at the snipper, or the fiddle)
I managed to solve this issue by using JS, but I'm looking for a pure CSS solution, if there is one.
$('.main').each(function(){
var div_length = $(this).find('div').length;
if((div_length % 3) === 0){
div_last_items = div_length;
}
else if((div_length % 3) === 1){
div_last_items = div_length - 1;
$(this).find('div:nth-last-child(1)').addClass('active-borders');
}
else if((div_length % 3) === 2){
div_last_items = div_length - 2;
$(this).find('div:nth-last-child(2)').addClass('active-border');
}
$(this).find('div:lt('+div_last_items+')').each(function(i){
i=i+2;
if(i % 3 === 0){
$(this).addClass('active-borders')
}
});
});
.main {
width: 360px;
text-align: center;
}
.main>div {
display:inline-block;
vertical-align:top;
width: 100px;
height: 100px;
background:red;
margin-top: 10px;
margin-bottom: 10px;
}
.main>div:nth-child(3n+2) {
margin-left: 20px;
margin-right: 20px;
}
.active-borders{
border-left: 5px solid black;
border-right: 5px solid black;
}
.active-border{
border-right: 5px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
JSFiddle
I never thought this would be possible with pure CSS but it is. All credits would go to this answer for showing an idea on how this could be achieved. This answer is based on that but I am drafting a separate answer because the selectors are a bit different here and I wanted to explain them.
Selector Additions:
div > div:nth-child(3n) + div:nth-last-child(1) {
border-left: 5px solid black;
border-right: 5px solid black;
}
div > div:nth-child(3n+1) + div:nth-last-child(1) {
border-left: 5px solid black;
}
div > div:nth-child(3n+1) + div:nth-last-child(2) {
border-left: 5px solid black;
border-right: 5px solid black;
}
Explanation:
div > div:nth-child(3n) + div:nth-last-child(1)
Select the last child of the parent div when it immediately follows the 3nth child. If the last child immediately follows the 3nth child then it would obviously be the only item in the last row.
div > div:nth-child(3n+1) + div:nth-last-child(1)
Select the last child of the parent div when it immediately follows the 3n+1th child. If the last child immediately follows the 3n+1th child then it means that the last row has 2 items.
div > div:nth-child(3n+1) + div:nth-last-child(2)
Select the second last child of the parent div when it immediately follows the 3n+1th child. If the second last child immediately follows the 3n+1th child then it means that the last row has 3 items.
We cannot use the selector div > div:nth-child(3n+2) + div:nth-last-child(1) for the case where the last row has 3 items because we need the middle element to be styled and not the last,
.main {
width: 360px;
text-align: center;
}
.main>div {
display: inline-block;
vertical-align: top;
width: 100px;
height: 100px;
background: red;
margin-top: 10px;
margin-bottom: 10px;
}
.main>div:nth-child(3n+2) {
margin-left: 20px;
margin-right: 20px;
}
div > div:nth-child(3n) + div:nth-last-child(1) {
border-left: 5px solid black;
border-right: 5px solid black;
}
div > div:nth-child(3n+1) + div:nth-last-child(1) {
border-left: 5px solid black;
}
div > div:nth-child(3n+1) + div:nth-last-child(2) {
border-left: 5px solid black;
border-right: 5px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
The selectors in the above snippet adds a border-left to the second div in the last row if it has only 2 items. If you need to apply border-right to the first div in the last row when it has only 2 items, you can make use of the below selector:
div > div:nth-child(3n+1):nth-last-child(2)
This means, select the second last child of the parent div when it also happens to be the 3n+1th div. If this selector is matched, it implies that the last row has two items.
.main {
width: 360px;
text-align: center;
}
.main>div {
display: inline-block;
vertical-align: top;
width: 100px;
height: 100px;
background: red;
margin-top: 10px;
margin-bottom: 10px;
}
.main>div:nth-child(3n+2) {
margin-left: 20px;
margin-right: 20px;
}
div > div:nth-child(3n) + div:nth-last-child(1) {
border-left: 5px solid black;
border-right: 5px solid black;
}
/*div > div:nth-child(3n+1) + div:nth-last-child(1) {
border-left: 5px solid black;
}*/
div > div:nth-child(3n+1):nth-last-child(2){
border-right: 5px solid black;
}
div > div:nth-child(3n+1) + div:nth-last-child(2) {
border-left: 5px solid black;
border-right: 5px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<hr>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
In general adding border changes border-box dimension of the element. That change of geometry by itself may move element to the next row. Chicken-egg problem.
Thus you cannot do that by CSS means. And even with JS you should be careful - you may get not you want in some circumstances.

Resources