2

How can I make it so that when --color is set, it overrides the checkerboard background?

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  /* from https://stackoverflow.com/a/51054396/65387 */
  background-image: /* tint image */
  linear-gradient(to right, rgba(204, 204, 204, 0.9), rgba(204, 204, 204, 0.9)), /* checkered effect */
  linear-gradient(to right, black 50%, white 50%), linear-gradient(to bottom, black 50%, white 50%);
  background-blend-mode: normal, difference, normal;
  background-size: 25px 25px;
}

.a {
  background-color: var(--color);
}

.b {
  background: var(--color);
}
<div>
  <div class="cell a">a1</div>
  <div class="cell a" style="--color:red;">a2</div>
</div>
<div>
  <div class="cell b">b1</div>
  <div class="cell b" style="--color:red;">b2</div>
</div>

a2 doesn't override the background like I want.

b2 does, but then b1 becomes white.

Is there a way to do this without JavaScript?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • `b1` becomes white because `--color` isn't defined in the stylesheet. If you added `:root { --color: red; }` to your CSS then both `b1` and `b2` would be red. – Tanner Dolby Jan 10 '21 at 06:42
  • 1
    @TannerDolby Right... that's the whole question. I only want it to fallback when it's not defined. – mpen Jan 10 '21 at 06:49
  • Got it. Hmm let me think here. `@supports` won't work because we don't care if the browser supports CSS custom properties, we're only concerned with the custom property being defined or not. – Tanner Dolby Jan 10 '21 at 06:51

3 Answers3

3

You can use var() second parameter that works if the variable isn't defined.

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  /* from https://stackoverflow.com/a/51054396/65387 */
  
  background-blend-mode: normal, difference, normal;
  background-size: 25px 25px;
}



.b {
  background-image:/* tint image */
  linear-gradient(to right, var(--color,rgba(204, 204, 204, 0.9)), var(--color,rgba(204, 204, 204, 0.9))), /* checkered effect */
  linear-gradient(to right, var(--color,black 50%),var(--color,white 50%)),     linear-gradient(to bottom, var(--color,black 50%), var(--color,white 50%));
}
<div>
  <div class="cell b">b1</div>
  <div class="cell b" style="--color:red;">b2</div>
</div>
ATP
  • 2,939
  • 4
  • 13
  • 34
2

Use an extra layer like below:

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  
  background: 
    linear-gradient(var(--color,transparent) 0 0), /* the extra layer */
    linear-gradient(rgba(204, 204, 204, 0.9) 0 0),
    linear-gradient(to right,  black 50%, white 0), 
    linear-gradient(to bottom, black 50%, white 0);
  background-blend-mode:normal, normal, difference, normal;
  background-size: 25px 25px;
}
<div class="cell">a1</div>
<div class="cell" style="--color:red;">a2</div>
<div class="cell" style="--color:blue;">b2</div>

You can also scale the solution to consider image too:

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  
  background: 
    var(--image,linear-gradient(transparent 0 0)), /* the extra image layer */
    linear-gradient(var(--color,transparent) 0 0), /* the extra color layer */
    linear-gradient(rgba(204, 204, 204, 0.9) 0 0),
    linear-gradient(to right,  black 50%, white 0) 0 0/25px 25px, 
    linear-gradient(to bottom, black 50%, white 0) 0 0/25px 25px;
  background-blend-mode:normal, normal ,normal, difference, normal;
}
<div class="cell">a1</div>
<div class="cell" style="--color:red;">a2</div>
<div class="cell" style="--image:linear-gradient(green,blue);">b2</div>
<div class="cell" style="--image:url(https://picsum.photos/id/1/200/200) center/cover;">b3</div>
<div class="cell" style="--image:url(https://picsum.photos/id/18/50/50)  center no-repeat;--color:yellow">b4</div>

An optimized version with conic-gradient and without blend-mode

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  
  background: 
    var(--image,linear-gradient(transparent 0 0)), /* the extra image layer */
    linear-gradient(var(--color,transparent) 0 0), /* the extra color layer */
    linear-gradient(rgba(204, 204, 204, 0.9) 0 0),
    repeating-conic-gradient(#fff 0 90deg,#000 0 180deg) 0 0/25px 25px;
}
<div class="cell">a1</div>
<div class="cell" style="--color:red;">a2</div>
<div class="cell" style="--image:linear-gradient(green,blue);">b2</div>
<div class="cell" style="--image:url(https://picsum.photos/id/1/200/200) center/cover;">b3</div>
<div class="cell" style="--image:url(https://picsum.photos/id/18/50/50)  center no-repeat;--color:yellow">b4</div>

Another idea using pseudo element:

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  position:relative;
  z-index:0;
  background: 
    linear-gradient(rgba(204, 204, 204, 0.9) 0 0),
    repeating-conic-gradient(#fff 0 90deg,#000 0 180deg) 0 0/25px 25px;
}
.cell::before,
.cell::after{
  content:"";
  position:absolute;
  z-index:-1;
  inset:0;
}
.cell::before {
  background:var(--color);
}
.cell::after {
  background:var(--image);
}
<div class="cell">a1</div>
<div class="cell" style="--color:red;">a2</div>
<div class="cell" style="--image:linear-gradient(green,blue);">b2</div>
<div class="cell" style="--image:url(https://picsum.photos/id/1/200/200) center/cover;">b3</div>
<div class="cell" style="--image:url(https://picsum.photos/id/18/50/50)  center no-repeat;--color:yellow">b4</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
1

Without using JavaScript, I think the only way to provide a custom fallback if --color isn't defined is by using a fallback value.

Custom fallback values are just a backup for the browser which supports CSS Custom Properties to choose a different value if the given variable isn't defined or has an invalid value.

.a {
  background-image: var(--color, var(--checkerboard)); /* css var for linear-gradients */
  background-color: var(--color);
}

So if the custom property --color isn't defined, the browser will use the fallback value. In this case, using a combination of background-image and background-color seems to do the trick.

It works by testing the background-image property to see if --color is defined. If it's not, then keep the checkerboard design by using the linear-gradient combination as the fallback value.

:root {
  --checkerboard: /* tint image */
  linear-gradient(to right, rgba(204, 204, 204, 0.9), rgba(204, 204, 204, 0.9)), /* checkered effect */
  linear-gradient(to right, black 50%, white 50%), linear-gradient(to bottom, black 50%, white 50%);
}

.cell {
  display: inline-block;
  width: 100px;
  height: 100px;
  /* from https://stackoverflow.com/a/51054396/65387 */
  background-image: var(--checkerboard); /* can be removed but then .a or .b will need to be defined */
  background-blend-mode: normal, difference, normal;
  background-size: 25px 25px;
}

.a {
  background-image: var(--color, var(--checkerboard));
  background-color: var(--color);
}

.b {
  background-image: var(--color, var(--checkerboard));
  background-color: var(--color);
}
<div>
  <div class="cell a">a1</div>
  <div class="cell a" style="--color: red;">a2</div>
</div>
<div>
  <div class="cell b">b1</div>
  <div class="cell b" style="--color: red;">b2</div>
</div>
Tanner Dolby
  • 4,253
  • 3
  • 10
  • 21