Skip to content

Instantly share code, notes, and snippets.

@davidkpiano
Last active October 26, 2020 08:38
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidkpiano/dbf20a242bcccd1d789c to your computer and use it in GitHub Desktop.
Save davidkpiano/dbf20a242bcccd1d789c to your computer and use it in GitHub Desktop.
Generated by SassMeister.com.
// ----
// Sass (v3.4.7)
// Compass (v1.0.1)
// ----
/// SCSS Specificity Calculator
///
/// Utility to calculate (and display) specificity or specificity map of any
/// valid simple/compound/complex selector.
/// @author David Khourshid
/// Replaces a batch of substrings (needles) in a string (haystack)
/// with a single replacement string.
/// @access private
/// @param {String} $haystack - string to perform search and replacement on
/// @param {List | String} $needles - string or list of strings to replace globally
/// @param {String} $replacement ('') - replacement string to replace needles
/// @return {String} replaced string
@function str-replace-batch($haystack, $needles, $replacement: '') {
$instances: false;
@if not type_of($needles) == list { $needles: ($needles); }
@while ($instances == false) or ($instances > 0) {
$instances: 0;
@each $needle in $needles {
$needle-index: str-index($haystack, $needle);
$instances: $instances + if($needle-index, 1, 0);
@if $needle-index {
$haystack: str-slice($haystack, 1, $needle-index - 1) + str-slice($haystack, $needle-index + str-length($needle), -1);
$haystack: str-insert($haystack, $replacement, $needle-index);
}
}
}
@return $haystack;
}
/// Signifies what specificity type the simple selector is.
/// a - IDs
/// b - class selectors, attribute selectors, pseudo-classes
/// c - type (element) selectors, pseudo-elements
/// @access private
/// @param {String} $simple-selector - a single simple selector
/// @return {String} specificity type of simple selector - 'a', 'b', 'c', or false
@function specificity-type($simple-selector) {
$types: (
c: (':before', ':after', ':first-line', ':first-letter', ':selection'),
b: ('.', '[', ':'),
a: ('#')
);
$simple-selector: str-replace-batch($simple-selector, '::', ':');
@if str-index($simple-selector, ':not(') == 1 {
$simple-selector: str-slice($simple-selector, 6, -2);
}
@each $type-key, $type-tokens in $types {
@each $token in $type-tokens {
@if str-index($simple-selector, $token) == 1 {
@return $type-key;
}
}
}
// Ignore the universal selector
@if str-index($simple-selector, '*') == 1 {
@return false;
}
// Simple selector is type selector (element)
@return c;
}
/// Returns the specificity value (in the specified base).
/// Base is set to 256 (16^2) to accurately represent historical 6-digit
/// hexadecimal representation of specificity in most browsers, though this
/// limitation has been "resolved" in some browsers (try base 65536 for those).
/// @access private
/// @param {Map} $specificity-map - map of frequency of each type (a, b, c) in complex selector
/// @param {Number} $base (256) - base used to calculate specificity value (default: 256)
/// @return {Number} specificity value of given specificity map as base 10 integer
@function specificity-value($specificity-map, $base: 256) {
$exponent-map: (a: 2, b: 1, c: 0);
$specificity: 0;
@each $specificity-type, $specificity-value in $specificity-map {
$specificity: $specificity + ($specificity-value * pow($base, map-get($exponent-map, $specificity-type)));
}
@return $specificity;
}
/// Returns the specificity map or value of given simple/complex/multiple selector(s).
/// @access public
/// @param {List | String} $initial-selector - selector returned by '&'
/// @param {Bool} $integer (false) - output specificity as integer? (default: false)
/// @return {Map | Number} specificity map or specificity value represented as integer
@function specificity($initial-selector, $integer: false) {
$initial-selector: str-replace-batch(#{$initial-selector}, ('+', '>', '~'));
$selectors: selector-parse($initial-selector);
$specificities-map: ();
@each $selector in $selectors {
$parts: ();
$selector-specificity-map: (a: 0, b: 0, c: 0);
@each $simple-selectors in $selector {
@each $simple-selector in simple-selectors($simple-selectors) {
$parts: append($parts, $simple-selector);
}
}
@each $part in $parts {
$specificity-type: specificity-type($part);
@if $specificity-type {
$selector-specificity-map: map-merge($selector-specificity-map, (#{$specificity-type}: map-get($selector-specificity-map, $specificity-type) + 1));
}
}
$specificities-map: map-merge($specificities-map, (specificity-value($selector-specificity-map): $selector-specificity-map));
}
$specificity-value: max(map-keys($specificities-map)...);
$specificity-map: map-values(map-get($specificities-map, $specificity-value));
@return if($integer, $specificity-value, $specificity-map);
}
/// Outputs specificity in your CSS as (invalid) properties.
/// Please, don't use this mixin in production.
/// @access public
/// @output specificity (map as string), specificity-value (specificity value as integer)
@mixin specificity() {
specificity: specificity(&);
specificity-value: specificity(&, true);
}
* { @include specificity; }
li { @include specificity; }
li:first-line { @include specificity; }
ul li { @include specificity; }
ul ol+li { @include specificity; }
h1 + *[rel=up] { @include specificity; }
ul ol li.red { @include specificity; }
li.red.level { @include specificity; }
p { @include specificity; }
div p { @include specificity; }
.sith { @include specificity; }
div p.sith { @include specificity; }
#sith { @include specificity; }
body #darkside .sith p { @include specificity; }
* { @include specificity; }
LI { @include specificity; }
UL LI { @include specificity; }
UL OL+LI { @include specificity; }
H1 + *[REL=up] { @include specificity; }
UL OL LI.red { @include specificity; }
LI.red.level { @include specificity; }
#x34y { @include specificity; }
#s12:not(FOO) { @include specificity; }
/* The highest specificity is the one that's displayed */
#super_high_specificity, .sorta[low="specificity"] {
@include specificity;
}
/* It even works with nested selectors! */
.first {
.second[disabled]:hover {
.third > #fourth ~ div ul > li:first-child + li.active {
@include specificity;
}
}
}
/* Here's why in some browsers, 256 classes can override a single ID! */
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
{ @include specificity; }
#h { @include specificity; }
* {
specificity: 0, 0, 0;
specificity-value: 0;
}
li {
specificity: 0, 0, 1;
specificity-value: 1;
}
li:first-line {
specificity: 0, 0, 2;
specificity-value: 2;
}
ul li {
specificity: 0, 0, 2;
specificity-value: 2;
}
ul ol + li {
specificity: 0, 0, 3;
specificity-value: 3;
}
h1 + *[rel=up] {
specificity: 0, 1, 1;
specificity-value: 257;
}
ul ol li.red {
specificity: 0, 1, 3;
specificity-value: 259;
}
li.red.level {
specificity: 0, 2, 1;
specificity-value: 513;
}
p {
specificity: 0, 0, 1;
specificity-value: 1;
}
div p {
specificity: 0, 0, 2;
specificity-value: 2;
}
.sith {
specificity: 0, 1, 0;
specificity-value: 256;
}
div p.sith {
specificity: 0, 1, 2;
specificity-value: 258;
}
#sith {
specificity: 1, 0, 0;
specificity-value: 65536;
}
body #darkside .sith p {
specificity: 1, 1, 2;
specificity-value: 65794;
}
* {
specificity: 0, 0, 0;
specificity-value: 0;
}
LI {
specificity: 0, 0, 1;
specificity-value: 1;
}
UL LI {
specificity: 0, 0, 2;
specificity-value: 2;
}
UL OL + LI {
specificity: 0, 0, 3;
specificity-value: 3;
}
H1 + *[REL=up] {
specificity: 0, 1, 1;
specificity-value: 257;
}
UL OL LI.red {
specificity: 0, 1, 3;
specificity-value: 259;
}
LI.red.level {
specificity: 0, 2, 1;
specificity-value: 513;
}
#x34y {
specificity: 1, 0, 0;
specificity-value: 65536;
}
#s12:not(FOO) {
specificity: 1, 0, 1;
specificity-value: 65537;
}
/* The highest specificity is the one that's displayed */
#super_high_specificity, .sorta[low="specificity"] {
specificity: 1, 0, 0;
specificity-value: 65536;
}
/* It even works with nested selectors! */
.first .second[disabled]:hover .third > #fourth ~ div ul > li:first-child + li.active {
specificity: 1, 7, 4;
specificity-value: 67332;
}
/* Here's why in some browsers, 256 classes can override a single ID! */
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h
.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h.h {
specificity: 0, 256, 0;
specificity-value: 65536;
}
#h {
specificity: 1, 0, 0;
specificity-value: 65536;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment