6

I have a responsive grid layout. There can be any number of columns depending on the window width.

I am trying to make the grid have a checkered pattern, so I use the odd and even selectors to color the grid cells.

But it only works when the number of columns is odd. When the number of columns is even, it becomes a striped pattern.

Is there a CSS property/selector to solve this, or a better way to do it?

Here's the simplified code of my project showing the problem:

.grid {
  display: grid;
  counter-reset: spans;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-gap: 1px;
}

.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
}

.grid > *::after {
  content: counter(spans);
}

/* Coloring */
.grid > *:nth-child(odd) {
  background-color: #789;
}

.grid > *:not(:nth-child(odd)) {
  background-color: #567;
}
<h2>Works when columns are odd</h2>
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<h2>Doesn't work while even</h2>
<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
Boann
  • 48,794
  • 16
  • 117
  • 146
Hao Wu
  • 17,573
  • 6
  • 28
  • 60
  • This won't be possible without JavaScript. You are coloring every second `span` and wonder that you do so. – dmuensterer Aug 19 '19 at 06:25
  • 1
    So what should the "even" variant look like? If it's supposed to be checkered there have to be 2 light grey cells following each other, so each row would have to start with a different pattern? – empiric Aug 19 '19 at 06:31
  • It's a simple mathematics, I guess it's not possible with evens – Abhishek Pandey Aug 19 '19 at 06:31
  • @empiric Seems so, the even variant should be like `0101, 1010, 0101, 1010`, which every 4th and the 5th item have the same color. That's why it's complicated and couldn't be solved with simply `odd` and `even`. Wish there's a selector to do such a job like this. – Hao Wu Aug 19 '19 at 06:39
  • There is a way to solve this is wrapping them into new row then this can be fixed by rows – Abhishek Pandey Aug 19 '19 at 06:40
  • `.evenRow > .oddColumns` & `.oddRow > .evenColumns` – Abhishek Pandey Aug 19 '19 at 06:41
  • @AbhishekPandey That works, but it destroys the dom structure and be less responsive. Also javascript is required :( But it seems like I have no other choices? – Hao Wu Aug 19 '19 at 06:44
  • @HaoWu Let's hope we find any other options, but for now it seems quite impossible, I'm also excited for the working answer :D – Abhishek Pandey Aug 19 '19 at 07:07
  • 1
    @AbhishekPandey I added an *idea* of solution if you are intrested – Temani Afif Aug 19 '19 at 10:25
  • If only there's a selector like `:nth-row` or `:nth-column` for grid layout :/ – Hao Wu Aug 19 '19 at 10:26

1 Answers1

3

If you know the number of rows or you know at least their max value you can easily achieve this using gradient and multiple background. The only drawback is that the coloration will be on the container so you can have empty cells colored too.

.grid {
  display: grid;
  margin:10px 0;
  counter-reset: spans;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: 40px;
  --grad:repeating-linear-gradient(to right,red 0 calc(50% / var(--cols)),blue calc(50% / var(--cols))  calc(100% / var(--cols)));
  background:
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad);
  background-size:200% 40px;
  background-position: 
    0                        calc(0*40px),
    calc(100% / var(--cols)) calc(1*40px),
    0                        calc(2*40px),
    calc(100% / var(--cols)) calc(3*40px),
    0                        calc(4*40px);
  background-repeat:no-repeat;
}

.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
}

.grid > *::after {
  content: counter(spans);
}
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 8;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

To avoid the coloration of the empty cells we can consider a hack using pseudo element but without transparency:

.grid {
  display: grid;
  counter-reset: spans;
  margin:10px 0;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: 40px;
  --grad:repeating-linear-gradient(to right,red 0 calc(50% / var(--cols)),blue calc(50% / var(--cols))  calc(100% / var(--cols)));
  background:
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad);
  background-size:200% 40px;
  background-position: 
    0                        calc(0*40px),
    calc(100% / var(--cols)) calc(1*40px),
    0                        calc(2*40px),
    calc(100% / var(--cols)) calc(3*40px),
    0                        calc(4*40px);
  background-repeat:no-repeat;
  overflow:hidden;
}

.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
  position:relative;
}

.grid > *::after {
  content: counter(spans);
}
.grid > *:last-child::before {
  content:"";
  position:absolute;
  top:0;
  bottom:0;
  left:100%;
  width:100vw;
  background:#fff;
}
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 8;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

To simulate the gaps you can consider outline on the grid items (still without transparency):

.grid {
  display: grid;
  counter-reset: spans;
  margin:10px 0;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: 40px;
  --grad:repeating-linear-gradient(to right,red 0 calc(50% / var(--cols)),blue calc(50% / var(--cols))  calc(100% / var(--cols)));
  background:
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad);
  background-size:200% 40px;
  background-position: 
    0                        calc(0*40px),
    calc(100% / var(--cols)) calc(1*40px),
    0                        calc(2*40px),
    calc(100% / var(--cols)) calc(3*40px),
    0                        calc(4*40px);
  background-repeat:no-repeat;
  overflow:hidden;
}

.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
  position:relative;
  outline:1px solid #fff;
}

.grid > *::after {
  content: counter(spans);
}
.grid > *:last-child::before {
  content:"";
  position:absolute;
  top:0;
  bottom:0;
  left:100%;
  width:100vw;
  background:#fff;
}
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 8;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

