Skip to content

Commit

Permalink
Create and implement the specificity utility
Browse files Browse the repository at this point in the history
  • Loading branch information
jofaval committed Jul 12, 2022
1 parent d561942 commit 29a5bf3
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

This project won't have versionining per se. But it's format is based in the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## 2022-07-12

### Added

- Created the specificity utility for both, python and javascript.

## 2022-06-26

### Added
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,13 @@ An easy and fast to develop in language that's proven to be very useful, special
### Developed

- [ascii-to-text.py](./python/ascii-to-text.py), it does as it says, given ascii codes it returns a text, and viceversa.
- [store-folder-state.py](./python/store-folder-state.py), prints all of the folders, with the option for recursiveness, into a txt as a sort of snapshot of a given folder
- [store-folder-state.py](./python/store-folder-state.py), prints all of the folders, with the option for recursiveness, into a txt as a sort of snapshot of a given folder
- [specificity.py](./python/specificity.py), utility functions to ease the pain of CSS specificity.

## Javascript

The langauge by default for user interacion in the web.

### Developed

- [specificity.js](./javascript/specificity.js), the same python utility, but in javascript
63 changes: 63 additions & 0 deletions javascript/specificity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Gets the points from a rule
* @param {string} rule The rule to evaluate
* @returns {number} The number of points of that CSS selector
*/
const getRulePoints = (rule) => {
const firstCharacter = rule.charAt(0);
switch (firstCharacter) {
case ".":
return 10;
case "#":
return 100;

default:
return firstCharacter.match(/[a-z]/i) ? 1 : 0;
}
};

/**
* Calculates the points of a CSS instruction
* @param {string} instruction The CSS instruction to evaluate
* @returns {number} The total points
*/
const getSpecificityPoints = (instruction) => {
const selectors = instruction.split(" ");
const points = selectors.reduce(
(prev, curr) => prev + getRulePoints(curr),
0
);
return points;
};

/**
* Evaluates multiple rules at once
* @param {Array} rules All of the rules to evaluate
* @param {boolean?} ascending If it will be ascending, it won't by default
* @param {boolean?} with_scores Will it return the scores, it will by default
* @returns {Array} The same array sorted
*/
const evaluateRules = ({ rules, ascending = false, withScores = true }) => {
const rulesWithScores = rules.map((rule) => [
rule,
getSpecificityPoints(rule),
]);

let sortedRules = rulesWithScores.sort((a, b) => {
if (a == b) return 0;

// If the condition is true, it will be a one, if it's not, it will be converted to -1
let result = ascending ? Number(a < b) : Number(a > b);
if (result == 0) result = -1;

return result;
});

if (!withScores) {
sortedRules = sortedRules.map((combination) => combination[0]);
}

return sortedRules;
};

export default { evaluateRules, getRulePoints, getSpecificityPoints };
5 changes: 3 additions & 2 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@

## Done

- [ascii-to-text.py](./python/ascii-to-text.py)
- [store-folder-state.py](./python/store-folder-state.py)
- [ascii-to-text.py](./ascii-to-text.py)
- [store-folder-state.py](./store-folder-state.py)
- [specificity.py](./specificity.py)
103 changes: 103 additions & 0 deletions python/specificity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Typing
from typing import List, Tuple
# Regex
import re
# List utilities
from functools import reduce


def get_rule_points(
rule: str
) -> int:
"""
Gets the points from a rule
rule : str
The rule to evaluate
returns number The number of points of that CSS selector
"""
if not rule:
return 0

first_character = rule[0]

if first_character == ".":
# classes
return 10
elif first_character == "#":
# ids
return 100
elif re.match(r'[a-z]', first_character, re.IGNORECASE):
# html tags
return 1

return 0


def get_specificity_points(
instruction: str
) -> int:
"""
Calculates the points of a CSS instruction
instruction : str
The CSS instruction to evaluate
returns number The total points
"""
selectors = instruction.split(" ")
points = reduce(
lambda prev, curr: prev + get_rule_points(curr),
selectors,
0
)
return points


def evaluate_css_rules(
rules: List[str],
ascending: bool = False,
with_scores=True,
) -> List[Tuple[str, int]]:
"""
Evaluates multiple CSS rules and sorts them
rules : List[str]
The rules to evaluate
ascending : bool
If it will be ascending, it won't by default
with_scores : bool
Will it return the scores, it will by default
returns List[Tuple[str, int]]
"""
rules_with_specificity = map(
lambda rule: (rule, get_specificity_points(rule)),
rules
)
sorted_rules = sorted(
rules_with_specificity,
key=lambda x: x[1],
reverse=not ascending
)

if not with_scores:
sorted_rules = map(lambda rule, _: rule, sorted_rules)

return sorted_rules


# checkout my css specificity calculator for a more interactive utility
# https://github.com/jofaval/css-specificity-calculator
if __name__ == '__main__':
# should evaluate them all together
assert evaluate_css_rules(
rules=[
"my-lonely-html-tag",
"#unique-id > .my-class + my-cool-html-tag",
]
) == [
("#unique-id > .my-class + my-cool-html-tag", 111),
("my-lonely-html-tag", 1),
]

0 comments on commit 29a5bf3

Please sign in to comment.