mirror of
https://github.com/snachodog/just-the-docs.git
synced 2025-09-13 05:13:33 -06:00
Initial commit
This commit is contained in:
107
node_modules/stylelint/lib/rules/no-descending-specificity/README.md
generated
vendored
Normal file
107
node_modules/stylelint/lib/rules/no-descending-specificity/README.md
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
# no-descending-specificity
|
||||
|
||||
Disallow selectors of lower specificity from coming after overriding selectors of higher specificity.
|
||||
|
||||
```css
|
||||
#container a { top: 10px; } a { top: 0; }
|
||||
/** ↑ ↑
|
||||
* The order of these selectors represents descending specificity */
|
||||
```
|
||||
|
||||
Source order is important in CSS, and when two selectors have the *same* specificity, the one that occurs *last* will take priority. However, the situation is different when one of the selectors has a *higher* specificity. In that case, source order does *not* matter: the selector with higher specificity will win out even if it comes first.
|
||||
|
||||
The clashes of these two mechanisms for prioritization, source order and specificity, can cause some confusion when reading stylesheets. If a selector with higher specificity comes *before* the selector it overrides, we have to think harder to understand it, because it violates the source order expectation. **Stylesheets are most legible when overriding selectors always come *after* the selectors they override.** That way both mechanisms, source order and specificity, work together nicely.
|
||||
|
||||
This rule enforces that practice *as best it can*. (It cannot catch every *actual* overriding selector (because it does not know the DOM structure, for one), but it can catch certain common mistakes.)
|
||||
|
||||
Here's how it works: **This rule looks at the last *compound selector* in every full selector, and then compares it with other selectors in the stylesheet that end in the same way.**
|
||||
|
||||
So `.foo .bar` (whose last compound selector is `.bar`) will be compared to `.bar` and `#baz .bar`, but not to `#baz .foo` or `.bar .foo`.
|
||||
|
||||
And `a > li#wag.pit` (whose last compound selector is `li#wag.pit`) will be compared to `div li#wag.pit` and `a > b > li + li#wag.pit`, but not to `li`, or `li #wag`, etc.
|
||||
|
||||
There's one other important feature: Selectors targeting pseudo-elements are not considered comparable to similar selectors without the pseudo-element, because they target other elements on the rendered page. For example, `a::before {}` will not be compared to `a:hover {}`, because `a::before` targets a pseudo-element whereas `a:hover` targets the actual `<a>`.
|
||||
|
||||
This rule only compares rules that are within the same media context. So `a {} @media print { #baz a {} }` is fine.
|
||||
|
||||
This rule resolves nested selectors before calculating the specificity of the selectors.
|
||||
|
||||
## Options
|
||||
|
||||
### `true`
|
||||
|
||||
The following patterns are considered warnings:
|
||||
|
||||
```css
|
||||
b a {}
|
||||
a {}
|
||||
```
|
||||
|
||||
```css
|
||||
a + a {}
|
||||
a {}
|
||||
```
|
||||
|
||||
```css
|
||||
b > a[foo] {}
|
||||
a[foo] {}
|
||||
```
|
||||
|
||||
```css
|
||||
a {
|
||||
& > b {}
|
||||
}
|
||||
b {}
|
||||
```
|
||||
|
||||
```css
|
||||
@media print {
|
||||
#c a {}
|
||||
a {}
|
||||
}
|
||||
```
|
||||
|
||||
The following patterns are *not* considered warnings:
|
||||
|
||||
```css
|
||||
a {}
|
||||
b a {}
|
||||
```
|
||||
|
||||
```css
|
||||
a {}
|
||||
a + a {}
|
||||
```
|
||||
|
||||
```css
|
||||
a[foo] {}
|
||||
b > a[foo] {}
|
||||
```
|
||||
|
||||
```css
|
||||
b {}
|
||||
a {
|
||||
& > b {}
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
a::before {}
|
||||
a:hover::before {}
|
||||
a {}
|
||||
a:hover {}
|
||||
```
|
||||
|
||||
```css
|
||||
@media print {
|
||||
a {}
|
||||
#c a {}
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
a {}
|
||||
@media print {
|
||||
#baz a {}
|
||||
}
|
||||
```
|
98
node_modules/stylelint/lib/rules/no-descending-specificity/index.js
generated
vendored
Normal file
98
node_modules/stylelint/lib/rules/no-descending-specificity/index.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
"use strict"
|
||||
|
||||
const specificity = require("specificity")
|
||||
const findAtRuleContext = require("../../utils/findAtRuleContext")
|
||||
const isCustomPropertySet = require("../../utils/isCustomPropertySet")
|
||||
const nodeContextLookup = require("../../utils/nodeContextLookup")
|
||||
const parseSelector = require("../../utils/parseSelector")
|
||||
const report = require("../../utils/report")
|
||||
const ruleMessages = require("../../utils/ruleMessages")
|
||||
const validateOptions = require("../../utils/validateOptions")
|
||||
const _ = require("lodash")
|
||||
const keywordSets = require("../../reference/keywordSets")
|
||||
const resolvedNestedSelector = require("postcss-resolve-nested-selector")
|
||||
|
||||
const ruleName = "no-descending-specificity"
|
||||
|
||||
const messages = ruleMessages(ruleName, {
|
||||
rejected: (b, a) => `Expected selector "${b}" to come before selector "${a}"`,
|
||||
})
|
||||
|
||||
const rule = function (actual) {
|
||||
return (root, result) => {
|
||||
const validOptions = validateOptions(result, ruleName, { actual })
|
||||
if (!validOptions) {
|
||||
return
|
||||
}
|
||||
|
||||
const selectorContextLookup = nodeContextLookup()
|
||||
|
||||
root.walkRules(rule => {
|
||||
// Ignore custom property set `--foo: {};`
|
||||
if (isCustomPropertySet(rule)) {
|
||||
return
|
||||
}
|
||||
|
||||
const comparisonContext = selectorContextLookup.getContext(rule, findAtRuleContext(rule))
|
||||
|
||||
rule.selectors.forEach(selector => {
|
||||
const trimSelector = selector.trim()
|
||||
// Ignore `.selector, { }`
|
||||
if (trimSelector === "") {
|
||||
return
|
||||
}
|
||||
|
||||
// The edge-case of duplicate selectors will act acceptably
|
||||
const index = rule.selector.indexOf(trimSelector)
|
||||
// Resolve any nested selectors before checking
|
||||
resolvedNestedSelector(selector, rule).forEach(resolvedSelector => {
|
||||
parseSelector(resolvedSelector, result, rule, s => checkSelector(s, rule, index, comparisonContext))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function checkSelector(selectorNode, rule, sourceIndex, comparisonContext) {
|
||||
const selector = selectorNode.toString()
|
||||
const referenceSelectorNode = lastCompoundSelectorWithoutPseudoClasses(selectorNode)
|
||||
const selectorSpecificity = specificity.calculate(selector)[0].specificityArray
|
||||
const entry = { selector, specificity: selectorSpecificity }
|
||||
|
||||
if (!comparisonContext.has(referenceSelectorNode)) {
|
||||
comparisonContext.set(referenceSelectorNode, [entry])
|
||||
return
|
||||
}
|
||||
|
||||
const priorComparableSelectors = comparisonContext.get(referenceSelectorNode)
|
||||
|
||||
priorComparableSelectors.forEach(priorEntry => {
|
||||
if (specificity.compare(selectorSpecificity, priorEntry.specificity) === -1) {
|
||||
report({
|
||||
ruleName,
|
||||
result,
|
||||
node: rule,
|
||||
message: messages.rejected(selector, priorEntry.selector),
|
||||
index: sourceIndex,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
priorComparableSelectors.push(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lastCompoundSelectorWithoutPseudoClasses(selectorNode) {
|
||||
const nodesAfterLastCombinator = _.last(selectorNode.nodes[0].split(node => {
|
||||
return node.type === "combinator"
|
||||
}))
|
||||
|
||||
const nodesWithoutPseudoClasses = nodesAfterLastCombinator.filter(node => {
|
||||
return node.type !== "pseudo" || keywordSets.pseudoElements.has(node.value.replace(/:/g, ""))
|
||||
}).join("")
|
||||
|
||||
return nodesWithoutPseudoClasses.toString()
|
||||
}
|
||||
|
||||
rule.ruleName = ruleName
|
||||
rule.messages = messages
|
||||
module.exports = rule
|
Reference in New Issue
Block a user