0.0.2 • Published 4 years ago
postcss-set-specificity v0.0.2
postcss-set-specificity - Set selector specificity to equal another's
/* Input */
@set-specificity :root {
  #id::before {
    content: "earlier, more specific selector should not match";
  }
  .class::before {
    content: "later, less specific selector should match";
  }
}
@set-specificity {
  .a, .b, .c {
    --test: "";
  }
}
/* Output */
:is(*,:not(._)):where(#id)::before {
  content: "earlier, more specific selector should not match";
}
:is(*,:not(._)):where(.class)::before {
  content: "later, less specific selector should match";
}
:where(.a), :where(.b), :where(.c) {
  --test: "";
}Installing
npm i -D postcss-set-specificitypostcss.config.js
module.exports = {
  plugins: [
    require("postcss-set-specificity") // After nesting plugin, if used
  ]
}Consult PostCSS documentation for alternative usage.
Browser Support
Requires support for these CSS Selectors Level 4 features:
This corresponds to a browserslist roughly like:
Chrome >= 88
Safari >= 14
Firefox >= 78How It Works
Given that:
:is()and:not()have specificities equal to the maximum specificity within them:is(*, :not(x))matches like the universal selector (*), but with the specificity ofx:where()always has a specificity of zero:is(*, :not(x)):where(y)matches likey, but with the specificity ofx- the specificity of a selector can be matched in an optimal way:
element#id.class:pseudo-class[attribute]::pseudo-element element *=>1,3,3_+_+_#_._._._=>1,3,3
 - pseudo elements can't be used in 
:where(),:is(), or:not() 
If provided with:
@set-specificity x {
  y::before {}
}The plugin produces:
:is(*,:not(_)):where(y)::before {}In cases where there is no selector provided, we can simply use :where() alone.
Caveats
- Does not yet interoperate with CSS Nesting, but wouldn't be hard to with 
@nest. - Pseudo elements are treated as exceptions and add 
0,0,1to the specificity, as they cannot be set to zero, and simply subtracting will lead to inconsistency. - Pseudo elements must be written with the 
::prefix instead of with their old:prefix 
Unimplemented Optimizations
- Calculate specificities
- Partial shared specificity
:is(*,:not(a#b.c)):where(d#e)tod#e:is(*,:not(.c))
 - Full shared specificity
:is(*,:not(a#b.c)):where(d#e.f)tod#e.f
 
 - Partial shared specificity
 - Parse selector lists for correctness
- Valid selector list (invalid selector lists would not fail because 
:where()is supposed to be forgiving):where(.a), :where(.b), :where(.c)to:where(.a,.b,.c)
 
 - Valid selector list (invalid selector lists would not fail because 
 - Assumptions/loose behavior
- Assume selector never to match
:is(*,:not(#🔝)):where(.a)to:not(#🔝):where(.a)
 - Assume selector will always match (e.g. must use something like 
<html id="🔝" class="🔝">):is(*,:not(#_)):where(.a)to#🔝 :where(.a)
 
 - Assume selector never to match