Update 2022.03.31 The code examples included on this page is outdated as @bramus/specificity
2.0.0 has been released. Read the updated documentation for that version here.
To calculate the Specificity of a CSS Selector there are several rules to follow. To not have to manually calculate the specificity, you can resort to online tools such as the wonderful CSS Specificity Calculator by Kilian Valkhof.
When it comes to calculating specificity from within your own JavasScript code, there’s no package with proper Selectors Level 4 support to use … until now!
~
# The Package
Last night, sparked by Kilian’s online tool, I created a package named @bramus/specificity
. It comes as an ES Module and exposes a calculate
function to calculate the specificity of a given CSS selector.
Installation per NPM:
npm i @bramus/specificity
Once installed, you can use it as follows:
import { calculate } from '@bramus/specificity';
const specificity = calculate('.foo :is(.bar, #baz)');
// ~> { a: 1, b: 1, c: 0 }
A CJS-compatible build is also included.
~
# Implementation Demo
Here’s a quick demo that consumes the package:
See the Pen
Calculate Specificity by Bramus (@bramus)
on CodePen.
Note that this is a really simple implementation. It only shows the specificity, it doesn’t explain why it is calculated like that. If you’re looking for something fancier / more useful, go check out the CSS Specificity Calculator by Kilian Valkhof / Polypane
~
# The Return Format
Contrary to popular belief, CSS Specificity is not just a single number. It’s a triad, printed as (A,B,C)
. Therefore an Object with the keys a
, b
, c
is returned.
In a future release, the return format might be extended to contain more info. I’m open to input on this.
~
# Under the hood
The heavy lifting is done by CSSTree, a tool that can parse CSS to an AST.
const ast = csstree.parse(selector, {
context: 'selectorList',
});
By manually walking the generated AST, the Specificity is calculated by following the Specificity Rules:
- Count the number of ID selectors in the selector (= A)
- Count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B)
- Count the number of type selectors and pseudo-elements in the selector (= C)
- Ignore the universal selector
Unlike other libraries, @bramus/specificity
also plays nice with “evaluation contexts” that have their specificity defined specially:
- The specificity of an
:is()
,:not()
, or:has()
pseudo-class is replaced by the specificity of the most specific complex selector in its selector list argument.- Analogously, the specificity of an
:nth-child()
or:nth-last-child()
selector is the specificity of the pseudo class itself (counting as one pseudo-class selector) plus the specificity of the most specific complex selector in its selector list argument (if any).- The specificity of a
:where()
pseudo-class is replaced by zero.
~
# Limitations
As mentioned above, the return format currently is nothing but a simple Object which you’ll have to process yourself. In the future, the format might get extended or some extra methods may be exposed.
Furthermore input is now locked to a single Selector, not a SelectorList. This might change in an upcoming release.
~
# Links
@bramus/specificity
Source (GitHub) →@bramus/specificity
on NPM →
~
# Spread the word
To help spread the contents of this post, feel free to retweet the announcement tweet:
Calculate the Specificity of a CSS Selector with `@bramus/specificity`
🏷 #css #javascript #specificity pic.twitter.com/3ObQbmtPfQ
— Bram.us (@bramusblog) February 23, 2022
~
🔥 Like what you see? Want to stay in the loop? Here's how: