Search code examples
htmlcsscss-selectorspseudo-elementreusability

How to write modular CSS as far as pseudo-elements are concerned?


Let's say that by design all elements should look like tiles of a given color and with some shadow; something like this:

.panel {
  background-color: #bbffff;
  padding: 1em;
  box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
}
<div class="panel">
Some text
</div>

At this point, thinking about writing modular CSS, I'd be already tempted to pick the box-shadow declaration and put it in its own ruleset,

.down-right-box-shadow {
  box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
}

so that I can reuse it by adding class="down-right-box-shadow" to an HTML tag without bringing in also background-color and padding declarations.

Now assume that, for some reason, the CSS uses pseudo-elements to create some effects close to those tiles, something like this:

.panel {
  background-color: #bbffff;
  padding: 1em;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 0.4em;
}

.down-right-box-shadow {
  box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
}

.panel::after {
  content: "";
  width: 1em;
  height: 1em;
  border-radius: 100%;
  margin-top: 0.2em;
  background-color: blue;
  box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
}
<div class="panel down-right-box-shadow">
Some text
</div>

Clearly the intent is that the ::after pseudo-element has the same shadow as everything else on the page, but since it's a pseudo-element, I can't remove the box-shadow declaration from within it in favour of adding class="down-right-box-shadow" in the HTML, because there's no element in the HTML for it.

So I have to rewrite the same declaration for .down-right-box-shadow at least in these two places.

If tomorrow something in the .down-right-box-shadow ruleset changes, I'll have to remember applying the same change to the .panel::after ruleset.

Is there any clean way out of this code replication?


Solution

  • Temani Afif has got a point; using variables:

    :root {
      --down-right-box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
    }
    .panel {
      background-color: #bbffff;
      padding: 1em;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 0.4em;
    }
    
    .down-right-box-shadow {
      box-shadow: var(--down-right-box-shadow);
    }
    
    .panel::after {
      content: "";
      width: 1em;
      height: 1em;
      border-radius: 100%;
      margin-top: 0.2em;
      background-color: blue;
      box-shadow: var(--down-right-box-shadow);
    }
    <div class="panel down-right-box-shadow">
        Some text
    </div>


    I may be missing something, but this should work as well:

    .down-right-box-shadow,
    .panel.down-right-box-shadow::after {
      box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
    }
    

    Of course you'd have to add multiple selectors in case you want other elements that are not .panel, but it solves the multiple box-shadow problem

    .panel {
      background-color: #bbffff;
      padding: 1em;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 0.4em;
    }
    
    .down-right-box-shadow,
    .panel.down-right-box-shadow::after {
      box-shadow: 0.5em 0.5em 0.5em 0em rgba(0,0,0,50%);
    }
    
    .panel::after {
      content: "";
      width: 1em;
      height: 1em;
      border-radius: 100%;
      margin-top: 0.2em;
      background-color: blue;
    }
    <div class="panel down-right-box-shadow">
    Some text
    </div>