Irrespective of whether you are working on a small-sized, medium-size or a large project, coding issues tend to creep up if they aren’t addressed properly. Selector Specificity is one such issue that haunts developers, especially the ones working on large-size websites. As your website becomes bigger in size, it is important that the CSS selectors must become shorter. If not, this could result in making the specificity higher, which causes self-induced specificity issues.
In order to overcome such issues, you need to make the selectors more specific. Here you’ll learn about how you can identify conflicting declarations, and will understand the basic way to calculate the specificity of each selector. Additionally, you’ll find some useful hacks to increase the specificity, thereby providing valuable insight that helps improve your stylesheets.
Dealing With CSS Specificity
Let’s Get Started
Many front coders avoid CSS specificity, as it seems a complicated topic to them. However, to help make the process easy to comprehend for beginners the community has been continuously contributing many useful resources. For instance, you can find guides, teaching about CSS specificity with analogies such as plankton, fish and sharks or using any other terminology. Besides this, you can even find a CSS Specificity Calculator available over the web that help in calculating the “CSS specificity of stylesheet” and so on. But, you may hardly find these guides comprehensible.
So, to help you understand CSS Specificity in a simplified manner, we’ll be discussing a few common hacks to deal with specificity (which you may have heard about or read online). The hacks best suit the need of the projects having complicated architecture, making it difficult for you to find a simple fix to overcome the CSS specificity problem. But, before diving in-depth about the various hacks to maintain CSS specificity, let’s first have a brief insight on what we mean by CSS Specificity and its key aspects.
CSS Specificity: An Insight
- Specificity plays an important role in CSS (Cascading Style Sheets) in identifying which rule will be applied to an element.
- CSS Specificity helps determine which CSS rule will be applied to a selector by the browsers.
- In essence, Selector Specificity is a process that identifies which CSS rule takes precedence when multiple rules are being applied by two or more declarations to a similar element in the HTML markup.
- If two selectors are being applied to the same element, then one having a higher specificity will emerge as the winner.
- The specificity level of a selector in CSS can be calculated using 4 different categories – that we’ll be covering in the next section.
The Basic algorithm to Calculate CSS Specificity
Let’s now proceed and learn about the basic algorithm that can be used for calculating specificity of a selector – to figure out conflicting declarations.
Any simple CSS selector falls into any one of the following four types:
- Type a: Count 1 for an inline style or otherwise count 0
- Type b: ID attributes
- Type c: class selectors + attribute selectors + pseudo-classes
- Type d: element names + psuedo-elements
For example: Look at the following code snippet of a simple selector, to calculate CSS specificity (i.e. occurrences of the above discussed types):
p.message { color: red; } div.warning p { color: black; } body#home div p { color: white; }
Let’s break down each selector to calculate the specificity:
#1
p.message{
color: red;
}
- it doesn’t contain an inline style (type a = 0);
- it has 0 ID selectors (type b = 0);
- it contains one class selector, i.e. .message (type c = 1);
- and contains only one element (type d = 1)
Result: Specificity for the selector is 0,0,1,1
#2
#warning p {
color: gray;
}
- it has no inline style (type a = 0);
- it contains 1 ID selector, i.e. #warning (type b = 1);
- it has no class selectors, attribute selectors or pseudo-class (type c = 0);
- and contains one element, i.e. p (type d = 1)
Result: Specificity for the selector is 0,1,0,1
#3
body#home>div#warning p.message {
color: black;
}
- it doesn’t contain an inline style (type a = 0);
- it has 2 ID selectors, i.e. #home and #warning (type b = 2);
- it contains one class selector, i.e. .message (type c = 1);
- and contains only three elements, i.e. body, div and p (type d = 3)
Result: Specificity for the selector is 0,2,1,3
Since this part of the code contains the highest specificity, the color of element “p” will be black.
Now, if you’ll compare all of the above three styles, you’ll notice that the selector p.message
contains a lower specificity compared to other two selectors. But, considering the fact that p.message
has low specificity than #warning p
can make the beginners rack their brains – who believes that a class selector is sufficient to match an element.
So, let us now talk about the several hacks that beginners can consider to overcome the selector specificity problem.
Hack 1 – Using the BEM Methodology
One of the easiest ways to tackle the selector specificity is to make use of the BEM (also referred to as Block Element Modifier) hack. It serves as an excellent front-end toolkit that helps make CSS more structured and less confusing to understand.
The BEM method doesn’t require you to reflect a block containing the nested DOM structure. And, the best part is that specificity remains the same for every selector. BEM helps write classes rather than IDs in HTML, so as to keep specificity low. In simple words, since you’ll be using only classes for your CSS, every selector will contain a specificity of 0,1,0.
BEM has proved an effective hack for front-end developers who work mainly using HTML, but it isn’t a good solution for developers who create CMS. That’s because, the CMS code editors don’t require HTML coding skills to create basic content. So, you cannot always use BEM for all of the projects. But, you can use CSS preprocessors (like LESS, SASS, etc.) as an alternative to the BEM hack.
Hack 2 – Make Some Existing Selector Heavy
In case you come across a specificity problem, you can resolve it by making a selector heavier than others, by prepending it with an ID, attribute selector, class, etc. This helps increase the specificity a little.
What’s more, many front end coders ignore IE when writing CSS code. However, this feature has been mainly created to address this issue, and targets IE with the help of a conditional HTML tag. But, there’s a lot more that this feature can provide, you can look forward to utilize.
You can prepend a selector using the “parent reference selector” (&) when using CSS preprocessors, as follows:
.class { body & { foo: bar; } }
When the above code is compiled, it will produce the following result:
body .class { foo: bar; }
You can even prepend the first block of the code using the html or body tag. But, it would be better to use something more specific that can be reflected on all your pages like .page
, , and many more. For instance, Sass lets you prepend with a selector within a variable, so that you can easily make changes to it in the future. In addition, for large-sized projects, you can choose to create a set of prepending selectors having different weights:
$prepend1: "html &"; $prepend2: "#wrapper &"; .selector { #{$prepend1} { background: #cacaca; } #{$prepend2} { background: #fefefe; } }
The result of the above code will be:
html .selector { background: #cacaca; } #wrapper .selector { background: #fefefe; }
Outcome:
So, if you’ll prepend an existing selector with, let’s suppose, a type selector then the specificity will be 0,1,1. And prepending the selector with an ID will result in a specificity: 1,1,0.
But there is a disadvantage of using this hack: in the case of prepending something with an already heaviest selector (e.g. #wrapper), you won’t be able to override it and rather will have to create a new heavier selector. But, this may not always be possible.
Hack 3 – Self-chained selectors
Although, the above hack that suggested prepending a selector with something is useful, but it isn’t an expandable solution – you may feel restricted when finding ways to target a parent. Plus, prepending over one selector, i.e. the heaviest one in your code will limit your options needed to overcome a specificity problem in the future.
Thankfully, the front-end community has once again made a worthy contribution by introducing the feature that requires self-chaining a (child) selector to bump up specificity.
Both prepending a selector feature and self-chained selectors work with ids, classes, and attribute selectors. The difference between the two is that the latter doesn’t work with type selectors. In fact, the self-chained selectors feature perfectly suits the CSS codebase that mostly use classes to style the contents of the CSS, as it provides a scalable way to override any type of selector.
Furthermore, the same selector can be chained to itself several times using the parent reference selector, as shown in the code below:
.selector { &#{&} { background: #cacaca; } &#{&}#{&} { background: #fefefe; } } .selector.selector { background: #cacaca; } .selector.selector.selector { background: #fefefe; }
This code can be a bit difficult to grasp in the beginning, and so, you can use a mixin instead to bump each selector specificity iteratively:
@mixin specificity($num: 1) { $selector: &; @if $num > 1 { @for $i from 1 to $num { $selector: $selector + &; } @at-root #{$selector} { @content; } } @else { @content; } } .selector { @include specificity(2) { background: #cacaca; } @include specificity(3) { background: #fefefe; } }
Resulting in the following CSS:
.selector.selector { background: #cacaca; } .selector.selector.selector { background: #fefefe; }
Outcome:
Self-chaining the selectors (using classes) will increment their specificity from 0,1,0 to 0,2,0 to 0,3,0 to 0,4,0 and so on.
Conclusion
Have you been battling with selector specificity issues when working on CSS? Then, hopefully this post will prove a handy guide for you.
In essence, using the hacks as discussed above to increase the specificity will make the selectors more specific and easier to understand. In fact, they’ll prove more useful compared to using any dev tools to check whether the styles in your CSS have been overridden by any other style, because of some unknown reason.