mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 02:06:08 +03:00
feat(css/minifier): Compress selectors (#3623)
This commit is contained in:
parent
b1190ee203
commit
9e691fe75b
@ -26,7 +26,7 @@ impl VisitMut for CompressEmpty {
|
||||
| Rule::AtRule(AtRule::Property(PropertyRule { block, .. }))
|
||||
if block.value.is_empty() =>
|
||||
{
|
||||
return false;
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
@ -72,7 +72,7 @@ impl VisitMut for CompressEmpty {
|
||||
| ComponentValue::KeyframeBlock(KeyframeBlock { block, .. })
|
||||
if block.value.is_empty() =>
|
||||
{
|
||||
return false;
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
|
@ -2,4 +2,5 @@ pub mod angle;
|
||||
pub mod easing_function;
|
||||
pub mod empty;
|
||||
pub mod keyframes;
|
||||
pub mod selector;
|
||||
pub mod time;
|
||||
|
282
crates/swc_css_minifier/src/compress/selector.rs
Normal file
282
crates/swc_css_minifier/src/compress/selector.rs
Normal file
@ -0,0 +1,282 @@
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_css_ast::*;
|
||||
use swc_css_visit::{VisitMut, VisitMutWith};
|
||||
|
||||
pub fn compress_selector() -> impl VisitMut {
|
||||
CompressSelector {}
|
||||
}
|
||||
|
||||
struct CompressSelector {}
|
||||
|
||||
impl CompressSelector {}
|
||||
|
||||
impl VisitMut for CompressSelector {
|
||||
fn visit_mut_nth(&mut self, nth: &mut Nth) {
|
||||
nth.visit_mut_children_with(self);
|
||||
|
||||
match &nth.nth {
|
||||
// `2n+1`, `2n-1`, `2n-3`, etc => `odd`
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
span,
|
||||
..
|
||||
}) if *a == 2 && (*b == 1 || b % 2 == -1) => {
|
||||
nth.nth = NthValue::Ident(Ident {
|
||||
span: *span,
|
||||
value: "odd".into(),
|
||||
raw: "odd".into(),
|
||||
});
|
||||
}
|
||||
// `2n-0`, `2n-2`, `2n-4`, etc => `2n`
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
span,
|
||||
..
|
||||
}) if *a == 2 && *b < 0 && b % 2 == 0 => {
|
||||
nth.nth = NthValue::AnPlusB(AnPlusB {
|
||||
span: *span,
|
||||
a: Some(2),
|
||||
a_raw: Some("2".into()),
|
||||
b: None,
|
||||
b_raw: None,
|
||||
});
|
||||
}
|
||||
// `even` => `2n`
|
||||
NthValue::Ident(Ident { value, span, .. }) if &*value.to_lowercase() == "even" => {
|
||||
nth.nth = NthValue::AnPlusB(AnPlusB {
|
||||
span: *span,
|
||||
a: Some(2),
|
||||
a_raw: Some("2".into()),
|
||||
b: None,
|
||||
b_raw: None,
|
||||
});
|
||||
}
|
||||
// `0n+5` => `5`, `0n-5` => `-5`, etc
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: Some(a),
|
||||
b,
|
||||
b_raw,
|
||||
span,
|
||||
..
|
||||
}) if *a == 0 => {
|
||||
nth.nth = NthValue::AnPlusB(AnPlusB {
|
||||
span: *span,
|
||||
a: None,
|
||||
a_raw: None,
|
||||
b: *b,
|
||||
b_raw: b_raw.clone(),
|
||||
});
|
||||
}
|
||||
// `-5n+0` => `-5n`, etc
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a,
|
||||
a_raw,
|
||||
b: Some(b),
|
||||
span,
|
||||
..
|
||||
}) if *b == 0 => {
|
||||
nth.nth = NthValue::AnPlusB(AnPlusB {
|
||||
span: *span,
|
||||
a: *a,
|
||||
a_raw: a_raw.clone(),
|
||||
b: None,
|
||||
b_raw: None,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_subclass_selector(&mut self, subclass_selector: &mut SubclassSelector) {
|
||||
subclass_selector.visit_mut_children_with(self);
|
||||
|
||||
match &subclass_selector {
|
||||
SubclassSelector::PseudoElement(PseudoElementSelector { name, span, .. }) => {
|
||||
match &*name.value.to_lowercase() {
|
||||
"before" | "after" | "first-letter" | "first-line" => {
|
||||
*subclass_selector = SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
span: *span,
|
||||
name: name.clone(),
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
name,
|
||||
children: Some(children),
|
||||
span,
|
||||
..
|
||||
}) if &*name.value.to_lowercase() == "nth-child" && children.len() == 1 => {
|
||||
match children.get(0) {
|
||||
Some(PseudoSelectorChildren::Nth(Nth {
|
||||
nth:
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: None,
|
||||
b: Some(b),
|
||||
..
|
||||
}),
|
||||
..
|
||||
})) if *b == 1 => {
|
||||
*subclass_selector = SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
span: *span,
|
||||
name: Ident {
|
||||
span: DUMMY_SP,
|
||||
value: "first-child".into(),
|
||||
raw: "first-child".into(),
|
||||
},
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
name,
|
||||
children: Some(children),
|
||||
span,
|
||||
..
|
||||
}) if &*name.value.to_lowercase() == "nth-last-child" && children.len() == 1 => {
|
||||
match children.get(0) {
|
||||
Some(PseudoSelectorChildren::Nth(Nth {
|
||||
nth:
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: None,
|
||||
b: Some(b),
|
||||
..
|
||||
}),
|
||||
..
|
||||
})) if *b == 1 => {
|
||||
*subclass_selector = SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
span: *span,
|
||||
name: Ident {
|
||||
span: DUMMY_SP,
|
||||
value: "last-child".into(),
|
||||
raw: "last-child".into(),
|
||||
},
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
name,
|
||||
children: Some(children),
|
||||
span,
|
||||
..
|
||||
}) if &*name.value.to_lowercase() == "nth-of-type" && children.len() == 1 => {
|
||||
match children.get(0) {
|
||||
Some(PseudoSelectorChildren::Nth(Nth {
|
||||
nth:
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: None,
|
||||
b: Some(b),
|
||||
..
|
||||
}),
|
||||
..
|
||||
})) if *b == 1 => {
|
||||
*subclass_selector = SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
span: *span,
|
||||
name: Ident {
|
||||
span: DUMMY_SP,
|
||||
value: "first-of-type".into(),
|
||||
raw: "first-of-type".into(),
|
||||
},
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
name,
|
||||
children: Some(children),
|
||||
span,
|
||||
..
|
||||
}) if &*name.value.to_lowercase() == "nth-last-of-type" && children.len() == 1 => {
|
||||
match children.get(0) {
|
||||
Some(PseudoSelectorChildren::Nth(Nth {
|
||||
nth:
|
||||
NthValue::AnPlusB(AnPlusB {
|
||||
a: None,
|
||||
b: Some(b),
|
||||
..
|
||||
}),
|
||||
..
|
||||
})) if *b == 1 => {
|
||||
*subclass_selector = SubclassSelector::PseudoClass(PseudoClassSelector {
|
||||
span: *span,
|
||||
name: Ident {
|
||||
span: DUMMY_SP,
|
||||
value: "last-of-type".into(),
|
||||
raw: "last-of-type".into(),
|
||||
},
|
||||
children: None,
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_compound_selector(&mut self, compound_selector: &mut CompoundSelector) {
|
||||
compound_selector.visit_mut_children_with(self);
|
||||
|
||||
if let Some(TypeSelector::Universal(UniversalSelector { prefix: None, .. })) =
|
||||
&compound_selector.type_selector
|
||||
{
|
||||
compound_selector.type_selector = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_attribute_selector(&mut self, attribute_selector: &mut AttributeSelector) {
|
||||
attribute_selector.visit_mut_children_with(self);
|
||||
|
||||
if let Some(AttributeSelectorValue::Str(Str { value, span, .. })) =
|
||||
&attribute_selector.value
|
||||
{
|
||||
// A valid unquoted attribute value in CSS is any string of text that is not the
|
||||
// empty string, is not just a hyphen (-), consists of escaped characters and/or
|
||||
// characters matching [-_a-zA-Z0-9\u00A0-\u10FFFF] entirely, and doesn’t start
|
||||
// with a digit or a hyphen followed by a digit.
|
||||
|
||||
// is any string of text that is not the empty string, is not just a hyphen (-)
|
||||
if value.is_empty() || value == "-" {
|
||||
return;
|
||||
}
|
||||
|
||||
let chars = value.chars();
|
||||
let mut starts_with_hyphen = false;
|
||||
|
||||
for (idx, char) in chars.enumerate() {
|
||||
match char {
|
||||
'0'..='9' if idx == 0 || (starts_with_hyphen && idx == 1) => {
|
||||
return;
|
||||
}
|
||||
'-' => {
|
||||
if idx == 0 {
|
||||
starts_with_hyphen = true;
|
||||
}
|
||||
}
|
||||
_ if !matches!(char, '-' | '_' | 'a'..='z' | 'A'..='Z' | '0'..='9' | '\u{00a0}'..='\u{10FFFF}') =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
attribute_selector.value = Some(AttributeSelectorValue::Ident(Ident {
|
||||
span: *span,
|
||||
value: value.clone(),
|
||||
raw: value.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,14 @@ use swc_css_visit::VisitMutWith;
|
||||
|
||||
use self::compress::{
|
||||
angle::compress_angle, easing_function::compress_easing_function, empty::compress_empty,
|
||||
keyframes::compress_keyframes, time::compress_time,
|
||||
keyframes::compress_keyframes, selector::compress_selector, time::compress_time,
|
||||
};
|
||||
|
||||
mod compress;
|
||||
|
||||
pub fn minify(stylesheet: &mut Stylesheet) {
|
||||
stylesheet.visit_mut_with(&mut compress_empty());
|
||||
stylesheet.visit_mut_with(&mut compress_selector());
|
||||
stylesheet.visit_mut_with(&mut compress_keyframes());
|
||||
stylesheet.visit_mut_with(&mut compress_easing_function());
|
||||
stylesheet.visit_mut_with(&mut compress_time());
|
||||
|
@ -0,0 +1,166 @@
|
||||
a[color="blue"] {color:blue}
|
||||
a[color=""]{color:blue}
|
||||
a[color="-"]{color:blue}
|
||||
a[color="."] {color:blue}
|
||||
a[color=" "]{color:blue}
|
||||
a[color=" a "]{color:blue}
|
||||
a[color=" a"]{color:blue}
|
||||
a[color="a "]{color:blue}
|
||||
a[color="\22"]{color:blue}
|
||||
a[color="B\26 W\3F "]{color:blue}
|
||||
a[color="\47"]{color:blue}
|
||||
a[color="😂"]{color:blue}
|
||||
a[color="👩🦼"]{color:blue}
|
||||
a[color="1"]{color:blue}
|
||||
a[color="--"]{color:blue}
|
||||
a[color="-1"]{color:blue}
|
||||
a[color="-404"]{color:blue}
|
||||
a[color="-x"]{color:blue}
|
||||
a[ color= "blue" ] {color:blue}
|
||||
a[color="blue" i ] {color:blue}
|
||||
a[class="woop \
|
||||
woop woop"] {color:blue}
|
||||
a[class="woop_woop_woop"]{color:blue}
|
||||
h1[class=" *.js "] + *.js{color:blue}
|
||||
|
||||
h1::before {color:blue}
|
||||
h1::BEfoRE {color:blue}
|
||||
h1::after {color:blue}
|
||||
h1::first-letter {color:blue}
|
||||
h1::first-line {color:blue}
|
||||
|
||||
* {color:blue}
|
||||
*[hreflang|=en]{color:blue}
|
||||
*.warning{color:blue}
|
||||
*#myid{color:blue}
|
||||
*::before{content:"test";color:blue}
|
||||
*:hover{}
|
||||
*.class[hreflang|=en]{color:blue}
|
||||
|
||||
foo|*[hreflang|=en]{color:blue}
|
||||
*|*[hreflang|=en]{color:blue}
|
||||
|
||||
div {& *[hreflang|=en]{color:blue}}
|
||||
div {&*[hreflang|=en]{color:blue}}
|
||||
div {&div {color:blue}}
|
||||
div {&* {color:blue}}
|
||||
div {& {color:blue}}
|
||||
div {&*.class {color:blue}}
|
||||
|
||||
/* For example, the following selector matches any element that is being hovered or focused, regardless of its namespace. In particular, it is not limited to only matching elements in the default namespace that are being hovered or focused. */
|
||||
*|*:is(:hover, :focus) {color:blue}
|
||||
/* The following selector, however, represents only hovered or focused elements that are in the default namespace, because it uses an explicit universal selector within the :is() notation: */
|
||||
*|*:is(*:hover, *:focus) {color: blue}
|
||||
|
||||
div *:first-child{color:blue}
|
||||
|
||||
legend + * {color:blue}
|
||||
* + legend {color:blue}
|
||||
* + * {color:blue}
|
||||
|
||||
p:nth-child(1){color:blue}
|
||||
p:NTH-CHILD(1){color:blue}
|
||||
p:nth-child(2n + 1){color:blue}
|
||||
p:nth-child(2N + 1){color:blue}
|
||||
p:nth-child(even){color:blue}
|
||||
p:nth-child(EVEN){color:blue}
|
||||
p:nth-child(2n){color:blue}
|
||||
p:nth-child(1){color: blue}
|
||||
p:nth-child(0n+1){color: blue}
|
||||
p:nth-child(-0n+1){color: blue}
|
||||
p:nth-child(+0n+1) {color:blue}
|
||||
p:nth-child(0n+5){color: blue}
|
||||
p:nth-child(0n-5){color: blue}
|
||||
p:nth-child(+0n-5){color: blue}
|
||||
p:nth-child(-0n-5){color: blue}
|
||||
p:nth-child(-1n+3) {color: blue}
|
||||
p:nth-child(+1n+3) {color: blue}
|
||||
p:nth-child(1n+3) {color: blue}
|
||||
p:nth-child(n+3) {color: blue}
|
||||
p:nth-child(-n+3) {color: blue}
|
||||
p:nth-child(+n+3) {color: blue}
|
||||
p:nth-child(+n-3) {color: blue}
|
||||
p:nth-child(0n-3) {color: blue}
|
||||
p:nth-child(-0n-3) {color: blue}
|
||||
p:nth-child(+0n-3) {color: blue}
|
||||
p:nth-child(n+0) {color: blue}
|
||||
p:nth-child(+n+0) {color: blue}
|
||||
p:nth-child(-n+0) {color: blue}
|
||||
p:nth-child(5n+0) {color: blue}
|
||||
p:nth-child(-5n+0) {color: blue}
|
||||
p:nth-child(+5n+0) {color: blue}
|
||||
p:nth-child(-2n+1) {color:blue}
|
||||
p:nth-child(n - 0){color:blue}
|
||||
p:nth-child(n + 0){color:blue}
|
||||
p:nth-child(-n + 0){color:blue}
|
||||
p:nth-child(+n + 0){color:blue}
|
||||
p:nth-of-type(2n+1) {color:blue}
|
||||
p:nth-last-col(2n+1) {color:blue}
|
||||
p:nth-col(2n+1) {color:blue}
|
||||
p:nth-child(1n - 2){color:blue}
|
||||
p:nth-child(1n + 2){color:blue}
|
||||
p:nth-child(-1n + 2){color:blue}
|
||||
p:nth-child(1n){color:blue}
|
||||
p:nth-child(-1n){color:blue}
|
||||
p:nth-child(+1n){color:blue}
|
||||
p:nth-child(0n+5){color:blue}
|
||||
p:nth-child(1n+0){color:blue}
|
||||
p:nth-child(n+0){color:blue}
|
||||
p:nth-child(-n+ 6){color:blue}
|
||||
p:nth-child(2n-1){color:blue}
|
||||
p:nth-child(2n+5){color:blue}
|
||||
p:nth-child(2n+4){color:blue}
|
||||
p:nth-child(2n+3){color:blue}
|
||||
p:nth-child(2n+2){color:blue}
|
||||
p:nth-child(2n+1){color:blue}
|
||||
p:nth-child(2n-1){color:blue}
|
||||
p:nth-child(2n-2){color:blue}
|
||||
p:nth-child(2n-3){color:blue}
|
||||
p:nth-child(2n-4){color:blue}
|
||||
p:nth-child(2n-5){color:blue}
|
||||
p:nth-child(2n-6){color:blue}
|
||||
p:nth-child(2n-7){color:blue}
|
||||
p:nth-child(2n-8){color:blue}
|
||||
p:nth-child(2n-9){color:blue}
|
||||
p:nth-child(2n-10){color:blue}
|
||||
p:nth-child(2n-0){color:blue}
|
||||
p:nth-child(2n+10){color:blue}
|
||||
|
||||
p:nth-child(n+8):nth-child(-n+15){color:blue}
|
||||
p:nth-last-of-type(2n + 2){color:blue}
|
||||
body>h2:not(:first-of-type):not(:last-of-type){color:blue}
|
||||
|
||||
.class:nth-child(1){color:red}
|
||||
.class:nth-child(+1){color:red}
|
||||
|
||||
/* No */
|
||||
.class:nth-child(-1){color:red}
|
||||
|
||||
.class:nth-last-child(1){color:red}
|
||||
.class:nth-last-child(+1){color:red}
|
||||
|
||||
/* No */
|
||||
.class:nth-last-child(-1){color:red}
|
||||
|
||||
.class:nth-of-type(1){color:red}
|
||||
.class:nth-of-type(+1){color:red}
|
||||
|
||||
/* No */
|
||||
.class:nth-of-type(-1){color:red}
|
||||
|
||||
|
||||
.class:nth-last-of-type(1){color:red}
|
||||
.class:nth-last-of-type(+1){color:red}
|
||||
|
||||
/* No */
|
||||
.class:nth-last-of-type(-1){color:red}
|
||||
|
||||
.foo.foo.foo{}
|
||||
.foo.foo.foo{color:red}
|
||||
.class#id#id {color:red}
|
||||
#id#id.class {color:red}
|
||||
#id.class#id {color:red}
|
||||
#id#id#id {color:red}
|
||||
[attr][attr][attr] {color:red}
|
||||
[attr].class[attr].class[attr] {color:red}
|
||||
.foo.foo.foo#id#id#id[attr][attr][attr]{}
|
1
crates/swc_css_minifier/tests/fixture/compress-selector/output.min.css
vendored
Normal file
1
crates/swc_css_minifier/tests/fixture/compress-selector/output.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
a[color=blue]{color:blue}a[color=""]{color:blue}a[color="-"]{color:blue}a[color="."]{color:blue}a[color=" "]{color:blue}a[color=" a "]{color:blue}a[color=" a"]{color:blue}a[color="a "]{color:blue}a[color='"']{color:blue}a[color="B&W?"]{color:blue}a[color=G]{color:blue}a[color=😂]{color:blue}a[color=👩🦼]{color:blue}a[color="1"]{color:blue}a[color=--]{color:blue}a[color="-1"]{color:blue}a[color="-404"]{color:blue}a[color=-x]{color:blue}a[color=blue]{color:blue}a[color=blue i]{color:blue}a[class="woop woop woop"]{color:blue}a[class=woop_woop_woop]{color:blue}h1[class=" *.js "]+.js{color:blue}h1:before{color:blue}h1:BEfoRE{color:blue}h1:after{color:blue}h1:first-letter{color:blue}h1:first-line{color:blue}{color:blue}[hreflang|=en]{color:blue}.warning{color:blue}#myid{color:blue}:before{content:"test";color:blue}.class[hreflang|=en]{color:blue}foo|*[hreflang|=en]{color:blue}*|*[hreflang|=en]{color:blue}div{& [hreflang|=en]{color:blue}}div{&[hreflang|=en]{color:blue}}div{&div{color:blue}}div{&{color:blue}}div{&{color:blue}}div{&.class{color:blue}}*|*:is(:hover, :focus){color:blue}*|*:is(*:hover, *:focus){color:blue}div :first-child{color:blue}legend+{color:blue}+legend{color:blue}+{color:blue}p:first-child{color:blue}p:first-child{color:blue}p:nth-child(odd){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(2n){color:blue}p:nth-child(2n){color:blue}p:first-child{color:blue}p:first-child{color:blue}p:first-child{color:blue}p:first-child{color:blue}p:nth-child(5){color:blue}p:nth-child(-5){color:blue}p:nth-child(-5){color:blue}p:nth-child(-5){color:blue}p:nth-child(-n+3){color:blue}p:nth-child(n+3){color:blue}p:nth-child(n+3){color:blue}p:nth-child(n+3){color:blue}p:nth-child(-n+3){color:blue}p:nth-child(n+3){color:blue}p:nth-child(n-3){color:blue}p:nth-child(-3){color:blue}p:nth-child(-3){color:blue}p:nth-child(-3){color:blue}p:nth-child(n){color:blue}p:nth-child(n){color:blue}p:nth-child(-n){color:blue}p:nth-child(5n){color:blue}p:nth-child(-5n){color:blue}p:nth-child(5n){color:blue}p:nth-child(-2n+1){color:blue}p:nth-child(n){color:blue}p:nth-child(n){color:blue}p:nth-child(-n){color:blue}p:nth-child(n){color:blue}p:nth-of-type(odd){color:blue}p:nth-last-col(odd){color:blue}p:nth-col(odd){color:blue}p:nth-child(n-2){color:blue}p:nth-child(n+2){color:blue}p:nth-child(-n+2){color:blue}p:nth-child(n){color:blue}p:nth-child(-n){color:blue}p:nth-child(n){color:blue}p:nth-child(5){color:blue}p:nth-child(n){color:blue}p:nth-child(n){color:blue}p:nth-child(-n+6){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n+5){color:blue}p:nth-child(2n+4){color:blue}p:nth-child(2n+3){color:blue}p:nth-child(2n+2){color:blue}p:nth-child(odd){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(odd){color:blue}p:nth-child(2n){color:blue}p:nth-child(2n){color:blue}p:nth-child(2n+10){color:blue}p:nth-child(n+8):nth-child(-n+15){color:blue}p:nth-last-of-type(2n+2){color:blue}body>h2:not(:first-of-type):not(:last-of-type){color:blue}.class:first-child{color:red}.class:first-child{color:red}.class:nth-child(-1){color:red}.class:last-child{color:red}.class:last-child{color:red}.class:nth-last-child(-1){color:red}.class:first-of-type{color:red}.class:first-of-type{color:red}.class:nth-of-type(-1){color:red}.class:last-of-type{color:red}.class:last-of-type{color:red}.class:nth-last-of-type(-1){color:red}.foo.foo.foo{color:red}.class#id#id{color:red}#id#id.class{color:red}#id.class#id{color:red}#id#id#id{color:red}[attr][attr][attr]{color:red}[attr].class[attr].class[attr]{color:red}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user