If you want to have transparency for the gaps you can consider mask that you define with gradient. You will need an extra wrapper for this:

.grid {
  display: grid;
  counter-reset: spans;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: 40px;
  --grad:repeating-linear-gradient(to right,red 0 calc(50% / var(--cols)),blue calc(50% / var(--cols))  calc(100% / var(--cols)));
  background:
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad);
  background-size:200% 40px;
  background-position: 
    0                        calc(0*40px),
    calc(100% / var(--cols)) calc(1*40px),
    0                        calc(2*40px),
    calc(100% / var(--cols)) calc(3*40px),
    0                        calc(4*40px);
  background-repeat:no-repeat;
  -webkit-mask:
    repeating-linear-gradient(to right,
      transparent 0 1px,#fff 1px calc(100% / var(--cols) - 1px),
      transparent calc(100% / var(--cols) - 1px) calc(100% / var(--cols)))
    center/calc(100% + 2px) 100%;  
  mask:
    repeating-linear-gradient(to right,
      transparent 0 1px,#fff 1px calc(100% / var(--cols) - 1px),
      transparent calc(100% / var(--cols) - 1px) calc(100% / var(--cols)))
    center/calc(100% + 2px) 100%;  
}
.wrapper {
  margin:30px 0;
  -webkit-mask:repeating-linear-gradient(to bottom,
      transparent 0 1px,#fff 1px calc(40px - 1px),
      transparent calc(40px - 1px) 40px)
    center/100% calc(100% + 2px);
  mask:repeating-linear-gradient(to bottom,
      transparent 0 1px,#fff 1px calc(40px - 1px),
      transparent calc(40px - 1px) 40px)
    center/100% calc(100% + 2px);
}

.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
  position:relative;
}

.grid > *::after {
  content: counter(spans);
}


body {
  background:yellow;
}
<div class="wrapper">
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
</div>

<div class="wrapper">
<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
</div>

<div class="wrapper">
<div class="grid" style="--cols: 8;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

</div>

Using mask-composite we don't need an extra layer

.grid {
  display: grid;
  counter-reset: spans;
  margin:30px 0;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: 40px;
  --grad:repeating-linear-gradient(to right,red 0 calc(50% / var(--cols)),blue calc(50% / var(--cols))  calc(100% / var(--cols)));
  background:
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad),
    var(--grad);
  background-size:200% 40px;
  background-position: 
    0                        calc(0*40px),
    calc(100% / var(--cols)) calc(1*40px),
    0                        calc(2*40px),
    calc(100% / var(--cols)) calc(3*40px),
    0                        calc(4*40px);
  background-repeat:no-repeat;
  -webkit-mask:
    repeating-linear-gradient(to right,
      transparent 0 1px,#fff 1px calc(100% / var(--cols) - 1px),
      transparent calc(100% / var(--cols) - 1px) calc(100% / var(--cols)))
    center/calc(100% + 2px) 100%,
    repeating-linear-gradient(to bottom,
      transparent 0 1px,#fff 1px calc(40px - 1px),
      transparent calc(40px - 1px) 40px)
    center/100% calc(100% + 2px);  
  mask:
    repeating-linear-gradient(to right,
      transparent 0 1px,#fff 1px calc(100% / var(--cols) - 1px),
      transparent calc(100% / var(--cols) - 1px) calc(100% / var(--cols)))
    center/calc(100% + 2px) 100%,
    repeating-linear-gradient(to bottom,
      transparent 0 1px,#fff 1px calc(40px - 1px),
      transparent calc(40px - 1px) 40px)
    center/100% calc(100% + 2px);  
    
    -webkit-mask-composite:destination-in;
    mask-composite:intersect;
}


.grid > * {
  counter-increment: spans;
  text-align: center;
  padding: 10px 0;
  color: #fff;
  position:relative;
}

.grid > *::after {
  content: counter(spans);
}


body {
  background:yellow;
}
<div class="grid" style="--cols: 5;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 4;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<div class="grid" style="--cols: 8;">
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Damn this is way complicated than I thought, but it works! Although I also wanted to change other properties for different checkers( `color` or `border` etc.), but it seems it can't be easily done just by css by now. Thank you very much for this solution! You have my respect. – Hao Wu Aug 19 '19 at 10:19
  • @HaoWu we can make the code more complex if you want to also color the text and border, etc but it will become even more complicated that the actual one :) I can try to add it later if you want – Temani Afif Aug 19 '19 at 10:25
  • Maybe I should use something like `[col="4"]:nth-child(4n), [col="4"]:nth-child(4n+1)` etc.. But need to assign for every even numbers :/ using scss loops would be preferable – Hao Wu Aug 19 '19 at 10:29
  • @HaoWu yes you can do this, you will have a lot of selectors but if the maximum number of cols isn't big it should be fine I guess – Temani Afif Aug 19 '19 at 10:31
  • @TemaniAfif everything looks good except the color combination, my eyes are hurting :D – Abhishek Pandey Aug 19 '19 at 11:19
  • 1
    @AbhishekPandey yes, wanted to make sure no one will miss the colors ;) – Temani Afif Aug 19 '19 at 11:25