mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
fix(css/prefixer): Handle at-rules and don't generate unnecessary prefixes (#4318)
This commit is contained in:
parent
8959774a78
commit
ac4f14ad7b
@ -437,11 +437,10 @@ macro_rules! str_to_ident {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Prefixer {
|
struct Prefixer {
|
||||||
in_stylesheet: bool,
|
|
||||||
in_media_query_list: bool,
|
|
||||||
in_keyframe_block: bool,
|
in_keyframe_block: bool,
|
||||||
simple_block: Option<SimpleBlock>,
|
|
||||||
added_rules: Vec<Rule>,
|
added_rules: Vec<Rule>,
|
||||||
|
rule_prefix: Option<Prefix>,
|
||||||
|
simple_block: Option<SimpleBlock>,
|
||||||
added_declarations: Vec<Declaration>,
|
added_declarations: Vec<Declaration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,6 +497,7 @@ impl Prefixer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Prefix {
|
pub enum Prefix {
|
||||||
Webkit,
|
Webkit,
|
||||||
Moz,
|
Moz,
|
||||||
@ -506,15 +506,130 @@ pub enum Prefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for Prefixer {
|
impl VisitMut for Prefixer {
|
||||||
|
fn visit_mut_stylesheet(&mut self, n: &mut Stylesheet) {
|
||||||
|
let mut new = vec![];
|
||||||
|
|
||||||
|
for mut n in take(&mut n.rules) {
|
||||||
|
n.visit_mut_children_with(self);
|
||||||
|
|
||||||
|
new.append(&mut self.added_rules);
|
||||||
|
new.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
n.rules = new;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO handle declarations in `@media`/`@support`
|
// TODO handle declarations in `@media`/`@support`
|
||||||
// TODO handle `@viewport`
|
// TODO don't generate wrong prefixes in prefixed selectors and at-rules
|
||||||
// TODO handle `@keyframes`
|
fn visit_mut_at_rule(&mut self, n: &mut AtRule) {
|
||||||
|
let mut added_at_rules = vec![];
|
||||||
|
|
||||||
|
// TODO avoid duplicate
|
||||||
|
match &n.name {
|
||||||
|
AtRuleName::Ident(Ident { value, .. })
|
||||||
|
if value.as_ref().eq_ignore_ascii_case("viewport") =>
|
||||||
|
{
|
||||||
|
let new_name = AtRuleName::Ident(Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
value: "-ms-viewport".into(),
|
||||||
|
raw: "-ms-viewport".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
added_at_rules.push((
|
||||||
|
Some(Prefix::Ms),
|
||||||
|
AtRule {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: new_name,
|
||||||
|
prelude: n.prelude.clone(),
|
||||||
|
block: n.block.clone(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let new_name = AtRuleName::Ident(Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
value: "-o-viewport".into(),
|
||||||
|
raw: "-o-viewport".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
added_at_rules.push((
|
||||||
|
Some(Prefix::O),
|
||||||
|
AtRule {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: new_name,
|
||||||
|
prelude: n.prelude.clone(),
|
||||||
|
block: n.block.clone(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
AtRuleName::Ident(Ident { value, .. })
|
||||||
|
if value.as_ref().eq_ignore_ascii_case("keyframes") =>
|
||||||
|
{
|
||||||
|
let new_name = AtRuleName::Ident(Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
value: "-webkit-keyframes".into(),
|
||||||
|
raw: "-webkit-keyframes".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
added_at_rules.push((
|
||||||
|
Some(Prefix::Webkit),
|
||||||
|
AtRule {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: new_name,
|
||||||
|
prelude: n.prelude.clone(),
|
||||||
|
block: n.block.clone(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let new_name = AtRuleName::Ident(Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
value: "-moz-keyframes".into(),
|
||||||
|
raw: "-moz-keyframes".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
added_at_rules.push((
|
||||||
|
Some(Prefix::Moz),
|
||||||
|
AtRule {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: new_name,
|
||||||
|
prelude: n.prelude.clone(),
|
||||||
|
block: n.block.clone(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
let new_name = AtRuleName::Ident(Ident {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
value: "-o-keyframes".into(),
|
||||||
|
raw: "-o-keyframes".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
added_at_rules.push((
|
||||||
|
Some(Prefix::O),
|
||||||
|
AtRule {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: new_name,
|
||||||
|
prelude: n.prelude.clone(),
|
||||||
|
block: n.block.clone(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.visit_mut_children_with(self);
|
||||||
|
|
||||||
|
for mut rule in take(&mut added_at_rules) {
|
||||||
|
let old_rule_prefix = self.rule_prefix.clone();
|
||||||
|
|
||||||
|
self.rule_prefix = rule.0;
|
||||||
|
|
||||||
|
rule.1.visit_mut_children_with(self);
|
||||||
|
|
||||||
|
self.added_rules.push(Rule::AtRule(rule.1));
|
||||||
|
self.rule_prefix = old_rule_prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_mut_media_query_list(&mut self, media_query_list: &mut MediaQueryList) {
|
fn visit_mut_media_query_list(&mut self, media_query_list: &mut MediaQueryList) {
|
||||||
let old_in_media_query_list = self.in_media_query_list;
|
|
||||||
|
|
||||||
self.in_media_query_list = true;
|
|
||||||
|
|
||||||
let mut new = vec![];
|
let mut new = vec![];
|
||||||
|
|
||||||
for mut n in take(&mut media_query_list.queries) {
|
for mut n in take(&mut media_query_list.queries) {
|
||||||
@ -560,8 +675,6 @@ impl VisitMut for Prefixer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
media_query_list.queries = new;
|
media_query_list.queries = new;
|
||||||
|
|
||||||
self.in_media_query_list = old_in_media_query_list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_mut_keyframe_block(&mut self, n: &mut KeyframeBlock) {
|
fn visit_mut_keyframe_block(&mut self, n: &mut KeyframeBlock) {
|
||||||
@ -574,14 +687,9 @@ impl VisitMut for Prefixer {
|
|||||||
self.in_keyframe_block = old_in_keyframe_block;
|
self.in_keyframe_block = old_in_keyframe_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO don't generate wrong prefixes in prefixed selectors
|
|
||||||
fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) {
|
fn visit_mut_qualified_rule(&mut self, n: &mut QualifiedRule) {
|
||||||
n.visit_mut_children_with(self);
|
n.visit_mut_children_with(self);
|
||||||
|
|
||||||
if !self.in_stylesheet {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let QualifiedRulePrelude::Invalid(_) = n.prelude {
|
if let QualifiedRulePrelude::Invalid(_) = n.prelude {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -700,23 +808,28 @@ impl VisitMut for Prefixer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_mut_stylesheet(&mut self, n: &mut Stylesheet) {
|
fn visit_mut_simple_block(&mut self, simple_block: &mut SimpleBlock) {
|
||||||
let old_in_stylesheet = self.in_stylesheet;
|
let old_simple_block = self.simple_block.clone();
|
||||||
|
|
||||||
self.in_stylesheet = true;
|
self.simple_block = Some(simple_block.clone());
|
||||||
|
|
||||||
let mut new = vec![];
|
let mut new = vec![];
|
||||||
|
|
||||||
for mut n in take(&mut n.rules) {
|
for mut n in take(&mut simple_block.value) {
|
||||||
n.visit_mut_children_with(self);
|
n.visit_mut_children_with(self);
|
||||||
|
|
||||||
new.append(&mut self.added_rules);
|
new.extend(
|
||||||
|
self.added_declarations
|
||||||
|
.drain(..)
|
||||||
|
.map(DeclarationOrAtRule::Declaration)
|
||||||
|
.map(ComponentValue::DeclarationOrAtRule),
|
||||||
|
);
|
||||||
new.push(n);
|
new.push(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
n.rules = new;
|
simple_block.value = new;
|
||||||
|
|
||||||
self.in_stylesheet = old_in_stylesheet;
|
self.simple_block = old_simple_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_mut_declaration(&mut self, n: &mut Declaration) {
|
fn visit_mut_declaration(&mut self, n: &mut Declaration) {
|
||||||
@ -839,52 +952,82 @@ impl VisitMut for Prefixer {
|
|||||||
// TODO avoid duplication insert
|
// TODO avoid duplication insert
|
||||||
macro_rules! add_declaration {
|
macro_rules! add_declaration {
|
||||||
($prefix:expr,$name:expr) => {{
|
($prefix:expr,$name:expr) => {{
|
||||||
let need_prefix = match self.get_declaration_by_name($name) {
|
// Use only specific prefix in prefixed at-rules or rule, i.e.
|
||||||
Some(_) => false,
|
// don't use `-moz` prefix for properties in `@-webkit-keyframes` at-rule
|
||||||
_ => true,
|
let need_prefix = if let Some(rule_prefix) = &self.rule_prefix {
|
||||||
|
if $prefix != *rule_prefix {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
if need_prefix {
|
if need_prefix {
|
||||||
let name = DeclarationName::Ident(Ident {
|
// Check we don't have prefixed property
|
||||||
span: DUMMY_SP,
|
let need_prefix = match self.get_declaration_by_name($name) {
|
||||||
value: $name.into(),
|
Some(_) => false,
|
||||||
raw: $name.into(),
|
_ => true,
|
||||||
});
|
|
||||||
let new_value = match $prefix {
|
|
||||||
Prefix::Webkit => webkit_value.clone(),
|
|
||||||
Prefix::Moz => moz_value.clone(),
|
|
||||||
Prefix::O => o_value.clone(),
|
|
||||||
Prefix::Ms => ms_value.clone(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.added_declarations.push(Declaration {
|
if need_prefix {
|
||||||
span: n.span,
|
let name = DeclarationName::Ident(Ident {
|
||||||
name,
|
span: DUMMY_SP,
|
||||||
value: new_value,
|
value: $name.into(),
|
||||||
important: n.important.clone(),
|
raw: $name.into(),
|
||||||
});
|
});
|
||||||
|
let new_value = match $prefix {
|
||||||
|
Prefix::Webkit => webkit_value.clone(),
|
||||||
|
Prefix::Moz => moz_value.clone(),
|
||||||
|
Prefix::O => o_value.clone(),
|
||||||
|
Prefix::Ms => ms_value.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.added_declarations.push(Declaration {
|
||||||
|
span: n.span,
|
||||||
|
name,
|
||||||
|
value: new_value,
|
||||||
|
important: n.important.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($prefix:expr,$name:expr,$value:expr) => {{
|
($prefix:expr,$name:expr,$value:expr) => {{
|
||||||
let need_prefix = match self.get_declaration_by_name($name) {
|
// Use only specific prefix in prefixed at-rules or rule, i.e.
|
||||||
Some(_) => false,
|
// don't use `-moz` prefix for properties in `@-webkit-keyframes` at-rule
|
||||||
_ => true,
|
let need_prefix = if let Some(rule_prefix) = &self.rule_prefix {
|
||||||
|
if $prefix != *rule_prefix {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
if need_prefix {
|
if need_prefix {
|
||||||
let name = DeclarationName::Ident(Ident {
|
// Check we don't have prefixed property
|
||||||
span: DUMMY_SP,
|
let need_prefix = match self.get_declaration_by_name($name) {
|
||||||
value: $name.into(),
|
Some(_) => false,
|
||||||
raw: $name.into(),
|
_ => true,
|
||||||
});
|
};
|
||||||
|
|
||||||
self.added_declarations.push(Declaration {
|
if need_prefix {
|
||||||
span: n.span,
|
let name = DeclarationName::Ident(Ident {
|
||||||
name,
|
span: DUMMY_SP,
|
||||||
value: $value,
|
value: $name.into(),
|
||||||
important: n.important.clone(),
|
raw: $name.into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.added_declarations.push(Declaration {
|
||||||
|
span: n.span,
|
||||||
|
name,
|
||||||
|
value: $value,
|
||||||
|
important: n.important.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -2249,28 +2392,4 @@ impl VisitMut for Prefixer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_mut_simple_block(&mut self, simple_block: &mut SimpleBlock) {
|
|
||||||
let old_simple_block = self.simple_block.clone();
|
|
||||||
|
|
||||||
self.simple_block = Some(simple_block.clone());
|
|
||||||
|
|
||||||
let mut new = vec![];
|
|
||||||
|
|
||||||
for mut n in take(&mut simple_block.value) {
|
|
||||||
n.visit_mut_children_with(self);
|
|
||||||
|
|
||||||
new.extend(
|
|
||||||
self.added_declarations
|
|
||||||
.drain(..)
|
|
||||||
.map(DeclarationOrAtRule::Declaration)
|
|
||||||
.map(ComponentValue::DeclarationOrAtRule),
|
|
||||||
);
|
|
||||||
new.push(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
simple_block.value = new;
|
|
||||||
|
|
||||||
self.simple_block = old_simple_block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,75 @@
|
|||||||
|
@-webkit-keyframes anim {
|
||||||
|
from {
|
||||||
|
top: -webkit-calc(10% + 10px);
|
||||||
|
top: -moz-calc(10% + 10px);
|
||||||
|
top: calc(10% + 10px);
|
||||||
|
-webkit-transform: rotate(10deg);
|
||||||
|
transform: rotate(10deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
top: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -moz-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: -webkit-calc(10%);
|
||||||
|
top: -moz-calc(10%);
|
||||||
|
top: calc(10%);
|
||||||
|
-webkit-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-moz-keyframes anim {
|
||||||
|
from {
|
||||||
|
top: -webkit-calc(10% + 10px);
|
||||||
|
top: -moz-calc(10% + 10px);
|
||||||
|
top: calc(10% + 10px);
|
||||||
|
-moz-transform: rotate(10deg);
|
||||||
|
transform: rotate(10deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
top: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -moz-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: -webkit-calc(10%);
|
||||||
|
top: -moz-calc(10%);
|
||||||
|
top: calc(10%);
|
||||||
|
-moz-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-o-keyframes anim {
|
||||||
|
from {
|
||||||
|
top: -webkit-calc(10% + 10px);
|
||||||
|
top: -moz-calc(10% + 10px);
|
||||||
|
top: calc(10% + 10px);
|
||||||
|
-o-transform: rotate(10deg);
|
||||||
|
transform: rotate(10deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
top: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -moz-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: -webkit-calc(10%);
|
||||||
|
top: -moz-calc(10%);
|
||||||
|
top: calc(10%);
|
||||||
|
-o-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes anim {
|
@keyframes anim {
|
||||||
from {
|
from {
|
||||||
top: -webkit-calc(10% + 10px);
|
top: -webkit-calc(10% + 10px);
|
||||||
@ -26,9 +98,36 @@
|
|||||||
transform: rotate(0);
|
transform: rotate(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@-webkit-keyframes inside {}
|
||||||
|
@-moz-keyframes inside {}
|
||||||
|
@-o-keyframes inside {}
|
||||||
@media screen {
|
@media screen {
|
||||||
@keyframes inside {}
|
@keyframes inside {}
|
||||||
}
|
}
|
||||||
|
@-webkit-keyframes spaces {
|
||||||
|
from {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-moz-keyframes spaces {
|
||||||
|
from {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-o-keyframes spaces {
|
||||||
|
from {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes spaces {
|
@keyframes spaces {
|
||||||
from {
|
from {
|
||||||
color: black;
|
color: black;
|
||||||
|
@ -34,6 +34,24 @@ em {
|
|||||||
-moz-transform: rotateZ(45deg);
|
-moz-transform: rotateZ(45deg);
|
||||||
transform: rotateZ(45deg);
|
transform: rotateZ(45deg);
|
||||||
}
|
}
|
||||||
|
@-webkit-keyframes anim {
|
||||||
|
from {
|
||||||
|
-webkit-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-moz-keyframes anim {
|
||||||
|
from {
|
||||||
|
-moz-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-o-keyframes anim {
|
||||||
|
from {
|
||||||
|
-o-transform: rotate(90deg);
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes anim {
|
@keyframes anim {
|
||||||
from {
|
from {
|
||||||
-webkit-transform: rotate(90deg);
|
-webkit-transform: rotate(90deg);
|
||||||
|
4
crates/swc_css_prefixer/tests/fixture/viewport/input.css
Normal file
4
crates/swc_css_prefixer/tests/fixture/viewport/input.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@viewport {
|
||||||
|
min-width: 640px;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
12
crates/swc_css_prefixer/tests/fixture/viewport/output.css
Normal file
12
crates/swc_css_prefixer/tests/fixture/viewport/output.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@-ms-viewport{
|
||||||
|
min-width: 640px;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
@-o-viewport{
|
||||||
|
min-width: 640px;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
@viewport{
|
||||||
|
min-width: 640px;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
@ -66,12 +66,40 @@
|
|||||||
-webkit-writing-mode: sideways-lr;
|
-webkit-writing-mode: sideways-lr;
|
||||||
writing-mode: sideways-lr;
|
writing-mode: sideways-lr;
|
||||||
}
|
}
|
||||||
|
@-ms-viewport{
|
||||||
|
-ms-writing-mode: rl-tb;
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
@-o-viewport{
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
@viewport{
|
@viewport{
|
||||||
-webkit-writing-mode: horizontal-tb;
|
-webkit-writing-mode: horizontal-tb;
|
||||||
-ms-writing-mode: rl-tb;
|
-ms-writing-mode: rl-tb;
|
||||||
writing-mode: horizontal-tb;
|
writing-mode: horizontal-tb;
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
@-webkit-keyframes test {
|
||||||
|
100% {
|
||||||
|
-webkit-writing-mode: horizontal-tb;
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-moz-keyframes test {
|
||||||
|
100% {
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-o-keyframes test {
|
||||||
|
100% {
|
||||||
|
writing-mode: horizontal-tb;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@keyframes test {
|
@keyframes test {
|
||||||
100% {
|
100% {
|
||||||
-webkit-writing-mode: horizontal-tb;
|
-webkit-writing-mode: horizontal-tb;
|
||||||
|
Loading…
Reference in New Issue
Block a user