RH

CSS: Why scope may not be what you think it is

Many people talk about 'scope' in CSS. I want to examine this and try and work out exactly what is meant by this.

What is scope in CSS?

Harry Roberts

In this article Harry Roberts talks about 'loose selectors'. By this, he means selectors that are overly generic and so can easily be reused elsewhere in the stylesheet by accident.

According to Roberts, selectors are like variables that exist in the global scope of the stylesheet and have the same name clashing issues that global variables in a programming language have. They can be easily overwritten and can have unintended consequences in other parts of the program.

The problem that Roberts describes is a real one. However, a slight quibble is that selectors are not really like variable names. Selectors can clash even when they are completely different.

Consider the following:

Hello


<style>
  h2 {
    background: red !important;
  }

  .foo {
    background: green;
    font-size: 50px;
  }


</style>
<h2 class="foo">
  Hello
</h2>

The two rule sets' selectors are completely different, yet we still found that the declarations in one of them were overidden by the other.

I think that selectors are more like functions. They take a selector string and pass back a set of matching elements from the DOM. These functions do though act upon the entire document. We could write them like this:

  const elements1 = document.querySelectorAll('h2');
  const elements2 = document.querySelectorAll('.foo');

Note that the method acts upon the document. Every selector takes as input every element in the document.

Nor are rule sets like normal variable values. The declarations within a rule set are not automaticaly applied to every element that matches the selector. The cascade in CSS peforms a filtering process which only applies those declarations to a matching element whose selector has the highest specificity. So we cannot say that all the declarations in a rule set have the same scope. Some will apply and some wont.

In general, I do agree that one should be mindful of the fact that selectors have the potential to act upon every element in the document.

Chris Coyier

Chris Coyier of CSS Tricks also talks about CSS scope in this article. He gives this example and points out that the

font-family:Roboto, sans-serif
declaration is now applied on every element in the document:

html {
  font-family: Roboto, sans-serif;
}

Note that this is a different type of scope to that discussed by Roberts. This concerns the scope of the declaration rather than of the selector. The reason that this declaration is global is because the rule is applied to the root element, and because the font-family property is inherited. Consider this;

html {
  border: solid 2px red;
}

This does not apply a red border to every element in the document. This is because the border property is not inherited. Only the root element will have the border. To make every element have the red border, we would have to do something like this:

* {
  border: solid 2px red;
}

This uses the universal selector to target every element.

In Coyier's model, the variable is the declaration. 'font-size' is the variable name. 'Roboto, sans-serif' is the variable value. This 'variable' has global scope because it belongs to a ruleset that applies to the root element. Had the rule set applied to a smaller part of the document, we would have said that the variable had a 'local scope'. In this model of scope, therefore, scope is determined by the document tree. This is quite similar to the programming model where you have a tree of blocks that determine scope. Note, however, that when we used the 'border' property instead of 'font-family' we did not have global scope, and the scope was instead localised on the html element.

Conclusion

What I think this illustrates is that the concept of 'scope' in CSS is still somewhat vaguely defined.

We have two basic models of scope: selector scope and declaration scope. Selector scope is always global since every selector acts upon every element in the document. Property scope is global only if the property is inherited and if the property is set on the root element. Otherwise it is local.

Part of the problem is that scope isn't an actual CSS technical feature, it's a concept that developers use to help them reason about it. (Roberts describes it as a 'metaphor'). As such, there isn't any definitive definition of it anywhere.

I wouldn't say that it's not a useful way of thinking about CSS, but I would urge caution. There is a danger that it can distort your thinking about how CSS actually works.

Featured writings