Score CSS selector specificity
"Why isn't my style applying?" is nearly always specificity. Being able to score a selector by hand is what turns that question into a two-second answer, and linters and CSS-in-JS tools compute exactly this triple.
Implement specificity(selector) returning [a, b, c], where a counts id selectors, b counts class selectors, attribute selectors and pseudo-classes, and c counts type (element) selectors and pseudo-elements. Assume a single selector with no commas.
Count each #id into a. Count each .class, each [...] attribute selector, and each single-colon pseudo-class such as :hover or :nth-child(2n+1) into b; the arguments of a functional pseudo-class are ignored. Count each type selector such as div and each double-colon pseudo-element such as ::before into c. The universal selector * and the combinators >, +, ~ and descendant whitespace count for nothing.
:not(), :is() and :has() add nothing for themselves, but the selectors written inside their parentheses are counted as if they appeared in their place — so li:not(.active) scores [0, 1, 1]. Note that anything inside an attribute selector, including a # in [href^="#"], is part of the attribute selector and is never counted separately.
Arguments
selectorstring- A single CSS selector with no commas. Combinators, pseudo-classes, pseudo-elements and attribute selectors are supported.
Returns
[number, number, number] — A three-tuple [ids, (classes + attrs + pseudo-classes), (types + pseudo-elements)] — the triple CSS compares to resolve conflicts.
Examples
specificity("#nav .item a");
// => [1, 1, 1]One id, one class, one element selector.
specificity('a[href^="#"]:hover');
// => [0, 2, 1]The # inside the attribute selector does not count as an id.
specificity("li:not(.active) > span::before");
// => [0, 1, 3]:not() itself scores nothing, but .active inside it counts; ::before is a pseudo-element.
What it has to do
- Return a three element array
[ids, classes, types]of numbers. - Count
#idinto the first slot, and.class,[attr]and:pseudo-classinto the second. - Count element names and
::pseudo-elementsinto the third. - Score
*and all combinators as zero. - Ignore
:not(),:is()and:has()themselves but count the selectors inside them, and never count characters inside an attribute selector.
Notes
- Specificity is compared lexicographically from left to right:
[1, 0, 0]always beats[0, 10, 10]. A single#idcan override a wall of classes. - Inline
style="..."and!importantsit above this triple in the real cascade. The score only decides which stylesheet rule wins before those overrides apply.
Stuck? The javascript course covers everything this challenge needs.
- 1.scores one of each kind
- 2.scores a descendant selector of two elements
- 3.ignores a hash inside an attribute selector
- 4.counts inside :not() but not the :not() itself
- 5.scores the universal selector as zero
These assertions run in your browser the moment you hit Run. Pass all of them to claim the XP.
Discussion
Sign in to postNo comments yet. Be the first to say something.