A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

[Proposal] Additive background rules

Ferdy_Christant
2019-10-16

This is admittedly a pretty raw idea, but I’m still curious what the community thinks of it.

Currently, the CSS background rule allows for passing in multiple background layers (color, image, gradient) as part of a single declaration.

Problem statement

Imagine a design system. Which is not hard to do as design is increasingly systemic in our industry. Often such design systems make use of flexible CSS utility classes.

Imagine we have 20 classes for colors, making up our brand. We also have 5 texture classes (repeating backgrounds). We apply these single purpose classes to markup to compose UI.

In this particular scenario, we want to be able to apply a background color to an element. Or, to give it a background image (example: noise pattern). And here comes the heart of the matter: we also want to be able to apply both. Example: a noise pattern on a gray background.

<div class="gray noise">
</div>

Edit: I realize this is not the best example as it could be solved using background-color and background-image separately. The issue may still occur though in several other situations such as combining multiple background images, gradients, etc.

This is not a far-fetched scenario, yet currently inefficient to implement. If either utility class uses the background-image property, they would overwrite each other. The only way to solve this currently is to have classes for every possible combination of layers. In this case: 5 x 20 = 100 classes.

We can easily dramatize the problem by introducing utility gradient classes. Now we would require several hundreds of classes.

Idea for a solution

The idea of additive backgrounds is that you can declare a background on an element which will be added to the existing declaration (that may exist elsewhere), if any. I haven’t really thought much about the syntax, so this is just to illustrate the idea:

.gray {
background: #ccc;
}

.noise {
+background: url('noise.gif');
}

When both of the above classes are applied to an element, the end result will be that the background consists of the color and the image background combined.

Notes

I imagine the order of declaration to be a potential source of confusion. In the example shown, one would probably want the image before the color. It may be tricky to reason about by developers.

Finally, should the idea have some merit, it could be considered for other CSS properties that expect multiple values. I have thought of filters and box shadows but don’t really see a practical use case there.

guest271314
2019-10-16

One means of achieving the requirement using CSS is to utilize pseudo elements and mix-blend-mode, and opacity

h1 {
  --text: "Text";
  --width: 300px;
}

h1 {
  width: var(--width);
  mix-blend-mode: normal;
  background: grey;
}

h1:before {
  opacity: .25;
  content: var(--text);
  width: var(--width);
  color: transparent;
  position: absolute;
  background: linear-gradient(to left, rgba(222, 111, 99, 1), rgba(22, 33, 44, 0));
}
birtles
2019-10-21

There have been various proposals around this, the most recent that I am aware of being: https://github.com/w3c/csswg-drafts/issues/1594

It’s slightly different to the proposal here but the rough idea is to add !add at the end of a property declaration similar to !important. Addition then takes place using specificity order.

The rules for addition for many common types are already defined in CSS Values and Units Level 4 since this is needed for animation already.

For list-type properties like background-image, addition means appending to the list so it works for things like adding adding multiple filters, or transform functions etc.

Ferdy_Christant
2019-10-21

Wow, thanks so much. I had no idea about this already being a proposal, funny I accidentally used the same “additive” term. Really liking the !add proposal, very simple and elegant solution.

The proposal does more than I ever hoped for so thanks again, no follow-up needed for this one.

Crissov
2019-11-07

I would solve this use case with a different, more radical approach: backgrounds/backdrops/box-shadows, contents and masks/filters effectively create a multitude of stacked box layers, which, a bit like ::marker that is generated by list-item properties, should be accessible with pseudo-elements.

Relative Box Layers

.noise::background {content: url('noise.gif');}
.gray::background {fill: #ccc;}

wherein new backgrounds are deeper (and new foregrounds are higher) than all existing ones, or

.gray::last-background {fill: #ccc;}
.noise::first-background {content: url('noise.gif');}

Absolute Box Layers

.gray::layer(-2) {fill: #ccc;}
.noise::layer(-1) {content: url('noise.gif');}

wherein negative layers are backgrounds, positive layers are foregrounds, and the main content is in ::layer(0).

By the way, there are also some reasons for introducing ::border pseudo elements which would use SVG/FXTF stroke properties.

::border 
{
    fill: <border-color>;
    stroke-width: <border-width>;
    stroke-color: <border-color>;
    stroke-pattern: <~border-style>;
    content: <border-image>;
}
::border-top,    ::border:top, 
::border-right,  ::border:right, 
::border-bottom, ::border:bottom, 
::border-left,   ::border:left
{
    ...;
}