//! Lightning CSS is a CSS parser, transformer, and minifier based on the //! [cssparser](https://github.com/servo/rust-cssparser) crate used in Firefox. //! It supports fully parsing all CSS rules, properties, and values into normalized //! structures exactly how a browser would. Once parsed, the CSS can be transformed //! to add or remove vendor prefixes, or lower syntax for older browsers as appropriate. //! The style sheet can also be minified to merge longhand properties into shorthands, //! merge adjacent rules, reduce `calc()` expressions, and more. Finally, the style sheet //! can be printed back to CSS syntax, either minified to remove whitespace and compress //! the output as much as possible, or pretty printed. //! //! The [StyleSheet](stylesheet::StyleSheet) struct is the main entrypoint for Lightning CSS, //! and supports parsing and transforming entire CSS files. You can also parse and manipulate //! individual CSS [rules](rules), [properties](properties), or [values](values). The [bundler](bundler) //! module also can be used to combine a CSS file and all of its dependencies together into a single //! style sheet. See the individual module documentation for more details and examples. #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(feature = "bundler")] #[cfg_attr(docsrs, doc(cfg(feature = "bundler")))] pub mod bundler; mod compat; mod context; pub mod css_modules; pub mod declaration; pub mod dependencies; pub mod error; mod logical; mod macros; pub mod media_query; mod parser; mod prefixes; pub mod printer; pub mod properties; pub mod rules; pub mod selector; pub mod stylesheet; pub mod targets; pub mod traits; pub mod values; pub mod vendor_prefix; #[cfg(feature = "visitor")] #[cfg_attr(docsrs, doc(cfg(feature = "visitor")))] pub mod visitor; #[cfg(feature = "serde")] mod serialization; #[cfg(test)] mod tests { use crate::css_modules::{CssModuleExport, CssModuleExports, CssModuleReference, CssModuleReferences}; use crate::dependencies::Dependency; use crate::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterErrorKind, SelectorError}; use crate::properties::custom::Token; use crate::properties::Property; use crate::rules::CssRule; use crate::rules::Location; use crate::stylesheet::*; use crate::targets::Browsers; use crate::traits::{Parse, ToCss}; use crate::values::color::CssColor; use crate::vendor_prefix::VendorPrefix; use cssparser::SourceLocation; use indoc::indoc; use std::collections::HashMap; fn test(source: &str, expected: &str) { test_with_options(source, expected, ParserOptions::default()) } fn test_with_options<'i, 'o>(source: &'i str, expected: &'i str, options: ParserOptions<'o, 'i>) { let mut stylesheet = StyleSheet::parse(&source, options).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); } fn minify_test(source: &str, expected: &str) { let mut stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet .to_css(PrinterOptions { minify: true, ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); } fn prefix_test(source: &str, expected: &str, targets: Browsers) { let mut stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); stylesheet .minify(MinifyOptions { targets: Some(targets), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet .to_css(PrinterOptions { targets: Some(targets), ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); } fn attr_test(source: &str, expected: &str, minify: bool, targets: Option) { let mut attr = StyleAttribute::parse(source, ParserOptions::default()).unwrap(); attr.minify(MinifyOptions { targets, ..MinifyOptions::default() }); let res = attr .to_css(PrinterOptions { targets, minify, ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); } fn nesting_test(source: &str, expected: &str) { let targets = Some(Browsers { chrome: Some(95 << 16), ..Browsers::default() }); let mut stylesheet = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet .minify(MinifyOptions { targets, ..MinifyOptions::default() }) .unwrap(); let res = stylesheet .to_css(PrinterOptions { targets, ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); } fn nesting_test_no_targets(source: &str, expected: &str) { let mut stylesheet = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); } fn css_modules_test<'i>( source: &'i str, expected: &str, expected_exports: CssModuleExports, expected_references: CssModuleReferences, config: crate::css_modules::Config<'i>, ) { let mut stylesheet = StyleSheet::parse( &source, ParserOptions { filename: "test.css".into(), css_modules: Some(config), ..ParserOptions::default() }, ) .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); assert_eq!(res.exports.unwrap(), expected_exports); assert_eq!(res.references.unwrap(), expected_references); } fn custom_media_test(source: &str, expected: &str) { let mut stylesheet = StyleSheet::parse( &source, ParserOptions { custom_media: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet .minify(MinifyOptions { targets: Some(Browsers { chrome: Some(95 << 16), ..Browsers::default() }), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); } fn error_test(source: &str, error: ParserError) { let res = StyleSheet::parse(&source, ParserOptions::default()); match res { Ok(_) => unreachable!(), Err(e) => assert_eq!(e.kind, error), } } fn nesting_error_test(source: &str, error: ParserError) { let res = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ); match res { Ok(_) => unreachable!(), Err(e) => assert_eq!(e.kind, error), } } macro_rules! map( { $($key:expr => $name:literal $(referenced: $referenced: literal)? $($value:literal $(global: $global: literal)? $(from $from:literal)?)*),* } => { { #[allow(unused_mut)] let mut m = HashMap::new(); $( #[allow(unused_mut)] let mut v = Vec::new(); #[allow(unused_macros)] macro_rules! insert { ($local:literal from $specifier:literal) => { v.push(CssModuleReference::Dependency { name: $local.into(), specifier: $specifier.into() }); }; ($local:literal global: $is_global: literal) => { v.push(CssModuleReference::Global { name: $local.into() }); }; ($local:literal) => { v.push(CssModuleReference::Local { name: $local.into() }); }; } $( insert!($value $(global: $global)? $(from $from)?); )* macro_rules! is_referenced { ($ref: literal) => { $ref }; () => { false }; } m.insert($key.into(), CssModuleExport { name: $name.into(), composes: v, is_referenced: is_referenced!($($referenced)?) }); )* m } }; ); #[test] pub fn test_border_spacing() { minify_test( r#" .foo { border-spacing: 0px; } "#, indoc! {".foo{border-spacing:0}" }, ); minify_test( r#" .foo { border-spacing: 0px 0px; } "#, indoc! {".foo{border-spacing:0}" }, ); minify_test( r#" .foo { border-spacing: 12px 0px; } "#, indoc! {".foo{border-spacing:12px 0}" }, ); minify_test( r#" .foo { border-spacing: calc(3px * 2) calc(5px * 0); } "#, indoc! {".foo{border-spacing:6px 0}" }, ); minify_test( r#" .foo { border-spacing: calc(3px * 2) max(0px, 8px); } "#, indoc! {".foo{border-spacing:6px 8px}" }, ); // TODO: The `` in border-spacing cannot have a negative value, // we may need to implement NonNegativeLength like Servo does. // Servo Code: https://github.com/servo/servo/blob/08bc2d53579c9ab85415d4363888881b91df073b/components/style/values/specified/length.rs#L875 // CSSWG issue: https://lists.w3.org/Archives/Public/www-style/2008Sep/0161.html // `border-spacing = ?` minify_test( r#" .foo { border-spacing: -20px; } "#, indoc! {".foo{border-spacing:-20px}" }, ); } #[test] pub fn test_border() { test( r#" .foo { border-left: 2px solid red; border-right: 2px solid red; border-bottom: 2px solid red; border-top: 2px solid red; } "#, indoc! {r#" .foo { border: 2px solid red; } "# }, ); test( r#" .foo { border-left-color: red; border-right-color: red; border-bottom-color: red; border-top-color: red; } "#, indoc! {r#" .foo { border-color: red; } "# }, ); test( r#" .foo { border-left-width: thin; border-right-width: thin; border-bottom-width: thin; border-top-width: thin; } "#, indoc! {r#" .foo { border-width: thin; } "# }, ); test( r#" .foo { border-left-style: dotted; border-right-style: dotted; border-bottom-style: dotted; border-top-style: dotted; } "#, indoc! {r#" .foo { border-style: dotted; } "# }, ); test( r#" .foo { border-left-width: thin; border-left-style: dotted; border-left-color: red; } "#, indoc! {r#" .foo { border-left: thin dotted red; } "# }, ); test( r#" .foo { border-left-width: thick; border-left: thin dotted red; } "#, indoc! {r#" .foo { border-left: thin dotted red; } "# }, ); test( r#" .foo { border-left-width: thick; border: thin dotted red; } "#, indoc! {r#" .foo { border: thin dotted red; } "# }, ); test( r#" .foo { border: thin dotted red; border-right-width: thick; } "#, indoc! {r#" .foo { border: thin dotted red; border-right-width: thick; } "# }, ); test( r#" .foo { border: thin dotted red; border-right: thick dotted red; } "#, indoc! {r#" .foo { border: thin dotted red; border-right-width: thick; } "# }, ); test( r#" .foo { border: thin dotted red; border-right-width: thick; border-right-style: solid; } "#, indoc! {r#" .foo { border: thin dotted red; border-right: thick solid red; } "# }, ); test( r#" .foo { border-top: thin dotted red; border-block-start: thick solid green; } "#, indoc! {r#" .foo { border-top: thin dotted red; border-block-start: thick solid green; } "# }, ); test( r#" .foo { border: thin dotted red; border-block-start-width: thick; border-left-width: medium; } "#, indoc! {r#" .foo { border: thin dotted red; border-block-start-width: thick; border-left-width: medium; } "# }, ); test( r#" .foo { border-block-start: thin dotted red; border-inline-end: thin dotted red; } "#, indoc! {r#" .foo { border-block-start: thin dotted red; border-inline-end: thin dotted red; } "# }, ); test( r#" .foo { border-block-start-width: thin; border-block-start-style: dotted; border-block-start-color: red; border-inline-end: thin dotted red; } "#, indoc! {r#" .foo { border-block-start: thin dotted red; border-inline-end: thin dotted red; } "# }, ); test( r#" .foo { border-block-start: thin dotted red; border-block-end: thin dotted red; } "#, indoc! {r#" .foo { border-block: thin dotted red; } "# }, ); minify_test( r#" .foo { border: none; } "#, indoc! {".foo{border:none}" }, ); minify_test(".foo { border-width: 0 0 1px; }", ".foo{border-width:0 0 1px}"); test( r#" .foo { border-block-width: 1px; border-inline-width: 1px; } "#, indoc! {r#" .foo { border-width: 1px; } "# }, ); test( r#" .foo { border-block-start-width: 1px; border-block-end-width: 1px; border-inline-start-width: 1px; border-inline-end-width: 1px; } "#, indoc! {r#" .foo { border-width: 1px; } "# }, ); test( r#" .foo { border-block-start-width: 1px; border-block-end-width: 1px; border-inline-start-width: 2px; border-inline-end-width: 2px; } "#, indoc! {r#" .foo { border-block-width: 1px; border-inline-width: 2px; } "# }, ); test( r#" .foo { border-block-start-width: 1px; border-block-end-width: 1px; border-inline-start-width: 2px; border-inline-end-width: 3px; } "#, indoc! {r#" .foo { border-block-width: 1px; border-inline-width: 2px 3px; } "# }, ); minify_test( ".foo { border-bottom: 1px solid var(--spectrum-global-color-gray-200)}", ".foo{border-bottom:1px solid var(--spectrum-global-color-gray-200)}", ); test( r#" .foo { border-width: 0; border-bottom: var(--test, 1px) solid; } "#, indoc! {r#" .foo { border-width: 0; border-bottom: var(--test, 1px) solid; } "# }, ); test( r#" .foo { border: 1px solid black; border-width: 1px 1px 0 0; } "#, indoc! {r#" .foo { border: 1px solid #000; border-width: 1px 1px 0 0; } "#}, ); test( r#" .foo { border-top: 1px solid black; border-bottom: 1px solid black; border-left: 2px solid black; border-right: 2px solid black; } "#, indoc! {r#" .foo { border: 1px solid #000; border-width: 1px 2px; } "#}, ); test( r#" .foo { border-top: 1px solid black; border-bottom: 1px solid black; border-left: 2px solid black; border-right: 1px solid black; } "#, indoc! {r#" .foo { border: 1px solid #000; border-left-width: 2px; } "#}, ); test( r#" .foo { border-top: 1px solid black; border-bottom: 1px solid black; border-left: 1px solid red; border-right: 1px solid red; } "#, indoc! {r#" .foo { border: 1px solid #000; border-color: #000 red; } "#}, ); test( r#" .foo { border-block-start: 1px solid black; border-block-end: 1px solid black; border-inline-start: 1px solid red; border-inline-end: 1px solid red; } "#, indoc! {r#" .foo { border: 1px solid #000; border-inline-color: red; } "#}, ); test( r#" .foo { border-block-start: 1px solid black; border-block-end: 1px solid black; border-inline-start: 2px solid black; border-inline-end: 2px solid black; } "#, indoc! {r#" .foo { border: 1px solid #000; border-inline-width: 2px; } "#}, ); test( r#" .foo { border-block-start: 1px solid black; border-block-end: 1px solid black; border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "#, indoc! {r#" .foo { border: 1px solid #000; border-inline: 2px solid red; } "#}, ); test( r#" .foo { border-block-start: 1px solid black; border-block-end: 1px solid black; border-inline-start: 2px solid red; border-inline-end: 3px solid red; } "#, indoc! {r#" .foo { border: 1px solid #000; border-inline-start: 2px solid red; border-inline-end: 3px solid red; } "#}, ); test( r#" .foo { border-block-start: 2px solid black; border-block-end: 1px solid black; border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "#, indoc! {r#" .foo { border: 2px solid red; border-block-start-color: #000; border-block-end: 1px solid #000; } "#}, ); test( r#" .foo { border-block-start: 2px solid red; border-block-end: 1px solid red; border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "#, indoc! {r#" .foo { border: 2px solid red; border-block-end-width: 1px; } "#}, ); test( r#" .foo { border-block-start: 2px solid red; border-block-end: 2px solid red; border-inline-start: 2px solid red; border-inline-end: 1px solid red; } "#, indoc! {r#" .foo { border: 2px solid red; border-inline-end-width: 1px; } "#}, ); test( r#" .foo { border: 1px solid currentColor; } "#, indoc! {r#" .foo { border: 1px solid; } "# }, ); minify_test( r#" .foo { border: 1px solid currentColor; } "#, ".foo{border:1px solid}", ); prefix_test( r#" .foo { border-block: 2px solid red; } "#, indoc! {r#" .foo { border-top: 2px solid red; border-bottom: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-block-start: 2px solid red; } "#, indoc! {r#" .foo { border-top: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-block-end: 2px solid red; } "#, indoc! {r#" .foo { border-bottom: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline: 2px solid red; } "#, indoc! {r#" .foo { border-left: 2px solid red; border-right: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-block-width: 2px; } "#, indoc! {r#" .foo { border-block-start-width: 2px; border-block-end-width: 2px; } "# }, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-block-width: 2px; } "#, indoc! {r#" .foo { border-block-width: 2px; } "# }, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid red; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: 2px solid red; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: 2px solid red; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start-width: 2px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left-width: 2px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right-width: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right-width: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-end: 2px solid red; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right: 2px solid red; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 2px solid red; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid red; border-inline-end: 5px solid green; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: 2px solid red; border-right: 5px solid green; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 5px solid green; border-right: 2px solid red; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 5px solid green; border-right: 2px solid red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid red; border-inline-end: 5px solid green; } .bar { border-inline-start: 1px dotted gray; border-inline-end: 1px solid black; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: 2px solid red; border-right: 5px solid green; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 5px solid green; border-right: 2px solid red; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 5px solid green; border-right: 2px solid red; } .bar:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: 1px dotted gray; border-right: 1px solid #000; } .bar:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 1px solid #000; border-right: 1px dotted gray; } .bar:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 1px solid #000; border-right: 1px dotted gray; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-width: 2px; } "#, indoc! {r#" .foo { border-left-width: 2px; border-right-width: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-width: 2px; } "#, indoc! {r#" .foo { border-left-width: 2px; border-right-width: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-style: solid; } "#, indoc! {r#" .foo { border-left-style: solid; border-right-style: solid; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-color: red; } "#, indoc! {r#" .foo { border-left-color: red; border-right-color: red; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-end: var(--test); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right: var(--test); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: var(--test); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: var(--test); } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: var(--start); border-inline-end: var(--end); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: var(--start); border-right: var(--end); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: var(--start); border-left: var(--end); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: var(--start); border-left: var(--end); } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); for prop in &[ "border-inline-start-color", "border-inline-end-color", "border-block-start-color", "border-block-end-color", "border-top-color", "border-bottom-color", "border-left-color", "border-right-color", "border-color", "border-block-color", "border-inline-color", ] { prefix_test( &format!( r#" .foo {{ {}: lab(40% 56.6 39); }} "#, prop ), &format!( indoc! {r#" .foo {{ {}: #b32323; {}: lab(40% 56.6 39); }} "#}, prop, prop ), Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } for prop in &[ "border", "border-inline", "border-block", "border-left", "border-right", "border-top", "border-bottom", "border-block-start", "border-block-end", "border-inline-start", "border-inline-end", ] { prefix_test( &format!( r#" .foo {{ {}: 2px solid lab(40% 56.6 39); }} "#, prop ), &format!( indoc! {r#" .foo {{ {}: 2px solid #b32323; {}: 2px solid lab(40% 56.6 39); }} "#}, prop, prop ), Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } for prop in &[ "border", "border-inline", "border-block", "border-left", "border-right", "border-top", "border-bottom", "border-block-start", "border-block-end", "border-inline-start", "border-inline-end", ] { prefix_test( &format!( r#" .foo {{ {}: var(--border-width) solid lab(40% 56.6 39); }} "#, prop ), &format!( indoc! {r#" .foo {{ {}: var(--border-width) solid #b32323; }} @supports (color: lab(0% 0 0)) {{ .foo {{ {}: var(--border-width) solid lab(40% 56.6 39); }} }} "#}, prop, prop ), Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } prefix_test( r#" .foo { border-inline-start-color: lab(40% 56.6 39); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left-color: #b32323; border-left-color: lab(40% 56.6 39); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right-color: #b32323; border-right-color: lab(40% 56.6 39); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right-color: #b32323; border-right-color: lab(40% 56.6 39); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-end-color: lab(40% 56.6 39); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right-color: #b32323; border-right-color: lab(40% 56.6 39); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left-color: #b32323; border-left-color: lab(40% 56.6 39); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left-color: #b32323; border-left-color: lab(40% 56.6 39); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start-color: lab(40% 56.6 39); border-inline-end-color: lch(50.998% 135.363 338); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left-color: #b32323; border-left-color: lab(40% 56.6 39); border-right-color: #ee00be; border-right-color: lch(50.998% 135.363 338); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left-color: #ee00be; border-left-color: lch(50.998% 135.363 338); border-right-color: #b32323; border-right-color: lab(40% 56.6 39); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left-color: #ee00be; border-left-color: lch(50.998% 135.363 338); border-right-color: #b32323; border-right-color: lab(40% 56.6 39); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start-color: lab(40% 56.6 39); border-inline-end-color: lch(50.998% 135.363 338); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left-color: #b32323; border-left-color: color(display-p3 .643308 .192455 .167712); border-left-color: lab(40% 56.6 39); border-right-color: #ee00be; border-right-color: color(display-p3 .972962 -.362078 .804206); border-right-color: lch(50.998% 135.363 338); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left-color: #ee00be; border-left-color: color(display-p3 .972962 -.362078 .804206); border-left-color: lch(50.998% 135.363 338); border-right-color: #b32323; border-right-color: color(display-p3 .643308 .192455 .167712); border-right-color: lab(40% 56.6 39); } "#}, Browsers { chrome: Some(8 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid lab(40% 56.6 39); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-left: 2px solid #b32323; border-left: 2px solid lab(40% 56.6 39); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: 2px solid #b32323; border-right: 2px solid lab(40% 56.6 39); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-right: 2px solid #b32323; border-right: 2px solid lab(40% 56.6 39); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-end: 2px solid lab(40% 56.6 39); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right: 2px solid #b32323; border-right: 2px solid lab(40% 56.6 39); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 2px solid #b32323; border-left: 2px solid lab(40% 56.6 39); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: 2px solid #b32323; border-left: 2px solid lab(40% 56.6 39); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-end: var(--border-width) solid lab(40% 56.6 39); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right: var(--border-width) solid #b32323; } @supports (color: lab(0% 0 0)) { .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { border-right: var(--border-width) solid lab(40% 56.6 39); } } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: var(--border-width) solid #b32323; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: var(--border-width) solid #b32323; } @supports (color: lab(0% 0 0)) { .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { border-left: var(--border-width) solid lab(40% 56.6 39); } } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "#, indoc! {r#" .foo { border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "# }, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-inline-start: 2px solid red; border-inline-end: 2px solid red; } "#, indoc! {r#" .foo { border-inline: 2px solid red; } "# }, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-width: 22px; border-width: max(2cqw, 22px); } "#, indoc! {r#" .foo { border-width: 22px; border-width: max(2cqw, 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-width: 22px; border-width: max(2cqw, 22px); } "#, indoc! {r#" .foo { border-width: max(2cqw, 22px); } "# }, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-color: #4263eb; border-color: color(display-p3 0 .5 1); } "#, indoc! {r#" .foo { border-color: #4263eb; border-color: color(display-p3 0 .5 1); } "# }, Browsers { chrome: Some(99 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-color: #4263eb; border-color: color(display-p3 0 .5 1); } "#, indoc! {r#" .foo { border-color: color(display-p3 0 .5 1); } "# }, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border: 1px solid #4263eb; border-color: color(display-p3 0 .5 1); } "#, indoc! {r#" .foo { border: 1px solid #4263eb; border-color: color(display-p3 0 .5 1); } "# }, Browsers { chrome: Some(99 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border: 1px solid #4263eb; border-color: color(display-p3 0 .5 1); } "#, indoc! {r#" .foo { border: 1px solid #4263eb; border-color: color(display-p3 0 .5 1); } "# }, Browsers { chrome: Some(99 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border: 1px solid #4263eb; border-color: color(display-p3 0 .5 1); } "#, indoc! {r#" .foo { border: 1px solid color(display-p3 0 .5 1); } "# }, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); } #[test] pub fn test_border_image() { test( r#" .foo { border-image: url(test.png) 60; } "#, indoc! {r#" .foo { border-image: url("test.png") 60; } "# }, ); test( r#" .foo { border-image: url(test.png) 60; border-image-source: url(foo.png); } "#, indoc! {r#" .foo { border-image: url("foo.png") 60; } "# }, ); test( r#" .foo { border-image-source: url(foo.png); border-image-slice: 10 40 10 40 fill; border-image-width: 10px; border-image-outset: 0; border-image-repeat: round round; } "#, indoc! {r#" .foo { border-image: url("foo.png") 10 40 fill / 10px round; } "# }, ); test( r#" .foo { border-image: url(foo.png) 60; border-image-source: var(--test); } "#, indoc! {r#" .foo { border-image: url("foo.png") 60; border-image-source: var(--test); } "# }, ); test( r#" .foo { -webkit-border-image: url("test.png") 60; } "#, indoc! {r#" .foo { -webkit-border-image: url("test.png") 60; } "# }, ); test( r#" .foo { -webkit-border-image: url("test.png") 60; border-image: url("test.png") 60; } "#, indoc! {r#" .foo { -webkit-border-image: url("test.png") 60; border-image: url("test.png") 60; } "# }, ); test( r#" .foo { -webkit-border-image: url("test.png") 60; border-image-source: url(foo.png); } "#, indoc! {r#" .foo { -webkit-border-image: url("test.png") 60; border-image-source: url("foo.png"); } "# }, ); test( r#" .foo { border: 1px solid red; border-image: url(test.png) 60; } "#, indoc! {r#" .foo { border: 1px solid red; border-image: url("test.png") 60; } "# }, ); test( r#" .foo { border-image: url(test.png) 60; border: 1px solid red; } "#, indoc! {r#" .foo { border: 1px solid red; } "# }, ); test( r#" .foo { border: 1px solid red; border-image: var(--border-image); } "#, indoc! {r#" .foo { border: 1px solid red; border-image: var(--border-image); } "# }, ); prefix_test( r#" .foo { border-image: url("test.png") 60; } "#, indoc! {r#" .foo { -webkit-border-image: url("test.png") 60; -moz-border-image: url("test.png") 60; -o-border-image: url("test.png") 60; border-image: url("test.png") 60; } "# }, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), opera: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: url(foo.png) 10 40 fill / 10px round; } "#, indoc! {r#" .foo { border-image: url("foo.png") 10 40 fill / 10px round; } "# }, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), opera: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: var(--test) 60; } "#, indoc! {r#" .foo { -webkit-border-image: var(--test) 60; -moz-border-image: var(--test) 60; -o-border-image: var(--test) 60; border-image: var(--test) 60; } "# }, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), opera: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-border-image: url(foo.png) 60; -moz-border-image: url(foo.png) 60; -o-border-image: url(foo.png) 60; border-image: url(foo.png) 60; } "#, indoc! {r#" .foo { border-image: url("foo.png") 60; } "# }, Browsers { chrome: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "#, indoc! {r#" .foo { -webkit-border-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)) 60; -webkit-border-image: -webkit-linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "# }, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "#, indoc! {r#" .foo { -webkit-border-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)) 60; -webkit-border-image: -webkit-linear-gradient(#ff0f0e, #7773ff) 60; -moz-border-image: -moz-linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "# }, Browsers { chrome: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "#, indoc! {r#" .foo { border-image: -webkit-linear-gradient(#ff0f0e, #7773ff) 60; border-image: -moz-linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(#ff0f0e, #7773ff) 60; border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 60; } "# }, Browsers { chrome: Some(15 << 16), firefox: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image-source: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#, indoc! {r#" .foo { border-image-source: -webkit-linear-gradient(#ff0f0e, #7773ff); border-image-source: linear-gradient(#ff0f0e, #7773ff); border-image-source: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "# }, Browsers { chrome: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) var(--foo); } "#, indoc! {r#" .foo { border-image: linear-gradient(#ff0f0e, #7773ff) var(--foo); } @supports (color: lab(0% 0 0)) { .foo { border-image: linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)) var(--foo); } } "# }, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); minify_test(".foo { border: none green }", ".foo{border:green}"); } #[test] pub fn test_border_radius() { test( r#" .foo { border-radius: 10px 100px 10px 100px; } "#, indoc! {r#" .foo { border-radius: 10px 100px; } "# }, ); test( r#" .foo { border-radius: 10px 100px 10px 100px / 120px 120px; } "#, indoc! {r#" .foo { border-radius: 10px 100px / 120px; } "# }, ); test( r#" .foo { border-top-left-radius: 10px 120px; border-top-right-radius: 100px 120px; border-bottom-right-radius: 100px 120px; border-bottom-left-radius: 10px 120px; } "#, indoc! {r#" .foo { border-radius: 10px 100px 100px 10px / 120px; } "# }, ); test( r#" .foo { border-top-left-radius: 4px 2px; border-top-right-radius: 3px 4px; border-bottom-right-radius: 6px 2px; border-bottom-left-radius: 3px 4px; } "#, indoc! {r#" .foo { border-radius: 4px 3px 6px / 2px 4px; } "# }, ); test( r#" .foo { border-top-left-radius: 1% 2%; border-top-right-radius: 3% 4%; border-bottom-right-radius: 5% 6%; border-bottom-left-radius: 7% 8%; } "#, indoc! {r#" .foo { border-radius: 1% 3% 5% 7% / 2% 4% 6% 8%; } "# }, ); test( r#" .foo { border-radius: 10px 100px 10px 100px / 120px 120px; border-start-start-radius: 10px; } "#, indoc! {r#" .foo { border-radius: 10px 100px / 120px; border-start-start-radius: 10px; } "# }, ); test( r#" .foo { border-start-start-radius: 10px; border-radius: 10px 100px 10px 100px / 120px 120px; } "#, indoc! {r#" .foo { border-radius: 10px 100px / 120px; } "# }, ); test( r#" .foo { border-top-left-radius: 10px 120px; border-top-right-radius: 100px 120px; border-start-start-radius: 10px; border-bottom-right-radius: 100px 120px; border-bottom-left-radius: 10px 120px; } "#, indoc! {r#" .foo { border-top-left-radius: 10px 120px; border-top-right-radius: 100px 120px; border-start-start-radius: 10px; border-bottom-right-radius: 100px 120px; border-bottom-left-radius: 10px 120px; } "# }, ); test( r#" .foo { border-radius: 10px; border-top-left-radius: 20px; } "#, indoc! {r#" .foo { border-radius: 20px 10px 10px; } "# }, ); test( r#" .foo { border-radius: 10px; border-top-left-radius: var(--test); } "#, indoc! {r#" .foo { border-radius: 10px; border-top-left-radius: var(--test); } "# }, ); test( r#" .foo { -webkit-border-radius: 10px 100px 10px 100px; -moz-border-radius: 10px 100px 10px 100px; border-radius: 10px 100px 10px 100px; } "#, indoc! {r#" .foo { -webkit-border-radius: 10px 100px; -moz-border-radius: 10px 100px; border-radius: 10px 100px; } "# }, ); test( r#" .foo { -webkit-border-radius: 10px 100px 10px 100px; -moz-border-radius: 20px; border-radius: 30px; } "#, indoc! {r#" .foo { -webkit-border-radius: 10px 100px; -moz-border-radius: 20px; border-radius: 30px; } "# }, ); test( r#" .foo { -webkit-border-top-left-radius: 10px; -moz-border-top-left-radius: 10px; border-top-left-radius: 10px; } "#, indoc! {r#" .foo { -webkit-border-top-left-radius: 10px; -moz-border-top-left-radius: 10px; border-top-left-radius: 10px; } "# }, ); prefix_test( r#" .foo { border-radius: 30px; } "#, indoc! {r#" .foo { -webkit-border-radius: 30px; -moz-border-radius: 30px; border-radius: 30px; } "# }, Browsers { safari: Some(4 << 16), firefox: Some(3 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-top-left-radius: 30px; } "#, indoc! {r#" .foo { -webkit-border-top-left-radius: 30px; -moz-border-top-left-radius: 30px; border-top-left-radius: 30px; } "# }, Browsers { safari: Some(4 << 16), firefox: Some(3 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-border-radius: 30px; -moz-border-radius: 30px; border-radius: 30px; } "#, indoc! {r#" .foo { border-radius: 30px; } "# }, Browsers { safari: Some(14 << 16), firefox: Some(46 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-border-top-left-radius: 30px; -moz-border-top-left-radius: 30px; border-top-left-radius: 30px; } "#, indoc! {r#" .foo { border-top-left-radius: 30px; } "# }, Browsers { safari: Some(14 << 16), firefox: Some(46 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-border-radius: 30px; -moz-border-radius: 30px; } "#, indoc! {r#" .foo { -webkit-border-radius: 30px; -moz-border-radius: 30px; } "# }, Browsers { safari: Some(14 << 16), firefox: Some(46 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-border-top-left-radius: 30px; -moz-border-top-right-radius: 30px; border-bottom-right-radius: 30px; border-bottom-left-radius: 30px; } "#, indoc! {r#" .foo { -webkit-border-top-left-radius: 30px; -moz-border-top-right-radius: 30px; border-bottom-right-radius: 30px; border-bottom-left-radius: 30px; } "# }, Browsers { safari: Some(14 << 16), firefox: Some(46 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-radius: var(--test); } "#, indoc! {r#" .foo { -webkit-border-radius: var(--test); -moz-border-radius: var(--test); border-radius: var(--test); } "# }, Browsers { safari: Some(4 << 16), firefox: Some(3 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-start-start-radius: 5px; } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { border-top-left-radius: 5px; } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { border-top-right-radius: 5px; } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-start-start-radius: 5px; border-start-end-radius: 10px; } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { border-top-left-radius: 5px; border-top-right-radius: 10px; } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { border-top-left-radius: 10px; border-top-right-radius: 5px; } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-end-end-radius: 10px; border-end-start-radius: 5px; } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { border-bottom-right-radius: 10px; border-bottom-left-radius: 5px; } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { border-bottom-right-radius: 5px; border-bottom-left-radius: 10px; } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-start-start-radius: var(--radius); } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { border-top-left-radius: var(--radius); } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { border-top-right-radius: var(--radius); } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { border-start-start-radius: var(--start); border-start-end-radius: var(--end); } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { border-top-left-radius: var(--start); border-top-right-radius: var(--end); } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { border-top-right-radius: var(--start); border-top-left-radius: var(--end); } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); } #[test] pub fn test_outline() { test( r#" .foo { outline-width: 2px; outline-style: solid; outline-color: blue; } "#, indoc! {r#" .foo { outline: 2px solid #00f; } "# }, ); test( r#" .foo { outline: 2px solid blue; } "#, indoc! {r#" .foo { outline: 2px solid #00f; } "# }, ); test( r#" .foo { outline: 2px solid red; outline-color: blue; } "#, indoc! {r#" .foo { outline: 2px solid #00f; } "# }, ); test( r#" .foo { outline: 2px solid yellow; outline-color: var(--color); } "#, indoc! {r#" .foo { outline: 2px solid #ff0; outline-color: var(--color); } "# }, ); prefix_test( ".foo { outline-color: lab(40% 56.6 39) }", indoc! { r#" .foo { outline-color: #b32323; outline-color: lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { outline: 2px solid lab(40% 56.6 39) }", indoc! { r#" .foo { outline: 2px solid #b32323; outline: 2px solid lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { outline: var(--width) solid lab(40% 56.6 39) }", indoc! { r#" .foo { outline: var(--width) solid #b32323; } @supports (color: lab(0% 0 0)) { .foo { outline: var(--width) solid lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] pub fn test_margin() { test( r#" .foo { margin-left: 10px; margin-right: 10px; margin-top: 20px; margin-bottom: 20px; } "#, indoc! {r#" .foo { margin: 20px 10px; } "# }, ); test( r#" .foo { margin-block-start: 15px; margin-block-end: 15px; } "#, indoc! {r#" .foo { margin-block: 15px; } "# }, ); test( r#" .foo { margin-left: 10px; margin-right: 10px; margin-inline-start: 15px; margin-inline-end: 15px; margin-top: 20px; margin-bottom: 20px; } "#, indoc! {r#" .foo { margin-left: 10px; margin-right: 10px; margin-inline: 15px; margin-top: 20px; margin-bottom: 20px; } "# }, ); test( r#" .foo { margin: 10px; margin-top: 20px; } "#, indoc! {r#" .foo { margin: 20px 10px 10px; } "# }, ); test( r#" .foo { margin: 10px; margin-top: var(--top); } "#, indoc! {r#" .foo { margin: 10px; margin-top: var(--top); } "# }, ); prefix_test( r#" .foo { margin-inline-start: 2px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { margin-left: 2px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { margin-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { margin-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline-start: 2px; margin-inline-end: 4px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { margin-left: 2px; margin-right: 4px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { margin-left: 4px; margin-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { margin-left: 4px; margin-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline: 2px; } "#, indoc! {r#" .foo { margin-left: 2px; margin-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-block-start: 2px; } "#, indoc! {r#" .foo { margin-top: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-block-end: 2px; } "#, indoc! {r#" .foo { margin-bottom: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline-start: 2px; margin-inline-end: 2px; } "#, indoc! {r#" .foo { margin-inline-start: 2px; margin-inline-end: 2px; } "# }, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline: 2px; } "#, indoc! {r#" .foo { margin-inline-start: 2px; margin-inline-end: 2px; } "# }, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline-start: 2px; margin-inline-end: 2px; } "#, indoc! {r#" .foo { margin-inline: 2px; } "# }, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline: 2px; } "#, indoc! {r#" .foo { margin-inline: 2px; } "# }, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-right: 22px; margin-right: max(4%, 22px); } "#, indoc! {r#" .foo { margin-right: 22px; margin-right: max(4%, 22px); } "# }, Browsers { safari: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-right: 22px; margin-right: max(4%, 22px); } "#, indoc! {r#" .foo { margin-right: max(4%, 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-right: 22px; margin-right: max(2cqw, 22px); } "#, indoc! {r#" .foo { margin-right: 22px; margin-right: max(2cqw, 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-right: 22px; margin-right: max(2cqw, 22px); } "#, indoc! {r#" .foo { margin-right: max(2cqw, 22px); } "# }, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline-end: 22px; margin-inline-end: max(2cqw, 22px); } "#, indoc! {r#" .foo { margin-inline-end: 22px; margin-inline-end: max(2cqw, 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin-inline-end: 22px; margin-inline-end: max(var(--foo), 22px); } "#, indoc! {r#" .foo { margin-inline-end: 22px; margin-inline-end: max(var(--foo), 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { margin: 22px; margin: max(2cqw, 22px); } "#, indoc! {r#" .foo { margin: 22px; margin: max(2cqw, 22px); } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); } #[test] pub fn test_padding() { test( r#" .foo { padding-left: 10px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px; } "#, indoc! {r#" .foo { padding: 20px 10px; } "# }, ); test( r#" .foo { padding-block-start: 15px; padding-block-end: 15px; } "#, indoc! {r#" .foo { padding-block: 15px; } "# }, ); test( r#" .foo { padding-left: 10px; padding-right: 10px; padding-inline-start: 15px; padding-inline-end: 15px; padding-top: 20px; padding-bottom: 20px; } "#, indoc! {r#" .foo { padding-left: 10px; padding-right: 10px; padding-inline: 15px; padding-top: 20px; padding-bottom: 20px; } "# }, ); prefix_test( r#" .foo { padding-inline-start: 2px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { padding-left: 2px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-inline-start: 2px; padding-inline-end: 4px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { padding-left: 2px; padding-right: 4px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-left: 4px; padding-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-left: 4px; padding-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-inline-start: var(--padding); } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { padding-left: var(--padding); } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-right: var(--padding); } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { padding-right: var(--padding); } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-inline: 2px; } "#, indoc! {r#" .foo { padding-left: 2px; padding-right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-block-start: 2px; } "#, indoc! {r#" .foo { padding-top: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-block-end: 2px; } "#, indoc! {r#" .foo { padding-bottom: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-top: 1px; padding-left: 2px; padding-bottom: 3px; padding-right: 4px; } "#, indoc! {r#" .foo { padding: 1px 4px 3px 2px; } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-inline-start: 2px; padding-inline-end: 2px; } "#, indoc! {r#" .foo { padding-inline-start: 2px; padding-inline-end: 2px; } "# }, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { padding-inline-start: 2px; padding-inline-end: 2px; } "#, indoc! {r#" .foo { padding-inline: 2px; } "# }, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); } #[test] fn test_scroll_padding() { prefix_test( r#" .foo { scroll-padding-inline: 2px; } "#, indoc! {r#" .foo { scroll-padding-inline: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); } #[test] fn test_size() { prefix_test( r#" .foo { block-size: 25px; inline-size: 25px; min-block-size: 25px; min-inline-size: 25px; } "#, indoc! {r#" .foo { height: 25px; width: 25px; min-height: 25px; min-width: 25px; } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { block-size: 25px; inline-size: 25px; min-block-size: 25px; min-inline-size: 25px; } "#, indoc! {r#" .foo { block-size: 25px; inline-size: 25px; min-block-size: 25px; min-inline-size: 25px; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { block-size: var(--size); inline-size: var(--size); min-block-size: var(--size); min-inline-size: var(--size); } "#, indoc! {r#" .foo { height: var(--size); width: var(--size); min-height: var(--size); min-width: var(--size); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); for (in_prop, out_prop) in [ ("width", "width"), ("height", "height"), ("block-size", "height"), ("inline-size", "width"), ("min-width", "min-width"), ("min-height", "min-height"), ("min-block-size", "min-height"), ("min-inline-size", "min-width"), ("max-width", "max-width"), ("max-height", "max-height"), ("max-block-size", "max-height"), ("max-inline-size", "max-width"), ] { prefix_test( &format!( r#" .foo {{ {}: stretch; }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: -webkit-fill-available; {}: -moz-available; {}: stretch; }} "#}, out_prop, out_prop, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: -webkit-fill-available; }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: -webkit-fill-available; }} "#}, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: fit-content; }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: -webkit-fit-content; {}: -moz-fit-content; {}: fit-content; }} "#}, out_prop, out_prop, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: fit-content(50%); }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: fit-content(50%); }} "#}, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: min-content; }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: -webkit-min-content; {}: -moz-min-content; {}: min-content; }} "#}, out_prop, out_prop, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: max-content; }} "#, in_prop ), &format!( indoc! {r#" .foo {{ {}: -webkit-max-content; {}: -moz-max-content; {}: max-content; }} "#}, out_prop, out_prop, out_prop ), Browsers { safari: Some(8 << 16), firefox: Some(4 << 16), ..Browsers::default() }, ); } } #[test] pub fn test_background() { test( r#" .foo { background: url(img.png); background-position-x: 20px; background-position-y: 10px; background-size: 50px 100px; background-repeat: repeat no-repeat; } "#, indoc! {r#" .foo { background: url("img.png") 20px 10px / 50px 100px repeat-x; } "# }, ); test( r#" .foo { background-color: red; background-position: 0% 0%; background-size: auto; background-repeat: repeat; background-clip: border-box; background-origin: padding-box; background-attachment: scroll; background-image: none } "#, indoc! {r#" .foo { background: red; } "# }, ); test( r#" .foo { background-color: gray; background-position: 40% 50%; background-size: 10em auto; background-repeat: round; background-clip: border-box; background-origin: border-box; background-attachment: fixed; background-image: url('chess.png'); } "#, indoc! {r#" .foo { background: gray url("chess.png") 40% / 10em round fixed border-box; } "# }, ); test( r#" .foo { background: url(img.png), url(test.jpg) gray; background-position-x: right 20px, 10px; background-position-y: top 20px, 15px; background-size: 50px 50px, auto; background-repeat: repeat no-repeat, no-repeat; } "#, indoc! {r#" .foo { background: url("img.png") right 20px top 20px / 50px 50px repeat-x, gray url("test.jpg") 10px 15px no-repeat; } "# }, ); minify_test( r#" .foo { background-position: center center; } "#, indoc! {".foo{background-position:50%}" }, ); test( r#" .foo { background: url(img.png) gray; background-clip: content-box; -webkit-background-clip: text; } "#, indoc! {r#" .foo { background: gray url("img.png") padding-box content-box; -webkit-background-clip: text; } "# }, ); test( r#" .foo { background: url(img.png) gray; -webkit-background-clip: text; background-clip: content-box; } "#, indoc! {r#" .foo { background: gray url("img.png"); -webkit-background-clip: text; background-clip: content-box; } "# }, ); test( r#" .foo { background: url(img.png) gray; background-position: var(--pos); } "#, indoc! {r#" .foo { background: gray url("img.png"); background-position: var(--pos); } "# }, ); minify_test( ".foo { background-position: bottom left }", ".foo{background-position:0 100%}", ); minify_test( ".foo { background-position: left 10px center }", ".foo{background-position:10px 50%}", ); minify_test( ".foo { background-position: right 10px center }", ".foo{background-position:right 10px center}", ); minify_test( ".foo { background-position: right 10px top 20px }", ".foo{background-position:right 10px top 20px}", ); minify_test( ".foo { background-position: left 10px top 20px }", ".foo{background-position:10px 20px}", ); minify_test( ".foo { background-position: left 10px bottom 20px }", ".foo{background-position:left 10px bottom 20px}", ); minify_test( ".foo { background-position: left 10px top }", ".foo{background-position:10px 0}", ); minify_test( ".foo { background-position: bottom right }", ".foo{background-position:100% 100%}", ); minify_test( ".foo { background: url('img-sprite.png') no-repeat bottom right }", ".foo{background:url(img-sprite.png) 100% 100% no-repeat}", ); minify_test(".foo { background: transparent }", ".foo{background:0 0}"); minify_test(".foo { background: url(\"data:image/svg+xml,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E\") }", ".foo{background:url(\"data:image/svg+xml,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E\")}"); test( r#" .foo { background: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background: url("img.png") text; } "# }, ); prefix_test( r#" .foo { background: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background: url("img.png"); -webkit-background-clip: text; background-clip: text; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background: url("img.png") text; } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background: url("img.png"); -webkit-background-clip: text; background-clip: text; } "# }, Browsers { safari: Some(14 << 16), chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background-image: url("img.png"); -webkit-background-clip: text; background-clip: text; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: url(img.png); background-clip: text; } "#, indoc! {r#" .foo { background-image: url("img.png"); background-clip: text; } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); minify_test(".foo { background: none center }", ".foo{background:50%}"); minify_test(".foo { background: none }", ".foo{background:0 0}"); prefix_test( r#" .foo { background: lab(51.5117% 43.3777 -29.0443); } "#, indoc! {r#" .foo { background: #af5cae; background: lab(51.5117% 43.3777 -29.0443); } "# }, Browsers { chrome: Some(95 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: lab(51.5117% 43.3777 -29.0443) url(foo.png); } "#, indoc! {r#" .foo { background: #af5cae url("foo.png"); background: lab(51.5117% 43.3777 -29.0443) url("foo.png"); } "# }, Browsers { chrome: Some(95 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: lab(51.5117% 43.3777 -29.0443) linear-gradient(lab(52.2319% 40.1449 59.9171), lab(47.7776% -34.2947 -7.65904)); } "#, indoc! {r#" .foo { background: #af5cae linear-gradient(#c65d07, #00807c); background: lab(51.5117% 43.3777 -29.0443) linear-gradient(lab(52.2319% 40.1449 59.9171), lab(47.7776% -34.2947 -7.65904)); } "# }, Browsers { chrome: Some(95 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); test( ".foo { background: calc(var(--v) / 0.3)", indoc! {r#" .foo { background: calc(var(--v) / .3); } "#}, ); } #[test] pub fn test_flex() { test( r#" .foo { flex-direction: column; flex-wrap: wrap; } "#, indoc! {r#" .foo { flex-flow: column wrap; } "# }, ); test( r#" .foo { flex-direction: row; flex-wrap: wrap; } "#, indoc! {r#" .foo { flex-flow: wrap; } "# }, ); test( r#" .foo { flex-direction: row; flex-wrap: nowrap; } "#, indoc! {r#" .foo { flex-flow: row; } "# }, ); test( r#" .foo { flex-direction: column; flex-wrap: nowrap; } "#, indoc! {r#" .foo { flex-flow: column; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 1; flex-basis: 0%; } "#, indoc! {r#" .foo { flex: 1; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 1; flex-basis: 0; } "#, indoc! {r#" .foo { flex: 1 1 0; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 1; flex-basis: 0px; } "#, indoc! {r#" .foo { flex: 1 1 0; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 2; flex-basis: 0%; } "#, indoc! {r#" .foo { flex: 1 2; } "# }, ); test( r#" .foo { flex-grow: 2; flex-shrink: 1; flex-basis: 0%; } "#, indoc! {r#" .foo { flex: 2; } "# }, ); test( r#" .foo { flex-grow: 2; flex-shrink: 2; flex-basis: 0%; } "#, indoc! {r#" .foo { flex: 2 2; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 1; flex-basis: 10px; } "#, indoc! {r#" .foo { flex: 10px; } "# }, ); test( r#" .foo { flex-grow: 2; flex-shrink: 1; flex-basis: 10px; } "#, indoc! {r#" .foo { flex: 2 10px; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 0; flex-basis: 0%; } "#, indoc! {r#" .foo { flex: 1 0; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 0; flex-basis: auto; } "#, indoc! {r#" .foo { flex: 1 0 auto; } "# }, ); test( r#" .foo { flex-grow: 1; flex-shrink: 1; flex-basis: auto; } "#, indoc! {r#" .foo { flex: auto; } "# }, ); test( r#" .foo { flex: 0 0; flex-grow: 1; } "#, indoc! {r#" .foo { flex: 1 0; } "# }, ); test( r#" .foo { flex: 0 0; flex-grow: var(--grow); } "#, indoc! {r#" .foo { flex: 0 0; flex-grow: var(--grow); } "# }, ); test( r#" .foo { align-content: center; justify-content: center; } "#, indoc! {r#" .foo { place-content: center; } "# }, ); test( r#" .foo { align-content: first baseline; justify-content: safe right; } "#, indoc! {r#" .foo { place-content: baseline safe right; } "# }, ); test( r#" .foo { place-content: first baseline unsafe left; } "#, indoc! {r#" .foo { place-content: baseline unsafe left; } "# }, ); test( r#" .foo { place-content: center center; } "#, indoc! {r#" .foo { place-content: center; } "# }, ); test( r#" .foo { align-self: center; justify-self: center; } "#, indoc! {r#" .foo { place-self: center; } "# }, ); test( r#" .foo { align-self: center; justify-self: unsafe left; } "#, indoc! {r#" .foo { place-self: center unsafe left; } "# }, ); test( r#" .foo { align-items: center; justify-items: center; } "#, indoc! {r#" .foo { place-items: center; } "# }, ); test( r#" .foo { align-items: center; justify-items: legacy left; } "#, indoc! {r#" .foo { place-items: center legacy left; } "# }, ); test( r#" .foo { place-items: center; justify-items: var(--justify); } "#, indoc! {r#" .foo { place-items: center; justify-items: var(--justify); } "# }, ); test( r#" .foo { row-gap: 10px; column-gap: 20px; } "#, indoc! {r#" .foo { gap: 10px 20px; } "# }, ); test( r#" .foo { row-gap: 10px; column-gap: 10px; } "#, indoc! {r#" .foo { gap: 10px; } "# }, ); test( r#" .foo { gap: 10px; column-gap: 20px; } "#, indoc! {r#" .foo { gap: 10px 20px; } "# }, ); test( r#" .foo { column-gap: 20px; gap: 10px; } "#, indoc! {r#" .foo { gap: 10px; } "# }, ); test( r#" .foo { row-gap: normal; column-gap: 20px; } "#, indoc! {r#" .foo { gap: normal 20px; } "# }, ); test( r#" .foo { -webkit-flex-grow: 1; -webkit-flex-shrink: 1; -webkit-flex-basis: auto; } "#, indoc! {r#" .foo { -webkit-flex: auto; } "# }, ); test( r#" .foo { -webkit-flex-grow: 1; -webkit-flex-shrink: 1; -webkit-flex-basis: auto; flex-grow: 1; flex-shrink: 1; flex-basis: auto; } "#, indoc! {r#" .foo { -webkit-flex: auto; flex: auto; } "# }, ); prefix_test( r#" .foo { -webkit-box-orient: horizontal; -webkit-box-direction: normal; flex-direction: row; } "#, indoc! {r#" .foo { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } "#}, Browsers { safari: Some(4 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-direction: row; } "#, indoc! {r#" .foo { -webkit-box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-direction: normal; -moz-box-direction: normal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -moz-box-orient: horizontal; -moz-box-direction: normal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; } "#, indoc! {r#" .foo { flex-direction: row; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-wrap: wrap; } "#, indoc! {r#" .foo { -webkit-box-lines: multiple; -moz-box-lines: multiple; -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-lines: multiple; -moz-box-lines: multiple; -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; } "#, indoc! {r#" .foo { flex-wrap: wrap; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-flow: row wrap; } "#, indoc! {r#" .foo { -webkit-box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-direction: normal; -moz-box-direction: normal; -webkit-flex-flow: wrap; -ms-flex-flow: wrap; flex-flow: wrap; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-direction: normal; -moz-box-direction: normal; -webkit-flex-flow: wrap; -ms-flex-flow: wrap; flex-flow: wrap; } "#, indoc! {r#" .foo { flex-flow: wrap; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-grow: 1; } "#, indoc! {r#" .foo { -webkit-box-flex: 1; -moz-box-flex: 1; -ms-flex-positive: 1; -webkit-flex-grow: 1; flex-grow: 1; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-flex: 1; -moz-box-flex: 1; -ms-flex-positive: 1; -webkit-flex-grow: 1; flex-grow: 1; } "#, indoc! {r#" .foo { flex-grow: 1; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-shrink: 1; } "#, indoc! {r#" .foo { -ms-flex-negative: 1; -webkit-flex-shrink: 1; flex-shrink: 1; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-negative: 1; -webkit-flex-shrink: 1; flex-shrink: 1; } "#, indoc! {r#" .foo { flex-shrink: 1; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex-basis: 1px; } "#, indoc! {r#" .foo { -ms-flex-preferred-size: 1px; -webkit-flex-basis: 1px; flex-basis: 1px; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-preferred-size: 1px; -webkit-flex-basis: 1px; flex-basis: 1px; } "#, indoc! {r#" .foo { flex-basis: 1px; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { flex: 1; } "#, indoc! {r#" .foo { -webkit-box-flex: 1; -moz-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-flex: 1; -moz-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; } "#, indoc! {r#" .foo { flex: 1; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { align-content: space-between; } "#, indoc! {r#" .foo { -ms-flex-line-pack: justify; -webkit-align-content: space-between; align-content: space-between; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-line-pack: justify; -webkit-align-content: space-between; align-content: space-between; } "#, indoc! {r#" .foo { align-content: space-between; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { justify-content: space-between; } "#, indoc! {r#" .foo { -webkit-box-pack: justify; -moz-box-pack: justify; -ms-flex-pack: justify; -webkit-justify-content: space-between; justify-content: space-between; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-pack: justify; -moz-box-pack: justify; -ms-flex-pack: justify; -webkit-justify-content: space-between; justify-content: space-between; } "#, indoc! {r#" .foo { justify-content: space-between; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-content: space-between flex-end; } "#, indoc! {r#" .foo { -ms-flex-line-pack: justify; -webkit-box-pack: end; -moz-box-pack: end; -ms-flex-pack: end; -webkit-align-content: space-between; align-content: space-between; -webkit-justify-content: flex-end; justify-content: flex-end; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-line-pack: justify; -webkit-box-pack: end; -moz-box-pack: end; -ms-flex-pack: end; -webkit-align-content: space-between; -webkit-justify-content: flex-end; place-content: space-between flex-end; } "#, indoc! {r#" .foo { place-content: space-between flex-end; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-content: space-between flex-end; } "#, indoc! {r#" .foo { align-content: space-between; justify-content: flex-end; } "#}, Browsers { chrome: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-content: space-between flex-end; } "#, indoc! {r#" .foo { place-content: space-between flex-end; } "#}, Browsers { chrome: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { align-self: flex-end; } "#, indoc! {r#" .foo { -ms-flex-item-align: end; -webkit-align-self: flex-end; align-self: flex-end; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-item-align: end; -webkit-align-self: flex-end; align-self: flex-end; } "#, indoc! {r#" .foo { align-self: flex-end; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-self: center flex-end; } "#, indoc! {r#" .foo { -ms-flex-item-align: center; -webkit-align-self: center; align-self: center; justify-self: flex-end; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex-item-align: center; -webkit-align-self: center; place-self: center flex-end; } "#, indoc! {r#" .foo { place-self: center flex-end; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-self: center flex-end; } "#, indoc! {r#" .foo { align-self: center; justify-self: flex-end; } "#}, Browsers { chrome: Some(57 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-self: center flex-end; } "#, indoc! {r#" .foo { place-self: center flex-end; } "#}, Browsers { chrome: Some(59 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { align-items: flex-end; } "#, indoc! {r#" .foo { -webkit-box-align: end; -moz-box-align: end; -ms-flex-align: end; -webkit-align-items: flex-end; align-items: flex-end; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-align: end; -moz-box-align: end; -ms-flex-align: end; -webkit-align-items: flex-end; align-items: flex-end; } "#, indoc! {r#" .foo { align-items: flex-end; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-items: flex-end center; } "#, indoc! {r#" .foo { -webkit-box-align: end; -moz-box-align: end; -ms-flex-align: end; -webkit-align-items: flex-end; align-items: flex-end; justify-items: center; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-align: end; -moz-box-align: end; -ms-flex-align: end; -webkit-align-items: flex-end; place-items: flex-end center; } "#, indoc! {r#" .foo { place-items: flex-end center; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { place-items: flex-end center; } "#, indoc! {r#" .foo { align-items: flex-end; justify-items: center; } "#}, Browsers { safari: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { order: 1; } "#, indoc! {r#" .foo { -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; -webkit-order: 1; order: 1; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(4 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; -webkit-order: 1; order: 1; } "#, indoc! {r#" .foo { order: 1; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -ms-flex: 0 0 8%; flex: 0 0 5%; } "#, indoc! {r#" .foo { -ms-flex: 0 0 8%; flex: 0 0 5%; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); } #[test] fn test_font() { test( r#" .foo { font-family: "Helvetica", "Times New Roman", sans-serif; font-size: 12px; font-weight: bold; font-style: italic; font-stretch: expanded; font-variant-caps: small-caps; line-height: 1.2em; } "#, indoc! {r#" .foo { font: italic small-caps bold expanded 12px / 1.2em Helvetica, Times New Roman, sans-serif; } "# }, ); minify_test( r#" .foo { font-family: "Helvetica", "Times New Roman", sans-serif; font-size: 12px; font-weight: bold; font-style: italic; font-stretch: expanded; font-variant-caps: small-caps; line-height: 1.2em; } "#, indoc! {".foo{font:italic small-caps 700 125% 12px/1.2em Helvetica,Times New Roman,sans-serif}" }, ); test( r#" .foo { font: 12px "Helvetica", "Times New Roman", sans-serif; line-height: 1.2em; } "#, indoc! {r#" .foo { font: 12px / 1.2em Helvetica, Times New Roman, sans-serif; } "# }, ); test( r#" .foo { font: 12px "Helvetica", "Times New Roman", sans-serif; line-height: var(--lh); } "#, indoc! {r#" .foo { font: 12px Helvetica, Times New Roman, sans-serif; line-height: var(--lh); } "# }, ); minify_test( r#" .foo { font-family: "Helvetica", "Times New Roman", sans-serif; font-size: 12px; font-stretch: expanded; } "#, indoc! {".foo{font-family:Helvetica,Times New Roman,sans-serif;font-size:12px;font-stretch:125%}" }, ); test( r#" .foo { font-family: "Helvetica", "Times New Roman", sans-serif; font-size: 12px; font-weight: bold; font-style: italic; font-stretch: expanded; font-variant-caps: all-small-caps; line-height: 1.2em; } "#, indoc! {r#" .foo { font: italic bold expanded 12px / 1.2em Helvetica, Times New Roman, sans-serif; font-variant-caps: all-small-caps; } "# }, ); minify_test( ".foo { font: normal normal 600 9px/normal Charcoal; }", ".foo{font:600 9px Charcoal}", ); minify_test( ".foo { font: normal normal 500 medium/normal Charcoal; }", ".foo{font:500 medium Charcoal}", ); minify_test( ".foo { font: normal normal 400 medium Charcoal; }", ".foo{font:400 medium Charcoal}", ); minify_test( ".foo { font: normal normal 500 medium/10px Charcoal; }", ".foo{font:500 medium/10px Charcoal}", ); minify_test( ".foo { font-family: 'sans-serif'; }", ".foo{font-family:\"sans-serif\"}", ); minify_test(".foo { font-family: sans-serif; }", ".foo{font-family:sans-serif}"); minify_test(".foo { font-family: 'default'; }", ".foo{font-family:\"default\"}"); minify_test(".foo { font-family: default; }", ".foo{font-family:default}"); minify_test(".foo { font-family: 'inherit'; }", ".foo{font-family:\"inherit\"}"); minify_test(".foo { font-family: inherit; }", ".foo{font-family:inherit}"); minify_test(".foo { font-family: inherit test; }", ".foo{font-family:inherit test}"); minify_test( ".foo { font-family: 'inherit test'; }", ".foo{font-family:inherit test}", ); minify_test(".foo { font-family: revert; }", ".foo{font-family:revert}"); minify_test(".foo { font-family: 'revert'; }", ".foo{font-family:\"revert\"}"); minify_test(".foo { font-family: revert-layer; }", ".foo{font-family:revert-layer}"); minify_test( ".foo { font-family: revert-layer, serif; }", ".foo{font-family:revert-layer,serif}", ); minify_test( ".foo { font-family: 'revert', sans-serif; }", ".foo{font-family:\"revert\",sans-serif}", ); minify_test( ".foo { font-family: 'revert', foo, sans-serif; }", ".foo{font-family:\"revert\",foo,sans-serif}", ); minify_test(".foo { font-family: ''; }", ".foo{font-family:\"\"}"); // font-family in @font-face minify_test( "@font-face { font-family: 'revert'; }", "@font-face{font-family:\"revert\"}", ); minify_test( "@font-face { font-family: 'revert-layer'; }", "@font-face{font-family:\"revert-layer\"}", ); prefix_test( r#" .foo { font-family: Helvetica, system-ui, sans-serif; } "#, indoc! {r#" .foo { font-family: Helvetica, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { font: 100%/1.5 Helvetica, system-ui, sans-serif; } "#, indoc! {r#" .foo { font: 100% / 1.5 Helvetica, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } "#, indoc! {r#" .foo { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; } "# }, Browsers { firefox: Some(91 << 16), ..Browsers::default() }, ); } #[test] fn test_vertical_align() { minify_test(".foo { vertical-align: middle }", ".foo{vertical-align:middle}"); minify_test(".foo { vertical-align: 0.3em }", ".foo{vertical-align:.3em}"); } #[test] fn test_selectors() { minify_test(":nth-col(2n) {width: 20px}", ":nth-col(2n){width:20px}"); minify_test(":nth-col(10n-1) {width: 20px}", ":nth-col(10n-1){width:20px}"); minify_test(":nth-col(-n+2) {width: 20px}", ":nth-col(-n+2){width:20px}"); minify_test(":nth-col(even) {width: 20px}", ":nth-col(2n){width:20px}"); minify_test(":nth-col(odd) {width: 20px}", ":nth-col(odd){width:20px}"); minify_test(":nth-last-col(2n) {width: 20px}", ":nth-last-col(2n){width:20px}"); minify_test(":nth-last-col(10n-1) {width: 20px}", ":nth-last-col(10n-1){width:20px}"); minify_test(":nth-last-col(-n+2) {width: 20px}", ":nth-last-col(-n+2){width:20px}"); minify_test(":nth-last-col(even) {width: 20px}", ":nth-last-col(2n){width:20px}"); minify_test(":nth-last-col(odd) {width: 20px}", ":nth-last-col(odd){width:20px}"); minify_test(":nth-child(odd) {width: 20px}", ":nth-child(odd){width:20px}"); minify_test(":nth-child(2n) {width: 20px}", ":nth-child(2n){width:20px}"); minify_test(":nth-child(2n+1) {width: 20px}", ":nth-child(odd){width:20px}"); minify_test(":first-child {width: 20px}", ":first-child{width:20px}"); minify_test(":nth-child(1) {width: 20px}", ":first-child{width:20px}"); minify_test(":nth-last-child(1) {width: 20px}", ":last-child{width:20px}"); minify_test(":nth-of-type(1) {width: 20px}", ":first-of-type{width:20px}"); minify_test(":nth-last-of-type(1) {width: 20px}", ":last-of-type{width:20px}"); minify_test( ":nth-child(even of li.important) {width: 20px}", ":nth-child(2n of li.important){width:20px}", ); minify_test( ":nth-child(1 of li.important) {width: 20px}", ":nth-child(1 of li.important){width:20px}", ); minify_test( ":nth-last-child(even of li.important) {width: 20px}", ":nth-last-child(2n of li.important){width:20px}", ); minify_test( ":nth-last-child(1 of li.important) {width: 20px}", ":nth-last-child(1 of li.important){width:20px}", ); minify_test( ":nth-last-child(1 of.important) {width: 20px}", ":nth-last-child(1 of .important){width:20px}", ); minify_test("[foo=\"baz\"] {color:red}", "[foo=baz]{color:red}"); minify_test("[foo=\"foo bar\"] {color:red}", "[foo=foo\\ bar]{color:red}"); minify_test("[foo=\"foo bar baz\"] {color:red}", "[foo=\"foo bar baz\"]{color:red}"); minify_test("[foo=\"\"] {color:red}", "[foo=\"\"]{color:red}"); minify_test( ".test:not([foo=\"bar\"]) {color:red}", ".test:not([foo=bar]){color:red}", ); minify_test(".test + .foo {color:red}", ".test+.foo{color:red}"); minify_test(".test ~ .foo {color:red}", ".test~.foo{color:red}"); minify_test(".test .foo {color:red}", ".test .foo{color:red}"); minify_test( ".custom-range::-webkit-slider-thumb:active {color:red}", ".custom-range::-webkit-slider-thumb:active{color:red}", ); minify_test(".test:not(.foo, .bar) {color:red}", ".test:not(.foo,.bar){color:red}"); minify_test(".test:is(.foo, .bar) {color:red}", ".test:is(.foo,.bar){color:red}"); minify_test( ".test:where(.foo, .bar) {color:red}", ".test:where(.foo,.bar){color:red}", ); minify_test( ".test:where(.foo, .bar) {color:red}", ".test:where(.foo,.bar){color:red}", ); minify_test(":host {color:red}", ":host{color:red}"); minify_test(":host(.foo) {color:red}", ":host(.foo){color:red}"); minify_test("::slotted(span) {color:red", "::slotted(span){color:red}"); minify_test( "custom-element::part(foo) {color:red}", "custom-element::part(foo){color:red}", ); minify_test(".sm\\:text-5xl { font-size: 3rem }", ".sm\\:text-5xl{font-size:3rem}"); minify_test("a:has(> img) {color:red}", "a:has(>img){color:red}"); minify_test("dt:has(+ dt) {color:red}", "dt:has(+dt){color:red}"); minify_test( "section:not(:has(h1, h2, h3, h4, h5, h6)) {color:red}", "section:not(:has(h1,h2,h3,h4,h5,h6)){color:red}", ); minify_test( ":has(.sibling ~ .target) {color:red}", ":has(.sibling~.target){color:red}", ); minify_test(".x:has(> .a > .b) {color:red}", ".x:has(>.a>.b){color:red}"); minify_test(".x:has(.bar, #foo) {color:red}", ".x:has(.bar,#foo){color:red}"); minify_test(".x:has(span + span) {color:red}", ".x:has(span+span){color:red}"); minify_test("a:has(:visited) {color:red}", "a:has(:visited){color:red}"); for element in [ "-webkit-scrollbar", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-thumb", "-webkit-scrollbar-corner", "-webkit-resizer", ] { for class in [ "enabled", "disabled", "hover", "active", "horizontal", "vertical", "decrement", "increment", "start", "end", "double-button", "single-button", "no-button", "corner-present", "window-inactive", ] { minify_test( &format!("::{}:{} {{color:red}}", element, class), &format!("::{}:{}{{color:red}}", element, class), ); } } for class in [ "horizontal", "vertical", "decrement", "increment", "start", "end", "double-button", "single-button", "no-button", "corner-present", "window-inactive", ] { error_test( &format!(":{} {{color:red}}", class), ParserError::SelectorError(SelectorError::InvalidPseudoClassBeforeWebKitScrollbar), ); } for element in [ "-webkit-scrollbar", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-thumb", "-webkit-scrollbar-corner", "-webkit-resizer", ] { error_test( &format!("::{}:focus {{color:red}}", element), ParserError::SelectorError(SelectorError::InvalidPseudoClassAfterWebKitScrollbar), ); } error_test( "a::first-letter:last-child {color:red}", ParserError::SelectorError(SelectorError::InvalidPseudoClassAfterPseudoElement), ); minify_test( "a:last-child::first-letter {color:red}", "a:last-child:first-letter{color:red}", ); prefix_test( ".test:not(.foo, .bar) {color:red}", indoc! {r#" .test:not(.foo):not(.bar) { color: red; } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".test:not(.foo, .bar) {color:red}", indoc! {r#" .test:not(.foo, .bar) { color: red; } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); minify_test("a:lang(en) {color:red}", "a:lang(en){color:red}"); minify_test("a:lang(en, fr) {color:red}", "a:lang(en,fr){color:red}"); minify_test("a:lang('en') {color:red}", "a:lang(en){color:red}"); minify_test( "a:-webkit-any(.foo, .bar) {color:red}", "a:-webkit-any(.foo,.bar){color:red}", ); minify_test("a:-moz-any(.foo, .bar) {color:red}", "a:-moz-any(.foo,.bar){color:red}"); prefix_test( "a:is(.foo, .bar) {color:red}", indoc! {r#" a:-webkit-any(.foo, .bar) { color: red; } a:-moz-any(.foo, .bar) { color: red; } a:is(.foo, .bar) { color: red; } "#}, Browsers { safari: Some(11 << 16), firefox: Some(50 << 16), ..Browsers::default() }, ); prefix_test( "a:is(.foo > .bar) {color:red}", indoc! {r#" a:is(.foo > .bar) { color: red; } "#}, Browsers { safari: Some(11 << 16), firefox: Some(50 << 16), ..Browsers::default() }, ); prefix_test( "a:lang(en, fr) {color:red}", indoc! {r#" a:-webkit-any(:lang(en), :lang(fr)) { color: red; } a:-moz-any(:lang(en), :lang(fr)) { color: red; } a:is(:lang(en), :lang(fr)) { color: red; } "#}, Browsers { safari: Some(11 << 16), firefox: Some(50 << 16), ..Browsers::default() }, ); prefix_test( "a:lang(en, fr) {color:red}", indoc! {r#" a:is(:lang(en), :lang(fr)) { color: red; } "#}, Browsers { safari: Some(14 << 16), firefox: Some(88 << 16), ..Browsers::default() }, ); prefix_test( "a:lang(en, fr) {color:red}", indoc! {r#" a:lang(en, fr) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(rtl) {color:red}", indoc! {r#" a:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { color: red; } a:-moz-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { color: red; } a:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { color: red; } "#}, Browsers { safari: Some(11 << 16), firefox: Some(50 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(ltr) {color:red}", indoc! {r#" a:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { color: red; } "#}, Browsers { safari: Some(11 << 16), firefox: Some(50 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(rtl) {color:red}", indoc! {r#" a:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), firefox: Some(88 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(ltr) {color:red}", indoc! {r#" a:not(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), firefox: Some(88 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(rtl) {color:red}", indoc! {r#" a:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(ltr) {color:red}", indoc! {r#" a:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:is(:dir(rtl)) {color:red}", indoc! {r#" a:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:where(:dir(rtl)) {color:red}", indoc! {r#" a:where(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:has(:dir(rtl)) {color:red}", indoc! {r#" a:has(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:not(:dir(rtl)) {color:red}", indoc! {r#" a:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(rtl)::after {color:red}", indoc! {r#" a:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi):after { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( "a:dir(rtl) div {color:red}", indoc! {r#" a:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) div { color: red; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); minify_test(".foo::cue {color: red}", ".foo::cue{color:red}"); minify_test(".foo::cue-region {color: red}", ".foo::cue-region{color:red}"); minify_test(".foo::cue(b) {color: red}", ".foo::cue(b){color:red}"); minify_test(".foo::cue-region(b) {color: red}", ".foo::cue-region(b){color:red}"); minify_test( "::cue(v[voice='active']) {color: yellow;}", "::cue(v[voice=active]){color:#ff0}", ); minify_test(":foo(bar) { color: yellow }", ":foo(bar){color:#ff0}"); minify_test("::foo(bar) { color: yellow }", "::foo(bar){color:#ff0}"); minify_test("::foo(*) { color: yellow }", "::foo(*){color:#ff0}"); minify_test(":is(.foo) { color: yellow }", ".foo{color:#ff0}"); minify_test(":is(#foo) { color: yellow }", "#foo{color:#ff0}"); minify_test("a:is(.foo) { color: yellow }", "a.foo{color:#ff0}"); minify_test("a:is([data-test]) { color: yellow }", "a[data-test]{color:#ff0}"); minify_test(".foo:is(a) { color: yellow }", ".foo:is(a){color:#ff0}"); minify_test(".foo:is(*|a) { color: yellow }", ".foo:is(*|a){color:#ff0}"); minify_test(".foo:is(*) { color: yellow }", ".foo:is(*){color:#ff0}"); minify_test( "@namespace svg url(http://www.w3.org/2000/svg); .foo:is(svg|a) { color: yellow }", "@namespace svg \"http://www.w3.org/2000/svg\";.foo:is(svg|a){color:#ff0}", ); minify_test("a:is(.foo .bar) { color: yellow }", "a:is(.foo .bar){color:#ff0}"); minify_test(":is(.foo, .bar) { color: yellow }", ":is(.foo,.bar){color:#ff0}"); minify_test("a:is(:not(.foo)) { color: yellow }", "a:not(.foo){color:#ff0}"); minify_test("a:is(:first-child) { color: yellow }", "a:first-child{color:#ff0}"); minify_test("a:is(:has(.foo)) { color: yellow }", "a:has(.foo){color:#ff0}"); minify_test("a:is(:is(.foo)) { color: yellow }", "a.foo{color:#ff0}"); minify_test(":host(:hover) {color: red}", ":host(:hover){color:red}"); minify_test("::slotted(:hover) {color: red}", "::slotted(:hover){color:red}"); minify_test( ":root::view-transition {position: fixed}", ":root::view-transition{position:fixed}", ); for name in &[ "view-transition-group", "view-transition-image-pair", "view-transition-new", "view-transition-old", ] { minify_test( &format!(":root::{}(*) {{position: fixed}}", name), &format!(":root::{}(*){{position:fixed}}", name), ); minify_test( &format!(":root::{}(foo) {{position: fixed}}", name), &format!(":root::{}(foo){{position:fixed}}", name), ); minify_test( &format!(":root::{}(foo):only-child {{position: fixed}}", name), &format!(":root::{}(foo):only-child{{position:fixed}}", name), ); error_test( &format!(":root::{}(foo):first-child {{position: fixed}}", name), ParserError::SelectorError(SelectorError::InvalidPseudoClassAfterPseudoElement), ); error_test( &format!(":root::{}(foo)::before {{position: fixed}}", name), ParserError::SelectorError(SelectorError::InvalidState), ); } } #[test] fn test_keyframes() { minify_test( r#" @keyframes "test" { 100% { background: blue } } "#, "@keyframes test{to{background:#00f}}", ); minify_test( r#" @keyframes test { 100% { background: blue } } "#, "@keyframes test{to{background:#00f}}", ); // CSS-wide keywords and `none` cannot remove quotes. minify_test( r#" @keyframes "revert" { from { background: green; } } "#, "@keyframes \"revert\"{0%{background:green}}", ); minify_test( r#" @keyframes "none" { from { background: green; } } "#, "@keyframes \"none\"{0%{background:green}}", ); // CSS-wide keywords without quotes throws an error. error_test( r#" @keyframes revert {} "#, ParserError::UnexpectedToken(Token::Ident("revert".into())), ); error_test( r#" @keyframes revert-layer {} "#, ParserError::UnexpectedToken(Token::Ident("revert-layer".into())), ); error_test( r#" @keyframes none {} "#, ParserError::UnexpectedToken(Token::Ident("none".into())), ); error_test( r#" @keyframes NONE {} "#, ParserError::UnexpectedToken(Token::Ident("NONE".into())), ); minify_test( r#" @-webkit-keyframes test { from { background: green; background-color: red; } 100% { background: blue } } "#, "@-webkit-keyframes test{0%{background:red}to{background:#00f}}", ); minify_test( r#" @-moz-keyframes test { from { background: green; background-color: red; } 100% { background: blue } } "#, "@-moz-keyframes test{0%{background:red}to{background:#00f}}", ); minify_test(r#" @-webkit-keyframes test { from { background: green; background-color: red; } 100% { background: blue } } @-moz-keyframes test { from { background: green; background-color: red; } 100% { background: blue } } "#, "@-webkit-keyframes test{0%{background:red}to{background:#00f}}@-moz-keyframes test{0%{background:red}to{background:#00f}}"); prefix_test( r#" @keyframes test { from { background: green; } to { background: blue } } "#, indoc! { r#" @-webkit-keyframes test { from { background: green; } to { background: #00f; } } @-moz-keyframes test { from { background: green; } to { background: #00f; } } @keyframes test { from { background: green; } to { background: #00f; } } "#}, Browsers { safari: Some(5 << 16), firefox: Some(6 << 16), ..Browsers::default() }, ); prefix_test( r#" @-webkit-keyframes test { from { background: green; } to { background: blue; } } @-moz-keyframes test { from { background: green; } to { background: blue; } } @keyframes test { from { background: green; } to { background: blue } } "#, indoc! { r#" @keyframes test { from { background: green; } to { background: #00f; } } "#}, Browsers { safari: Some(10 << 16), firefox: Some(17 << 16), ..Browsers::default() }, ); prefix_test( r#" @-webkit-keyframes test1 { from { background: green; } to { background: blue; } } @-moz-keyframes test2 { from { background: green; } to { background: blue; } } @keyframes test3 { from { background: green; } to { background: blue } } "#, indoc! { r#" @-webkit-keyframes test1 { from { background: green; } to { background: #00f; } } @-moz-keyframes test2 { from { background: green; } to { background: #00f; } } @keyframes test3 { from { background: green; } to { background: #00f; } } "#}, Browsers { safari: Some(10 << 16), firefox: Some(17 << 16), ..Browsers::default() }, ); prefix_test( r#" @-webkit-keyframes test { from { background: green; } to { background: red; } } @-moz-keyframes test { from { background: green; } to { background: pink; } } @keyframes test { from { background: green; } to { background: blue } } "#, indoc! { r#" @-webkit-keyframes test { from { background: green; } to { background: red; } } @-moz-keyframes test { from { background: green; } to { background: pink; } } @keyframes test { from { background: green; } to { background: #00f; } } "#}, Browsers { safari: Some(10 << 16), firefox: Some(17 << 16), ..Browsers::default() }, ); } #[test] fn test_important() { test( r#" .foo { align-items: center; justify-items: center !important; } "#, indoc! {r#" .foo { align-items: center; justify-items: center !important; } "#}, ); test( r#" .foo { justify-items: center !important; align-items: center; } "#, indoc! {r#" .foo { align-items: center; justify-items: center !important; } "#}, ); minify_test( r#" .foo { font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; } "#, ".foo{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important}", ); } #[test] fn test_calc() { minify_test(".foo { width: calc(20px * 2) }", ".foo{width:40px}"); minify_test(".foo { font-size: calc(100vw / 35) }", ".foo{font-size:2.85714vw}"); minify_test(".foo { width: calc(20px * 2 * 3) }", ".foo{width:120px}"); minify_test(".foo { width: calc(20px + 30px) }", ".foo{width:50px}"); minify_test(".foo { width: calc(20px + 30px + 40px) }", ".foo{width:90px}"); minify_test(".foo { width: calc(100% - 30px) }", ".foo{width:calc(100% - 30px)}"); minify_test( ".foo { width: calc(100% - 30px + 20px) }", ".foo{width:calc(100% - 10px)}", ); minify_test( ".foo { width: calc(20px + 100% - 30px) }", ".foo{width:calc(100% - 10px)}", ); minify_test( ".foo { width: calc(20px + 100% + 10vw - 30px) }", ".foo{width:calc(100% - 10px + 10vw)}", ); minify_test( ".foo { width: calc(20px + 100% - 30px) }", ".foo{width:calc(100% - 10px)}", ); minify_test( ".foo { width: calc(2 * (100% - 20px)) }", ".foo{width:calc(200% - 40px)}", ); minify_test( ".foo { width: calc((100% - 20px) * 2) }", ".foo{width:calc(200% - 40px)}", ); minify_test(".foo { width: calc(100% - 20px * 2) }", ".foo{width:calc(100% - 40px)}"); minify_test(".foo { width: calc(1px + 1px) }", ".foo{width:2px}"); minify_test(".foo { width: calc(100vw / 2) }", ".foo{width:50vw}"); minify_test(".foo { width: calc(50px - (20px - 30px)) }", ".foo{width:60px}"); minify_test(".foo { width: calc(100px - (100px - 100%)) }", ".foo{width:100%}"); minify_test( ".foo { width: calc(100px + (100px - 100%)) }", ".foo{width:calc(200px - 100%)}", ); minify_test( ".foo { width: calc(1px - (2em + 3%)) }", ".foo{width:calc(1px + -2em - 3%)}", ); // TODO: fix sign minify_test( ".foo { width: calc((100vw - 50em) / 2) }", ".foo{width:calc(50vw - 25em)}", ); minify_test( ".foo { width: calc(1px - (2em + 4vh + 3%)) }", ".foo{width:calc(1px + -2em - 4vh - 3%)}", ); // TODO minify_test( ".foo { width: calc(1px + (2em + (3vh + 4px))) }", ".foo{width:calc(2em + 3vh + 5px)}", ); minify_test( ".foo { width: calc(1px - (2em + 4px - 6vh) / 2) }", ".foo{width:calc(-1em - 1px + 3vh)}", ); minify_test( ".foo { width: calc(100% - calc(50% + 25px)) }", ".foo{width:calc(50% - 25px)}", ); minify_test(".foo { width: calc(1px/100) }", ".foo{width:.01px}"); minify_test( ".foo { width: calc(100vw / 2 - 6px + 0px) }", ".foo{width:calc(50vw - 6px)}", ); minify_test(".foo { width: calc(1px + 1) }", ".foo{width:calc(1px + 1)}"); minify_test( ".foo { width: calc( (1em - calc( 10px + 1em)) / 2) }", ".foo{width:-5px}", ); minify_test( ".foo { width: calc((100px - 1em) + (-50px + 1em)) }", ".foo{width:50px}", ); minify_test( ".foo { width: calc(100% + (2 * 100px) - ((75.37% - 63.5px) - 900px)) }", ".foo{width:calc(24.63% + 1163.5px)}", ); minify_test( ".foo { width: calc(((((100% + (2 * 30px) + 63.5px) / 0.7537) - (100vw - 60px)) / 2) + 30px) }", ".foo{width:calc(66.3394% + 141.929px - 50vw)}", ); minify_test( ".foo { width: calc(((75.37% - 63.5px) - 900px) + (2 * 100px)) }", ".foo{width:calc(75.37% - 763.5px)}", ); minify_test( ".foo { width: calc((900px - (10% - 63.5px)) + (2 * 100px)) }", ".foo{width:calc(1163.5px - 10%)}", ); minify_test(".foo { width: calc(500px/0) }", ".foo{width:calc(500px/0)}"); minify_test(".foo { width: calc(500px/2px) }", ".foo{width:calc(500px/2px)}"); minify_test(".foo { width: calc(100% / 3 * 3) }", ".foo{width:100%}"); minify_test(".foo { width: calc(+100px + +100px) }", ".foo{width:200px}"); minify_test(".foo { width: calc(+100px - +100px) }", ".foo{width:0}"); minify_test(".foo { width: calc(200px * +1) }", ".foo{width:200px}"); minify_test(".foo { width: calc(200px / +1) }", ".foo{width:200px}"); minify_test(".foo { width: calc(1.1e+1px + 1.1e+1px) }", ".foo{width:22px}"); minify_test(".foo { border-width: calc(1px + 2px) }", ".foo{border-width:3px}"); minify_test( ".foo { border-width: calc(1em + 2px + 2em + 3px) }", ".foo{border-width:calc(3em + 5px)}", ); minify_test( ".foo { border-width: min(1em, 2px) }", ".foo{border-width:min(1em,2px)}", ); minify_test( ".foo { border-width: min(1em + 2em, 2px + 2px) }", ".foo{border-width:min(3em,4px)}", ); minify_test( ".foo { border-width: min(1em + 2px, 2px + 1em) }", ".foo{border-width:min(1em + 2px,2px + 1em)}", ); minify_test( ".foo { border-width: min(1em + 2px + 2px, 2px + 1em + 1px) }", ".foo{border-width:min(1em + 4px,3px + 1em)}", ); minify_test( ".foo { border-width: min(2px + 1px, 3px + 4px) }", ".foo{border-width:3px}", ); minify_test( ".foo { border-width: min(1px, 1em, 2px, 3in) }", ".foo{border-width:min(1px,1em)}", ); minify_test( ".foo { border-width: max(1em, 2px) }", ".foo{border-width:max(1em,2px)}", ); minify_test( ".foo { border-width: max(1em + 2em, 2px + 2px) }", ".foo{border-width:max(3em,4px)}", ); minify_test( ".foo { border-width: max(1em + 2px, 2px + 1em) }", ".foo{border-width:max(1em + 2px,2px + 1em)}", ); minify_test( ".foo { border-width: max(1em + 2px + 2px, 2px + 1em + 1px) }", ".foo{border-width:max(1em + 4px,3px + 1em)}", ); minify_test( ".foo { border-width: max(2px + 1px, 3px + 4px) }", ".foo{border-width:7px}", ); minify_test( ".foo { border-width: max(1px, 1em, 2px, 3in) }", ".foo{border-width:max(3in,1em)}", ); minify_test(".foo { border-width: clamp(1px, 2px, 3px) }", ".foo{border-width:2px}"); minify_test(".foo { border-width: clamp(1px, 10px, 3px) }", ".foo{border-width:3px}"); minify_test(".foo { border-width: clamp(5px, 2px, 10px) }", ".foo{border-width:5px}"); minify_test( ".foo { border-width: clamp(100px, 2px, 10px) }", ".foo{border-width:100px}", ); minify_test( ".foo { border-width: clamp(5px + 5px, 5px + 7px, 10px + 20px) }", ".foo{border-width:12px}", ); minify_test( ".foo { border-width: clamp(1em, 2px, 4vh) }", ".foo{border-width:clamp(1em,2px,4vh)}", ); minify_test( ".foo { border-width: clamp(1em, 2em, 4vh) }", ".foo{border-width:min(2em,4vh)}", ); minify_test( ".foo { border-width: clamp(1em, 2vh, 4vh) }", ".foo{border-width:max(1em,2vh)}", ); minify_test( ".foo { border-width: clamp(1px, 1px + 2em, 4px) }", ".foo{border-width:clamp(1px,1px + 2em,4px)}", ); minify_test(".foo { border-width: clamp(1px, 2pt, 1in) }", ".foo{border-width:2pt}"); minify_test( ".foo { top: calc(-1 * clamp(1.75rem, 8vw, 4rem)) }", ".foo{top:calc(-1*clamp(1.75rem,8vw,4rem))}", ); minify_test( ".foo { top: calc(-1 * min(1.75rem, 8vw, 4rem)) }", ".foo{top:calc(-1*min(1.75rem,8vw))}", ); minify_test( ".foo { top: calc(-1 * max(1.75rem, 8vw, 4rem)) }", ".foo{top:calc(-1*max(4rem,8vw))}", ); minify_test( ".foo { top: calc(clamp(1.75rem, 8vw, 4rem) * -1) }", ".foo{top:calc(-1*clamp(1.75rem,8vw,4rem))}", ); minify_test( ".foo { top: calc(min(1.75rem, 8vw, 4rem) * -1) }", ".foo{top:calc(-1*min(1.75rem,8vw))}", ); minify_test( ".foo { top: calc(max(1.75rem, 8vw, 4rem) * -1) }", ".foo{top:calc(-1*max(4rem,8vw))}", ); minify_test( ".foo { top: calc(clamp(1.75rem, 8vw, 4rem) / 2) }", ".foo{top:calc(clamp(1.75rem,8vw,4rem)/2)}", ); minify_test( ".foo { top: calc(min(1.75rem, 8vw, 4rem) / 2) }", ".foo{top:calc(min(1.75rem,8vw)/2)}", ); minify_test( ".foo { top: calc(max(1.75rem, 8vw, 4rem) / 2) }", ".foo{top:calc(max(4rem,8vw)/2)}", ); minify_test( ".foo { top: calc(0.5 * clamp(1.75rem, 8vw, 4rem)) }", ".foo{top:calc(clamp(1.75rem,8vw,4rem)/2)}", ); minify_test( ".foo { top: calc(1 * clamp(1.75rem, 8vw, 4rem)) }", ".foo{top:calc(clamp(1.75rem,8vw,4rem))}", ); minify_test( ".foo { top: calc(2 * clamp(1.75rem, 8vw, 4rem) / 2) }", ".foo{top:calc(clamp(1.75rem,8vw,4rem))}", ); minify_test(".foo { width: max(0px, 1vw) }", ".foo{width:max(0px,1vw)}"); prefix_test( ".foo { border-width: clamp(1em, 2px, 4vh) }", indoc! { r#" .foo { border-width: max(1em, min(2px, 4vh)); } "#}, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( ".foo { border-width: clamp(1em, 2px, 4vh) }", indoc! { r#" .foo { border-width: clamp(1em, 2px, 4vh); } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); minify_test(".foo { width: calc(1vh + 2vh) }", ".foo{width:3vh}"); minify_test(".foo { width: calc(1dvh + 2dvh) }", ".foo{width:3dvh}"); minify_test(".foo { width: calc(1lvh + 2lvh) }", ".foo{width:3lvh}"); minify_test(".foo { width: calc(1svh + 2svh) }", ".foo{width:3svh}"); minify_test(".foo { width: calc(1sVmin + 2Svmin) }", ".foo{width:3svmin}"); minify_test(".foo { width: calc(1ic + 2ic) }", ".foo{width:3ic}"); minify_test(".foo { width: calc(1ric + 2ric) }", ".foo{width:3ric}"); minify_test(".foo { width: calc(1cap + 2cap) }", ".foo{width:3cap}"); minify_test(".foo { width: calc(1lh + 2lh) }", ".foo{width:3lh}"); minify_test(".foo { width: calc(1x + 2x) }", ".foo{width:calc(1x + 2x)}"); minify_test( ".foo { left: calc(50% - 100px + clamp(0px, calc(50vw - 50px), 100px)) }", ".foo{left:calc(50% - 100px + clamp(0px,50vw - 50px,100px))}", ); minify_test( ".foo { left: calc(10px + min(10px, 1rem) + max(2px, 1vw)) }", ".foo{left:calc(10px + min(10px,1rem) + max(2px,1vw))}", ); minify_test(".foo { width: round(22px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(nearest, 22px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(down, 22px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(to-zero, 22px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(up, 22px, 5px) }", ".foo{width:25px}"); minify_test(".foo { width: round(23px, 5px) }", ".foo{width:25px}"); minify_test(".foo { width: round(nearest, 23px, 5px) }", ".foo{width:25px}"); minify_test(".foo { width: round(down, 23px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(to-zero, 23px, 5px) }", ".foo{width:20px}"); minify_test(".foo { width: round(up, 23px, 5px) }", ".foo{width:25px}"); minify_test(".foo { width: round(22px, 5vw) }", ".foo{width:round(22px,5vw)}"); minify_test(".foo { rotate: round(22deg, 5deg) }", ".foo{rotate:20deg}"); minify_test(".foo { rotate: round(22deg, 5deg) }", ".foo{rotate:20deg}"); minify_test( ".foo { transition-duration: round(22ms, 5ms) }", ".foo{transition-duration:20ms}", ); minify_test(".foo { margin: round(to-zero, -23px, 5px) }", ".foo{margin:-20px}"); minify_test(".foo { margin: round(nearest, -23px, 5px) }", ".foo{margin:-25px}"); minify_test(".foo { margin: calc(10px * round(22, 5)) }", ".foo{margin:200px}"); minify_test(".foo { width: rem(18px, 5px) }", ".foo{width:3px}"); minify_test(".foo { width: rem(-18px, 5px) }", ".foo{width:-3px}"); minify_test(".foo { width: rem(18px, 5vw) }", ".foo{width:rem(18px,5vw)}"); minify_test(".foo { rotate: rem(-140deg, -90deg) }", ".foo{rotate:-50deg}"); minify_test(".foo { rotate: rem(140deg, -90deg) }", ".foo{rotate:50deg}"); minify_test(".foo { width: calc(10px * rem(18, 5)) }", ".foo{width:30px}"); minify_test(".foo { width: mod(18px, 5px) }", ".foo{width:3px}"); minify_test(".foo { width: mod(-18px, 5px) }", ".foo{width:2px}"); minify_test(".foo { rotate: mod(-140deg, -90deg) }", ".foo{rotate:-50deg}"); minify_test(".foo { rotate: mod(140deg, -90deg) }", ".foo{rotate:-40deg}"); minify_test(".foo { width: mod(18px, 5vw) }", ".foo{width:mod(18px,5vw)}"); minify_test( ".foo { transform: rotateX(mod(140deg, -90deg)) rotateY(rem(140deg, -90deg)) }", ".foo{transform:rotateX(-40deg)rotateY(50deg)}", ); minify_test(".foo { width: calc(10px * mod(18, 5)) }", ".foo{width:30px}"); } #[test] fn test_trig() { minify_test(".foo { width: calc(2px * pi); }", ".foo{width:6.28319px}"); minify_test(".foo { width: calc(2px / pi); }", ".foo{width:.63662px}"); // minify_test( // ".foo { width: calc(2px * infinity); }", // ".foo{width:calc(2px*infinity)}", // ); // minify_test( // ".foo { width: calc(2px * -infinity); }", // ".foo{width:calc(2px*-infinity)}", // ); minify_test(".foo { width: calc(100px * sin(45deg))", ".foo{width:70.7107px}"); minify_test(".foo { width: calc(100px * sin(.125turn))", ".foo{width:70.7107px}"); minify_test( ".foo { width: calc(100px * sin(3.14159265 / 4))", ".foo{width:70.7107px}", ); minify_test(".foo { width: calc(100px * sin(pi / 4))", ".foo{width:70.7107px}"); minify_test( ".foo { width: calc(100px * sin(22deg + 23deg))", ".foo{width:70.7107px}", ); minify_test(".foo { width: calc(2px * cos(45deg))", ".foo{width:1.41421px}"); minify_test(".foo { width: calc(2px * tan(45deg))", ".foo{width:2px}"); minify_test(".foo { rotate: asin(sin(45deg))", ".foo{rotate:45deg}"); minify_test(".foo { rotate: asin(1)", ".foo{rotate:90deg}"); minify_test(".foo { rotate: asin(-1)", ".foo{rotate:-90deg}"); minify_test(".foo { rotate: asin(0.5)", ".foo{rotate:30deg}"); minify_test(".foo { rotate: asin(45deg)", ".foo{rotate:asin(45deg)}"); // invalid minify_test(".foo { rotate: asin(-20)", ".foo{rotate:asin(-20)}"); // evaluates to NaN minify_test(".foo { width: asin(sin(45deg))", ".foo{width:asin(sin(45deg))}"); // invalid minify_test(".foo { rotate: acos(cos(45deg))", ".foo{rotate:45deg}"); minify_test(".foo { rotate: acos(-1)", ".foo{rotate:180deg}"); minify_test(".foo { rotate: acos(0)", ".foo{rotate:90deg}"); minify_test(".foo { rotate: acos(1)", ".foo{rotate:none}"); minify_test(".foo { rotate: acos(45deg)", ".foo{rotate:acos(45deg)}"); // invalid minify_test(".foo { rotate: acos(-20)", ".foo{rotate:acos(-20)}"); // evaluates to NaN minify_test(".foo { rotate: atan(tan(45deg))", ".foo{rotate:45deg}"); minify_test(".foo { rotate: atan(1)", ".foo{rotate:45deg}"); minify_test(".foo { rotate: atan(0)", ".foo{rotate:none}"); minify_test(".foo { rotate: atan(45deg)", ".foo{rotate:atan(45deg)}"); // invalid minify_test(".foo { rotate: atan2(1px, -1px)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1vw, -1vw)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1, -1)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1ms, -1ms)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1%, -1%)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1deg, -1deg)", ".foo{rotate:135deg}"); minify_test(".foo { rotate: atan2(1cm, 1mm)", ".foo{rotate:84.2894deg}"); minify_test(".foo { rotate: atan2(0, -1)", ".foo{rotate:180deg}"); minify_test(".foo { rotate: atan2(-1, 1)", ".foo{rotate:-45deg}"); // incompatible units minify_test(".foo { rotate: atan2(1px, -1vw)", ".foo{rotate:atan2(1px,-1vw)}"); } #[test] fn test_exp() { minify_test(".foo { width: hypot()", ".foo{width:hypot()}"); minify_test(".foo { width: hypot(1px)", ".foo{width:1px}"); minify_test(".foo { width: hypot(1px, 2px)", ".foo{width:2.23607px}"); minify_test(".foo { width: hypot(1px, 2px, 3px)", ".foo{width:3.74166px}"); minify_test(".foo { width: hypot(1px, 2vw)", ".foo{width:hypot(1px,2vw)}"); minify_test(".foo { width: hypot(1px, 2px, 3vw)", ".foo{width:hypot(1px,2px,3vw)}"); minify_test(".foo { width: calc(100px * hypot(3, 4))", ".foo{width:500px}"); minify_test(".foo { width: calc(1px * pow(2, sqrt(100))", ".foo{width:1024px}"); minify_test(".foo { width: calc(100px * pow(2, pow(2, 2)", ".foo{width:1600px}"); minify_test(".foo { width: calc(1px * log(1))", ".foo{width:0}"); minify_test(".foo { width: calc(1px * log(10, 10))", ".foo{width:1px}"); minify_test(".foo { width: calc(1px * exp(0))", ".foo{width:1px}"); minify_test(".foo { width: calc(1px * log(e))", ".foo{width:1px}"); minify_test(".foo { width: calc(1px * (e - exp(1)))", ".foo{width:0}"); minify_test( ".foo { width: calc(1px * (exp(log(1) + exp(0)*2))", ".foo{width:7.38906px}", ); } #[test] fn test_sign() { minify_test(".foo { width: abs(1px)", ".foo{width:1px}"); minify_test(".foo { width: abs(-1px)", ".foo{width:1px}"); minify_test(".foo { width: abs(1%)", ".foo{width:abs(1%)}"); // spec says percentages must be against resolved value minify_test(".foo { width: calc(10px * sign(-1vw)", ".foo{width:-10px}"); minify_test(".foo { width: calc(10px * sign(1%)", ".foo{width:calc(10px*sign(1%))}"); } #[test] fn test_box_shadow() { minify_test( ".foo { box-shadow: 64px 64px 12px 40px rgba(0,0,0,0.4) }", ".foo{box-shadow:64px 64px 12px 40px #0006}", ); minify_test( ".foo { box-shadow: 12px 12px 0px 8px rgba(0,0,0,0.4) inset }", ".foo{box-shadow:inset 12px 12px 0 8px #0006}", ); minify_test( ".foo { box-shadow: inset 12px 12px 0px 8px rgba(0,0,0,0.4) }", ".foo{box-shadow:inset 12px 12px 0 8px #0006}", ); minify_test( ".foo { box-shadow: 12px 12px 8px 0px rgba(0,0,0,0.4) }", ".foo{box-shadow:12px 12px 8px #0006}", ); minify_test( ".foo { box-shadow: 12px 12px 0px 0px rgba(0,0,0,0.4) }", ".foo{box-shadow:12px 12px #0006}", ); minify_test( ".foo { box-shadow: 64px 64px 12px 40px rgba(0,0,0,0.4), 12px 12px 0px 8px rgba(0,0,0,0.4) inset }", ".foo{box-shadow:64px 64px 12px 40px #0006,inset 12px 12px 0 8px #0006}", ); prefix_test( ".foo { box-shadow: 12px 12px lab(40% 56.6 39) }", indoc! { r#" .foo { box-shadow: 12px 12px #b32323; box-shadow: 12px 12px lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { box-shadow: 12px 12px lab(40% 56.6 39) }", indoc! { r#" .foo { -webkit-box-shadow: 12px 12px #b32323; box-shadow: 12px 12px #b32323; box-shadow: 12px 12px lab(40% 56.6 39); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { box-shadow: 12px 12px lab(40% 56.6 39), 12px 12px yellow }", indoc! { r#" .foo { -webkit-box-shadow: 12px 12px #b32323, 12px 12px #ff0; box-shadow: 12px 12px #b32323, 12px 12px #ff0; box-shadow: 12px 12px lab(40% 56.6 39), 12px 12px #ff0; } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { -webkit-box-shadow: 12px 12px #0006 }", indoc! { r#" .foo { -webkit-box-shadow: 12px 12px rgba(0, 0, 0, .4); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { -webkit-box-shadow: 12px 12px #0006; -moz-box-shadow: 12px 12px #0009; }", indoc! { r#" .foo { -webkit-box-shadow: 12px 12px rgba(0, 0, 0, .4); -moz-box-shadow: 12px 12px rgba(0, 0, 0, .6); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { -webkit-box-shadow: 12px 12px #0006; -moz-box-shadow: 12px 12px #0006; box-shadow: 12px 12px #0006; }", indoc! { r#" .foo { box-shadow: 12px 12px #0006; } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo { box-shadow: var(--foo) 12px lab(40% 56.6 39) }", indoc! { r#" .foo { box-shadow: var(--foo) 12px #b32323; } @supports (color: lab(0% 0 0)) { .foo { box-shadow: var(--foo) 12px lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] fn test_media() { minify_test( "@media (min-width: 240px) { .foo { color: chartreuse }}", "@media (width>=240px){.foo{color:#7fff00}}", ); minify_test( "@media (width < 240px) { .foo { color: chartreuse }}", "@media (width<240px){.foo{color:#7fff00}}", ); minify_test( "@media (width <= 240px) { .foo { color: chartreuse }}", "@media (width<=240px){.foo{color:#7fff00}}", ); minify_test( "@media (width > 240px) { .foo { color: chartreuse }}", "@media (width>240px){.foo{color:#7fff00}}", ); minify_test( "@media (width >= 240px) { .foo { color: chartreuse }}", "@media (width>=240px){.foo{color:#7fff00}}", ); minify_test( "@media (240px < width) { .foo { color: chartreuse }}", "@media (width>240px){.foo{color:#7fff00}}", ); minify_test( "@media (240px <= width) { .foo { color: chartreuse }}", "@media (width>=240px){.foo{color:#7fff00}}", ); minify_test( "@media (240px > width) { .foo { color: chartreuse }}", "@media (width<240px){.foo{color:#7fff00}}", ); minify_test( "@media (240px >= width) { .foo { color: chartreuse }}", "@media (width<=240px){.foo{color:#7fff00}}", ); minify_test( "@media (100px < width < 200px) { .foo { color: chartreuse }}", "@media (100px=30em) and (width<=50em){.foo{color:#7fff00}}", ); minify_test( "@media screen, print { .foo { color: chartreuse }}", "@media screen,print{.foo{color:#7fff00}}", ); minify_test( "@media (hover: hover) { .foo { color: chartreuse }}", "@media (hover:hover){.foo{color:#7fff00}}", ); minify_test( "@media (hover) { .foo { color: chartreuse }}", "@media (hover){.foo{color:#7fff00}}", ); minify_test( "@media (aspect-ratio: 11/5) { .foo { color: chartreuse }}", "@media (aspect-ratio:11/5){.foo{color:#7fff00}}", ); minify_test( "@media (aspect-ratio: 2/1) { .foo { color: chartreuse }}", "@media (aspect-ratio:2){.foo{color:#7fff00}}", ); minify_test( "@media (aspect-ratio: 2) { .foo { color: chartreuse }}", "@media (aspect-ratio:2){.foo{color:#7fff00}}", ); minify_test( "@media not screen and (color) { .foo { color: chartreuse }}", "@media not screen and (color){.foo{color:#7fff00}}", ); minify_test( "@media only screen and (color) { .foo { color: chartreuse }}", "@media only screen and (color){.foo{color:#7fff00}}", ); minify_test( "@media (update: slow) or (hover: none) { .foo { color: chartreuse }}", "@media (update:slow) or (hover:none){.foo{color:#7fff00}}", ); minify_test( "@media (width < 600px) and (height < 600px) { .foo { color: chartreuse }}", "@media (width<600px) and (height<600px){.foo{color:#7fff00}}", ); minify_test( "@media (not (color)) or (hover) { .foo { color: chartreuse }}", "@media (not (color)) or (hover){.foo{color:#7fff00}}", ); error_test( "@media (example, all,), speech { .foo { color: chartreuse }}", ParserError::UnexpectedToken(Token::Comma), ); error_test( "@media &test, speech { .foo { color: chartreuse }}", ParserError::UnexpectedToken(Token::Delim('&')), ); error_test( "@media &test { .foo { color: chartreuse }}", ParserError::UnexpectedToken(Token::Delim('&')), ); minify_test( "@media (min-width: calc(200px + 40px)) { .foo { color: chartreuse }}", "@media (width>=240px){.foo{color:#7fff00}}", ); minify_test( "@media (min-width: calc(1em + 5px)) { .foo { color: chartreuse }}", "@media (width>=calc(1em + 5px)){.foo{color:#7fff00}}", ); minify_test("@media { .foo { color: chartreuse }}", ".foo{color:#7fff00}"); minify_test("@media all { .foo { color: chartreuse }}", ".foo{color:#7fff00}"); minify_test( "@media not (((color) or (hover))) { .foo { color: chartreuse }}", "@media not ((color) or (hover)){.foo{color:#7fff00}}", ); minify_test( "@media (hover) and ((color) and (test)) { .foo { color: chartreuse }}", "@media (hover) and (color) and (test){.foo{color:#7fff00}}", ); minify_test( "@media (grid: 1) { .foo { color: chartreuse }}", "@media (grid:1){.foo{color:#7fff00}}", ); minify_test( "@media (width >= calc(2px + 4px)) { .foo { color: chartreuse }}", "@media (width>=6px){.foo{color:#7fff00}}", ); prefix_test( r#" @media (width >= 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (min-width: 240px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width >= 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (width >= 240px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(64 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (color > 2) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (min-color: 3) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (color < 2) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (max-color: 1) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width > 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (min-width: 240.001px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width <= 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (max-width: 240px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width <= 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (width <= 240px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(64 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width < 240px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (max-width: 239.999px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(60 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (100px <= width <= 200px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (min-width: 100px) and (max-width: 200px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media not (100px <= width <= 200px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media not ((min-width: 100px) and (max-width: 200px)) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (hover) and (100px <= width <= 200px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (hover) and (min-width: 100px) and (max-width: 200px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (hover) or (100px <= width <= 200px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (hover) or ((min-width: 100px) and (max-width: 200px)) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (100px < width < 200px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (min-width: 100.001px) and (max-width: 199.999px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (200px >= width >= 100px) { .foo { color: chartreuse; } } "#, indoc! { r#" @media (max-width: 200px) and (min-width: 100px) { .foo { color: #7fff00; } } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); test( r#" @media not all { .a { color: green; } } "#, "\n", ); prefix_test( r#" @media (width > calc(1px + 1rem)) { .foo { color: yellow; } } "#, indoc! { r#" @media (min-width: calc(1.001px + 1rem)) { .foo { color: #ff0; } } "#}, Browsers { chrome: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width > max(10px, 1rem)) { .foo { color: yellow; } } "#, indoc! { r#" @media (min-width: calc(max(10px, 1rem) + .001px)) { .foo { color: #ff0; } } "#}, Browsers { chrome: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (width > 0) { .foo { color: yellow; } } "#, indoc! { r#" @media (min-width: .001px) { .foo { color: #ff0; } } "#}, Browsers { chrome: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (min-resolution: 2dppx) { .foo { color: yellow; } } "#, indoc! { r#" @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (min-resolution: 2dppx) { .foo { color: yellow; } } "#, indoc! { r#" @media (min--moz-device-pixel-ratio: 2), (min-resolution: 2dppx) { .foo { color: #ff0; } } "#}, Browsers { firefox: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (resolution > 2dppx) { .foo { color: yellow; } } "#, indoc! { r#" @media (-webkit-min-device-pixel-ratio: 2.001), (min-resolution: 2.001dppx) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (resolution >= 300dpi) { .foo { color: yellow; } } "#, indoc! { r#" @media (-webkit-min-device-pixel-ratio: 3.125), (min-resolution: 300dpi) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (min-resolution: 113.38dpcm) { .foo { color: yellow; } } "#, indoc! { r#" @media (-webkit-min-device-pixel-ratio: 2.99985), (min--moz-device-pixel-ratio: 2.99985), (min-resolution: 113.38dpcm) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), firefox: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (color) and (min-resolution: 2dppx) { .foo { color: yellow; } } "#, indoc! { r#" @media (color) and (-webkit-min-device-pixel-ratio: 2), (color) and (min-resolution: 2dppx) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" @media (min-resolution: 2dppx), (min-resolution: 192dpi) { .foo { color: yellow; } } "#, indoc! { r#" @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (min-resolution: 2dppx), (min-resolution: 192dpi) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), firefox: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" @media only screen and (min-resolution: 124.8dpi) { .foo { color: yellow; } } "#, indoc! { r#" @media only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-resolution: 124.8dpi) { .foo { color: #ff0; } } "#}, Browsers { safari: Some(15 << 16), firefox: Some(10 << 16), ..Browsers::default() }, ); error_test( "@media (min-width: hi) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (width >= hi) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (width >= 2/1) { .foo { color: chartreuse }}", ParserError::UnexpectedToken(Token::Delim('/')), ); error_test( "@media (600px <= min-height) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (scan >= 1) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (min-scan: interlace) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (1px <= width <= bar) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (1px <= min-width <= 2px) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (1px <= scan <= 2px) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (grid: 10) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); error_test( "@media (prefers-color-scheme = dark) { .foo { color: chartreuse }}", ParserError::InvalidMediaQuery, ); } #[test] fn test_merge_layers() { test( r#" @layer foo { .foo { color: red; } } @layer foo { .foo { background: #fff; } .baz { color: #fff; } } "#, indoc! {r#" @layer foo { .foo { color: red; background: #fff; } .baz { color: #fff; } } "#}, ); } #[test] fn test_merge_rules() { test( r#" .foo { color: red; } .bar { color: red; } "#, indoc! {r#" .foo, .bar { color: red; } "#}, ); test( r#" .foo { color: red; } .foo { background: green; } "#, indoc! {r#" .foo { color: red; background: green; } "#}, ); test( r#" .foo { color: red; } .foo { background: green !important; } "#, indoc! {r#" .foo { color: red; background: green !important; } "#}, ); test( r#" .foo { background: red; } .foo { background: green; } "#, indoc! {r#" .foo { background: green; } "#}, ); test( r#" .foo { color: red; } .bar { background: green; } "#, indoc! {r#" .foo { color: red; } .bar { background: green; } "#}, ); test( r#" .foo { color: red; } .baz { color: blue; } .bar { color: red; } "#, indoc! {r#" .foo { color: red; } .baz { color: #00f; } .bar { color: red; } "#}, ); test( r#" .foo { background: red; } .bar { background: red; } .foo { color: green; } .bar { color: green; } "#, indoc! {r#" .foo, .bar { color: green; background: red; } "#}, ); test( r#" .foo, .bar { background: red; } .foo { color: green; } .bar { color: green; } "#, indoc! {r#" .foo, .bar { color: green; background: red; } "#}, ); test( r#" .foo { background: red; } .foo { color: green; } .bar { background: red; } .bar { color: green; } "#, indoc! {r#" .foo, .bar { color: green; background: red; } "#}, ); test( r#" [foo="bar"] { color: red; } .bar { color: red; } "#, indoc! {r#" [foo="bar"] { color: red; } .bar { color: red; } "#}, ); test( r#" .a { color: red; } .b { color: green; } .a { color: red; } "#, indoc! {r#" .b { color: green; } .a { color: red; } "#}, ); test( r#" .a { color: red; } .b { color: green; } .a { color: pink; } "#, indoc! {r#" .b { color: green; } .a { color: pink; } "#}, ); test( r#" .a:foo(#000) { color: red; } .b { color: green; } .a:foo(#ff0) { color: pink; } "#, indoc! {r#" .a:foo(#000) { color: red; } .b { color: green; } .a:foo(#ff0) { color: pink; } "#}, ); test( r#" .a { border-radius: 10px; } .b { color: green; } .a { border-radius: 10px; } "#, indoc! {r#" .b { color: green; } .a { border-radius: 10px; } "#}, ); test( r#" .a { border-radius: 10px; } .b { color: green; } .a { -webkit-border-radius: 10px; } "#, indoc! {r#" .a { border-radius: 10px; } .b { color: green; } .a { -webkit-border-radius: 10px; } "#}, ); test( r#" .a { border-radius: 10px; } .b { color: green; } .a { border-radius: var(--foo); } "#, indoc! {r#" .b { color: green; } .a { border-radius: var(--foo); } "#}, ); test( r#" .a { border-radius: 10px; } .b { color: green; } .c { border-radius: 20px; } "#, indoc! {r#" .a { border-radius: 10px; } .b { color: green; } .c { border-radius: 20px; } "#}, ); test( r#" @media print { .a { color: red; } .b { color: green; } .a { color: red; } } "#, indoc! {r#" @media print { .b { color: green; } .a { color: red; } } "#}, ); test( r#" .a { border-radius: 10px; } .b { color: green; } .a { border-radius: 20px; color: pink; } "#, indoc! {r#" .a { border-radius: 10px; } .b { color: green; } .a { color: pink; border-radius: 20px; } "#}, ); prefix_test( r#" [foo="bar"] { color: red; } .bar { color: red; } "#, indoc! {r#" [foo="bar"] { color: red; } .bar { color: red; } "#}, Browsers { ie: Some(6 << 16), ..Browsers::default() }, ); prefix_test( r#" [foo="bar"] { color: red; } .bar { color: red; } "#, indoc! {r#" [foo="bar"], .bar { color: red; } "#}, Browsers { ie: Some(10 << 16), ..Browsers::default() }, ); test( r#" .foo:-moz-read-only { color: red; } "#, indoc! {r#" .foo:-moz-read-only { color: red; } "#}, ); test( r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#, indoc! {r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#}, ); prefix_test( r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#, indoc! {r#" .foo:read-only { color: red; } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:-moz-read-only { color: red; } .bar { color: yellow; } .foo:read-only { color: red; } "#, indoc! {r#" .foo:-moz-read-only { color: red; } .bar { color: #ff0; } .foo:read-only { color: red; } "#}, Browsers { firefox: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#, indoc! {r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#}, Browsers { firefox: Some(36 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:read-only { color: red; } "#, indoc! {r#" .foo:-moz-read-only { color: red; } .foo:read-only { color: red; } "#}, Browsers { firefox: Some(36 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:-webkit-full-screen { color: red; } .foo:-moz-full-screen { color: red; } .foo:-ms-fullscreen { color: red; } .foo:fullscreen { color: red; } "#, indoc! {r#" .foo:fullscreen { color: red; } "#}, Browsers { chrome: Some(96 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:fullscreen { color: red; } "#, indoc! {r#" .foo:-webkit-full-screen { color: red; } .foo:-moz-full-screen { color: red; } .foo:-ms-fullscreen { color: red; } .foo:fullscreen { color: red; } "#}, Browsers { chrome: Some(45 << 16), firefox: Some(45 << 16), ie: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo::placeholder { color: red; } "#, indoc! {r#" .foo::-webkit-input-placeholder { color: red; } .foo::-moz-placeholder { color: red; } .foo::-ms-input-placeholder { color: red; } .foo::placeholder { color: red; } "#}, Browsers { chrome: Some(45 << 16), firefox: Some(45 << 16), ie: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo::file-selector-button { color: red; } "#, indoc! {r#" .foo::-webkit-file-upload-button { color: red; } .foo::-ms-browse { color: red; } .foo::file-selector-button { color: red; } "#}, Browsers { chrome: Some(84 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo::file-selector-button { margin-inline-start: 2px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi))::-webkit-file-upload-button { margin-left: 2px; } .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi))::-ms-browse { margin-left: 2px; } .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi))::file-selector-button { margin-left: 2px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi))::-webkit-file-upload-button { margin-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi))::-ms-browse { margin-right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi))::file-selector-button { margin-right: 2px; } "#}, Browsers { chrome: Some(84 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); } #[test] fn test_merge_media_rules() { test( r#" @media (hover) { .foo { color: red; } } @media (hover) { .foo { background: #fff; } .baz { color: #fff; } } "#, indoc! {r#" @media (hover) { .foo { color: red; background: #fff; } .baz { color: #fff; } } "#}, ); test( r#" @media (hover) { .foo { color: red; } } @media (min-width: 250px) { .foo { background: #fff; } .baz { color: #fff; } } "#, indoc! {r#" @media (hover) { .foo { color: red; } } @media (width >= 250px) { .foo { background: #fff; } .baz { color: #fff; } } "#}, ); } #[test] fn test_merge_supports() { test( r#" @supports (flex: 1) { .foo { color: red; } } @supports (flex: 1) { .foo { background: #fff; } .baz { color: #fff; } } "#, indoc! {r#" @supports (flex: 1) { .foo { color: red; background: #fff; } .baz { color: #fff; } } "#}, ); test( r#" @supports (flex: 1) { .foo { color: red; } } @supports (display: grid) { .foo { background: #fff; } .baz { color: #fff; } } "#, indoc! {r#" @supports (flex: 1) { .foo { color: red; } } @supports (display: grid) { .foo { background: #fff; } .baz { color: #fff; } } "#}, ); } #[test] fn test_opacity() { minify_test(".foo { opacity: 0 }", ".foo{opacity:0}"); minify_test(".foo { opacity: 0% }", ".foo{opacity:0}"); minify_test(".foo { opacity: 0.5 }", ".foo{opacity:.5}"); minify_test(".foo { opacity: 50% }", ".foo{opacity:.5}"); minify_test(".foo { opacity: 1 }", ".foo{opacity:1}"); minify_test(".foo { opacity: 100% }", ".foo{opacity:1}"); } #[test] fn test_transitions() { minify_test(".foo { transition-duration: 500ms }", ".foo{transition-duration:.5s}"); minify_test(".foo { transition-duration: .5s }", ".foo{transition-duration:.5s}"); minify_test(".foo { transition-duration: 99ms }", ".foo{transition-duration:99ms}"); minify_test(".foo { transition-duration: .099s }", ".foo{transition-duration:99ms}"); minify_test(".foo { transition-duration: 2000ms }", ".foo{transition-duration:2s}"); minify_test(".foo { transition-duration: 2s }", ".foo{transition-duration:2s}"); minify_test( ".foo { transition-duration: calc(1s - 50ms) }", ".foo{transition-duration:.95s}", ); minify_test( ".foo { transition-duration: calc(1s - 50ms + 2s) }", ".foo{transition-duration:2.95s}", ); minify_test( ".foo { transition-duration: calc((1s - 50ms) * 2) }", ".foo{transition-duration:1.9s}", ); minify_test( ".foo { transition-duration: calc(2 * (1s - 50ms)) }", ".foo{transition-duration:1.9s}", ); minify_test( ".foo { transition-duration: calc((2s + 50ms) - (1s - 50ms)) }", ".foo{transition-duration:1.1s}", ); minify_test( ".foo { transition-duration: 500ms, 50ms }", ".foo{transition-duration:.5s,50ms}", ); minify_test(".foo { transition-delay: 500ms }", ".foo{transition-delay:.5s}"); minify_test( ".foo { transition-property: background }", ".foo{transition-property:background}", ); minify_test( ".foo { transition-property: background, opacity }", ".foo{transition-property:background,opacity}", ); minify_test( ".foo { transition-timing-function: linear }", ".foo{transition-timing-function:linear}", ); minify_test( ".foo { transition-timing-function: ease }", ".foo{transition-timing-function:ease}", ); minify_test( ".foo { transition-timing-function: ease-in }", ".foo{transition-timing-function:ease-in}", ); minify_test( ".foo { transition-timing-function: ease-out }", ".foo{transition-timing-function:ease-out}", ); minify_test( ".foo { transition-timing-function: ease-in-out }", ".foo{transition-timing-function:ease-in-out}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1) }", ".foo{transition-timing-function:ease}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0.42, 0, 1, 1) }", ".foo{transition-timing-function:ease-in}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0, 0, 0.58, 1) }", ".foo{transition-timing-function:ease-out}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1) }", ".foo{transition-timing-function:ease-in-out}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0.58, 0.2, 0.11, 1.2) }", ".foo{transition-timing-function:cubic-bezier(.58,.2,.11,1.2)}", ); minify_test( ".foo { transition-timing-function: step-start }", ".foo{transition-timing-function:step-start}", ); minify_test( ".foo { transition-timing-function: step-end }", ".foo{transition-timing-function:step-end}", ); minify_test( ".foo { transition-timing-function: steps(1, start) }", ".foo{transition-timing-function:step-start}", ); minify_test( ".foo { transition-timing-function: steps(1, jump-start) }", ".foo{transition-timing-function:step-start}", ); minify_test( ".foo { transition-timing-function: steps(1, end) }", ".foo{transition-timing-function:step-end}", ); minify_test( ".foo { transition-timing-function: steps(1, jump-end) }", ".foo{transition-timing-function:step-end}", ); minify_test( ".foo { transition-timing-function: steps(5, jump-start) }", ".foo{transition-timing-function:steps(5,start)}", ); minify_test( ".foo { transition-timing-function: steps(5, jump-end) }", ".foo{transition-timing-function:steps(5,end)}", ); minify_test( ".foo { transition-timing-function: steps(5, jump-both) }", ".foo{transition-timing-function:steps(5,jump-both)}", ); minify_test( ".foo { transition-timing-function: ease-in-out, cubic-bezier(0.42, 0, 1, 1) }", ".foo{transition-timing-function:ease-in-out,ease-in}", ); minify_test( ".foo { transition-timing-function: cubic-bezier(0.42, 0, 1, 1), cubic-bezier(0.58, 0.2, 0.11, 1.2) }", ".foo{transition-timing-function:ease-in,cubic-bezier(.58,.2,.11,1.2)}", ); minify_test( ".foo { transition-timing-function: step-start, steps(5, jump-start) }", ".foo{transition-timing-function:step-start,steps(5,start)}", ); minify_test(".foo { transition: width 2s ease }", ".foo{transition:width 2s}"); minify_test( ".foo { transition: width 2s ease, height 1000ms cubic-bezier(0.25, 0.1, 0.25, 1) }", ".foo{transition:width 2s,height 1s}", ); minify_test(".foo { transition: width 2s 1s }", ".foo{transition:width 2s 1s}"); minify_test(".foo { transition: width 2s ease 1s }", ".foo{transition:width 2s 1s}"); minify_test( ".foo { transition: ease-in 1s width 4s }", ".foo{transition:width 1s ease-in 4s}", ); minify_test(".foo { transition: opacity 0s .6s }", ".foo{transition:opacity 0s .6s}"); test( r#" .foo { transition-property: opacity; transition-duration: 0.09s; transition-timing-function: ease-in-out; transition-delay: 500ms; } "#, indoc! {r#" .foo { transition: opacity 90ms ease-in-out .5s; } "#}, ); test( r#" .foo { transition: opacity 2s; transition-timing-function: ease; transition-delay: 500ms; } "#, indoc! {r#" .foo { transition: opacity 2s .5s; } "#}, ); test( r#" .foo { transition: opacity 500ms; transition-timing-function: var(--ease); } "#, indoc! {r#" .foo { transition: opacity .5s; transition-timing-function: var(--ease); } "#}, ); test( r#" .foo { transition-property: opacity; transition-duration: 0.09s; transition-timing-function: ease-in-out; transition-delay: 500ms; transition: color 2s; } "#, indoc! {r#" .foo { transition: color 2s; } "#}, ); test( r#" .foo { transition-property: opacity, color; transition-duration: 2s, 4s; transition-timing-function: ease-in-out, ease-in; transition-delay: 500ms, 0s; } "#, indoc! {r#" .foo { transition: opacity 2s ease-in-out .5s, color 4s ease-in; } "#}, ); test( r#" .foo { transition-property: opacity, color; transition-duration: 2s; transition-timing-function: ease-in-out; transition-delay: 500ms; } "#, indoc! {r#" .foo { transition: opacity 2s ease-in-out .5s, color 2s ease-in-out .5s; } "#}, ); test( r#" .foo { transition-property: opacity, color, width, height; transition-duration: 2s, 4s; transition-timing-function: ease; transition-delay: 0s; } "#, indoc! {r#" .foo { transition: opacity 2s, color 4s, width 2s, height 4s; } "#}, ); test( r#" .foo { -webkit-transition-property: opacity, color; -webkit-transition-duration: 2s, 4s; -webkit-transition-timing-function: ease-in-out, ease-in; -webkit-transition-delay: 500ms, 0s; } "#, indoc! {r#" .foo { -webkit-transition: opacity 2s ease-in-out .5s, color 4s ease-in; } "#}, ); test( r#" .foo { -webkit-transition-property: opacity, color; -webkit-transition-duration: 2s, 4s; -webkit-transition-timing-function: ease-in-out, ease-in; -webkit-transition-delay: 500ms, 0s; -moz-transition-property: opacity, color; -moz-transition-duration: 2s, 4s; -moz-transition-timing-function: ease-in-out, ease-in; -moz-transition-delay: 500ms, 0s; transition-property: opacity, color; transition-duration: 2s, 4s; transition-timing-function: ease-in-out, ease-in; transition-delay: 500ms, 0s; } "#, indoc! {r#" .foo { -webkit-transition: opacity 2s ease-in-out .5s, color 4s ease-in; -moz-transition: opacity 2s ease-in-out .5s, color 4s ease-in; transition: opacity 2s ease-in-out .5s, color 4s ease-in; } "#}, ); test( r#" .foo { -webkit-transition-property: opacity, color; -moz-transition-property: opacity, color; transition-property: opacity, color; -webkit-transition-duration: 2s, 4s; -moz-transition-duration: 2s, 4s; transition-duration: 2s, 4s; -webkit-transition-timing-function: ease-in-out, ease-in; transition-timing-function: ease-in-out, ease-in; -moz-transition-timing-function: ease-in-out, ease-in; -webkit-transition-delay: 500ms, 0s; -moz-transition-delay: 500ms, 0s; transition-delay: 500ms, 0s; } "#, indoc! {r#" .foo { -webkit-transition: opacity 2s ease-in-out .5s, color 4s ease-in; -moz-transition: opacity 2s ease-in-out .5s, color 4s ease-in; transition: opacity 2s ease-in-out .5s, color 4s ease-in; } "#}, ); test( r#" .foo { -webkit-transition-property: opacity; -moz-transition-property: color; transition-property: opacity, color; -webkit-transition-duration: 2s; -moz-transition-duration: 4s; transition-duration: 2s, 4s; -webkit-transition-timing-function: ease-in-out; -moz-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out, ease-in; -webkit-transition-delay: 500ms; -moz-transition-delay: 0s; transition-delay: 500ms, 0s; } "#, indoc! {r#" .foo { -webkit-transition-property: opacity; -moz-transition-property: color; transition-property: opacity, color; -webkit-transition-duration: 2s; -moz-transition-duration: 4s; transition-duration: 2s, 4s; -webkit-transition-timing-function: ease-in-out; -moz-transition-timing-function: ease-in-out; -webkit-transition-delay: .5s; transition-timing-function: ease-in-out, ease-in; -moz-transition-delay: 0s; transition-delay: .5s, 0s; } "#}, ); test( r#" .foo { -webkit-transition-property: opacity; transition-property: opacity, color; -moz-transition-property: color; -webkit-transition-duration: 2s; transition-duration: 2s, 4s; -moz-transition-duration: 4s; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out, ease-in; -moz-transition-timing-function: ease-in-out; -webkit-transition-delay: 500ms; transition-delay: 500ms, 0s; -moz-transition-delay: 0s; } "#, indoc! {r#" .foo { -webkit-transition-property: opacity; transition-property: opacity, color; -moz-transition-property: color; -webkit-transition-duration: 2s; transition-duration: 2s, 4s; -moz-transition-duration: 4s; -webkit-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out, ease-in; -webkit-transition-delay: .5s; -moz-transition-timing-function: ease-in-out; transition-delay: .5s, 0s; -moz-transition-delay: 0s; } "#}, ); test( r#" .foo { transition: opacity 2s; -webkit-transition-duration: 2s; } "#, indoc! {r#" .foo { transition: opacity 2s; -webkit-transition-duration: 2s; } "#}, ); prefix_test( r#" .foo { transition-property: margin-inline-start; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { transition-property: margin-left; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: margin-inline-start, padding-inline-start; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { transition-property: margin-left, padding-left; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right, padding-right; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right, padding-right; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: margin-inline-start, opacity, padding-inline-start, color; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { transition-property: margin-left, opacity, padding-left, color; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right, opacity, padding-right, color; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition-property: margin-right, opacity, padding-right, color; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: margin-block; } "#, indoc! {r#" .foo { transition-property: margin-top, margin-bottom; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: margin-inline-start 2s; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { transition: margin-left 2s; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition: margin-right 2s; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition: margin-right 2s; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: margin-inline-start 2s, padding-inline-start 2s; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { transition: margin-left 2s, padding-left 2s; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition: margin-right 2s, padding-right 2s; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { transition: margin-right 2s, padding-right 2s; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: margin-block-start 2s; } "#, indoc! {r#" .foo { transition: margin-top 2s; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: transform; } "#, indoc! {r#" .foo { -webkit-transition: -webkit-transform, transform; transition: -webkit-transform, transform; } "# }, Browsers { safari: Some(6 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: border-start-start-radius; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { -webkit-transition: -webkit-border-top-left-radius, border-top-left-radius; transition: -webkit-border-top-left-radius, border-top-left-radius; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { -webkit-transition: -webkit-border-top-right-radius, border-top-right-radius; transition: -webkit-border-top-right-radius, border-top-right-radius; } "# }, Browsers { safari: Some(4 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: border-start-start-radius; } "#, indoc! {r#" .foo:not(:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi)) { transition: border-top-left-radius; } .foo:lang(ae, ar, arc, bcc, bqi, ckb, dv, fa, glk, he, ku, mzn, nqo, pnb, ps, sd, ug, ur, yi) { transition: border-top-right-radius; } "# }, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); test( r#" .foo { -webkit-transition: background 200ms; -moz-transition: background 200ms; transition: background 230ms; } "#, indoc! {r#" .foo { -webkit-transition: background .2s; -moz-transition: background .2s; transition: background .23s; } "#}, ); prefix_test( r#" .foo { -webkit-transition: background 200ms; -moz-transition: background 200ms; transition: background 230ms; } "#, indoc! {r#" .foo { -webkit-transition: background .2s; -moz-transition: background .2s; transition: background .23s; } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); } #[test] fn test_animation() { minify_test(".foo { animation-name: test }", ".foo{animation-name:test}"); minify_test(".foo { animation-name: \"test\" }", ".foo{animation-name:test}"); minify_test(".foo { animation-name: foo, bar }", ".foo{animation-name:foo,bar}"); minify_test(".foo { animation-name: \"none\" }", ".foo{animation-name:\"none\"}"); minify_test( ".foo { animation-name: \"none\", foo }", ".foo{animation-name:\"none\",foo}", ); let name = crate::properties::animation::AnimationName::parse_string("default"); assert!(matches!(name, Err(..))); minify_test(".foo { animation-name: none }", ".foo{animation-name:none}"); minify_test(".foo { animation-name: none, none }", ".foo{animation-name:none,none}"); // Test CSS-wide keywords minify_test(".foo { animation-name: unset }", ".foo{animation-name:unset}"); minify_test(".foo { animation-name: \"unset\" }", ".foo{animation-name:\"unset\"}"); minify_test(".foo { animation-name: \"revert\" }", ".foo{animation-name:\"revert\"}"); minify_test( ".foo { animation-name: \"unset\", \"revert\"}", ".foo{animation-name:\"unset\",\"revert\"}", ); minify_test( ".foo { animation-name: foo, \"revert\"}", ".foo{animation-name:foo,\"revert\"}", ); minify_test( ".foo { animation-name: \"string\", \"revert\"}", ".foo{animation-name:string,\"revert\"}", ); minify_test( ".foo { animation-name: \"string\", foo, \"revert\"}", ".foo{animation-name:string,foo,\"revert\"}", ); minify_test( ".foo { animation-name: \"default\" }", ".foo{animation-name:\"default\"}", ); minify_test(".foo { animation-duration: 100ms }", ".foo{animation-duration:.1s}"); minify_test( ".foo { animation-duration: 100ms, 2000ms }", ".foo{animation-duration:.1s,2s}", ); minify_test( ".foo { animation-timing-function: ease }", ".foo{animation-timing-function:ease}", ); minify_test( ".foo { animation-timing-function: cubic-bezier(0.42, 0, 1, 1) }", ".foo{animation-timing-function:ease-in}", ); minify_test( ".foo { animation-timing-function: ease, cubic-bezier(0.42, 0, 1, 1) }", ".foo{animation-timing-function:ease,ease-in}", ); minify_test( ".foo { animation-iteration-count: 5 }", ".foo{animation-iteration-count:5}", ); minify_test( ".foo { animation-iteration-count: 2.5 }", ".foo{animation-iteration-count:2.5}", ); minify_test( ".foo { animation-iteration-count: 2.0 }", ".foo{animation-iteration-count:2}", ); minify_test( ".foo { animation-iteration-count: infinite }", ".foo{animation-iteration-count:infinite}", ); minify_test( ".foo { animation-iteration-count: 1, infinite }", ".foo{animation-iteration-count:1,infinite}", ); minify_test( ".foo { animation-direction: reverse }", ".foo{animation-direction:reverse}", ); minify_test( ".foo { animation-direction: alternate, reverse }", ".foo{animation-direction:alternate,reverse}", ); minify_test( ".foo { animation-play-state: paused }", ".foo{animation-play-state:paused}", ); minify_test( ".foo { animation-play-state: running, paused }", ".foo{animation-play-state:running,paused}", ); minify_test(".foo { animation-delay: 100ms }", ".foo{animation-delay:.1s}"); minify_test( ".foo { animation-delay: 100ms, 2000ms }", ".foo{animation-delay:.1s,2s}", ); minify_test( ".foo { animation-fill-mode: forwards }", ".foo{animation-fill-mode:forwards}", ); minify_test( ".foo { animation-fill-mode: Backwards,forwards }", ".foo{animation-fill-mode:backwards,forwards}", ); minify_test(".foo { animation: none }", ".foo{animation:none}"); minify_test(".foo { animation: \"none\" }", ".foo{animation:\"none\"}"); minify_test(".foo { animation: \"None\" }", ".foo{animation:\"None\"}"); minify_test(".foo { animation: \"none\", none }", ".foo{animation:\"none\",none}"); minify_test(".foo { animation: none, none }", ".foo{animation:none,none}"); minify_test(".foo { animation: \"none\" none }", ".foo{animation:\"none\"}"); minify_test(".foo { animation: none none }", ".foo{animation:none}"); // Test animation-name + animation-fill-mode minify_test( ".foo { animation: 2s both \"none\"}", ".foo{animation:2s both \"none\"}", ); minify_test( ".foo { animation: both \"none\" 2s}", ".foo{animation:2s both \"none\"}", ); minify_test(".foo { animation: \"none\" 2s none}", ".foo{animation:2s \"none\"}"); minify_test(".foo { animation: none \"none\" 2s}", ".foo{animation:2s \"none\"}"); minify_test( ".foo { animation: none, \"none\" 2s forwards}", ".foo{animation:none,2s forwards \"none\"}", ); minify_test(".foo { animation: \"unset\" }", ".foo{animation:\"unset\"}"); minify_test(".foo { animation: \"string\" .5s }", ".foo{animation:.5s string}"); minify_test(".foo { animation: \"unset\" .5s }", ".foo{animation:.5s \"unset\"}"); minify_test( ".foo { animation: none, \"unset\" .5s}", ".foo{animation:none,.5s \"unset\"}", ); minify_test( ".foo { animation: \"unset\" 0s 3s infinite, none }", ".foo{animation:0s 3s infinite \"unset\",none}", ); minify_test(".foo { animation: \"infinite\" 2s 1 }", ".foo{animation:2s 1 infinite}"); minify_test(".foo { animation: \"paused\" 2s }", ".foo{animation:2s running paused}"); minify_test( ".foo { animation: \"forwards\" 2s }", ".foo{animation:2s none forwards}", ); minify_test( ".foo { animation: \"reverse\" 2s }", ".foo{animation:2s normal reverse}", ); minify_test( ".foo { animation: \"reverse\" 2s alternate }", ".foo{animation:2s alternate reverse}", ); minify_test( ".foo { animation: 3s ease-in 1s infinite reverse both running slidein }", ".foo{animation:3s ease-in 1s infinite reverse both slidein}", ); minify_test( ".foo { animation: 3s slidein paused ease 1s 1 reverse both }", ".foo{animation:3s 1s reverse both paused slidein}", ); minify_test(".foo { animation: 3s ease ease }", ".foo{animation:3s ease ease}"); minify_test( ".foo { animation: 3s cubic-bezier(0.25, 0.1, 0.25, 1) foo }", ".foo{animation:3s foo}", ); minify_test( ".foo { animation: foo 0s 3s infinite }", ".foo{animation:0s 3s infinite foo}", ); test( r#" .foo { animation-name: foo; animation-duration: 0.09s; animation-timing-function: ease-in-out; animation-iteration-count: 2; animation-direction: alternate; animation-play-state: running; animation-delay: 100ms; animation-fill-mode: forwards; } "#, indoc! {r#" .foo { animation: 90ms ease-in-out .1s 2 alternate forwards foo; } "#}, ); test( r#" .foo { animation-name: foo, bar; animation-duration: 0.09s, 200ms; animation-timing-function: ease-in-out, ease; animation-iteration-count: 2, 1; animation-direction: alternate, normal; animation-play-state: running, paused; animation-delay: 100ms, 0s; animation-fill-mode: forwards, none; } "#, indoc! {r#" .foo { animation: 90ms ease-in-out .1s 2 alternate forwards foo, .2s paused bar; } "#}, ); test( r#" .foo { animation: bar 200ms; animation-timing-function: ease-in-out; } "#, indoc! {r#" .foo { animation: .2s ease-in-out bar; } "#}, ); test( r#" .foo { animation: bar 200ms; animation-timing-function: var(--ease); } "#, indoc! {r#" .foo { animation: .2s bar; animation-timing-function: var(--ease); } "#}, ); test( r#" .foo { animation-name: foo, bar; animation-duration: 0.09s; animation-timing-function: ease-in-out; animation-iteration-count: 2; animation-direction: alternate; animation-play-state: running; animation-delay: 100ms; animation-fill-mode: forwards; } "#, indoc! {r#" .foo { animation-name: foo, bar; animation-duration: 90ms; animation-timing-function: ease-in-out; animation-iteration-count: 2; animation-direction: alternate; animation-play-state: running; animation-delay: .1s; animation-fill-mode: forwards; } "#}, ); test( r#" .foo { -webkit-animation-name: foo; -webkit-animation-duration: 0.09s; -webkit-animation-timing-function: ease-in-out; -webkit-animation-iteration-count: 2; -webkit-animation-direction: alternate; -webkit-animation-play-state: running; -webkit-animation-delay: 100ms; -webkit-animation-fill-mode: forwards; } "#, indoc! {r#" .foo { -webkit-animation: 90ms ease-in-out .1s 2 alternate forwards foo; } "#}, ); test( r#" .foo { -moz-animation: bar 200ms; -moz-animation-timing-function: ease-in-out; } "#, indoc! {r#" .foo { -moz-animation: .2s ease-in-out bar; } "#}, ); test( r#" .foo { -webkit-animation: bar 200ms; -webkit-animation-timing-function: ease-in-out; -moz-animation: bar 200ms; -moz-animation-timing-function: ease-in-out; } "#, indoc! {r#" .foo { -webkit-animation: .2s ease-in-out bar; -moz-animation: .2s ease-in-out bar; } "#}, ); prefix_test( r#" .foo { animation: .2s ease-in-out bar; } "#, indoc! {r#" .foo { -webkit-animation: .2s ease-in-out bar; -moz-animation: .2s ease-in-out bar; animation: .2s ease-in-out bar; } "#}, Browsers { firefox: Some(6 << 16), safari: Some(6 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-animation: .2s ease-in-out bar; -moz-animation: .2s ease-in-out bar; animation: .2s ease-in-out bar; } "#, indoc! {r#" .foo { animation: .2s ease-in-out bar; } "#}, Browsers { firefox: Some(20 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { animation: 200ms var(--ease) bar; } "#, indoc! {r#" .foo { -webkit-animation: .2s var(--ease) bar; -moz-animation: .2s var(--ease) bar; animation: .2s var(--ease) bar; } "#}, Browsers { firefox: Some(6 << 16), safari: Some(6 << 16), ..Browsers::default() }, ); } #[test] fn test_transform() { minify_test( ".foo { transform: translate(2px, 3px)", ".foo{transform:translate(2px,3px)}", ); minify_test( ".foo { transform: translate(2px, 0px)", ".foo{transform:translate(2px)}", ); minify_test( ".foo { transform: translate(0px, 2px)", ".foo{transform:translateY(2px)}", ); minify_test(".foo { transform: translateX(2px)", ".foo{transform:translate(2px)}"); minify_test(".foo { transform: translateY(2px)", ".foo{transform:translateY(2px)}"); minify_test(".foo { transform: translateZ(2px)", ".foo{transform:translateZ(2px)}"); minify_test( ".foo { transform: translate3d(2px, 3px, 4px)", ".foo{transform:translate3d(2px,3px,4px)}", ); minify_test( ".foo { transform: translate3d(10%, 20%, 4px)", ".foo{transform:translate3d(10%,20%,4px)}", ); minify_test( ".foo { transform: translate3d(2px, 0px, 0px)", ".foo{transform:translate(2px)}", ); minify_test( ".foo { transform: translate3d(0px, 2px, 0px)", ".foo{transform:translateY(2px)}", ); minify_test( ".foo { transform: translate3d(0px, 0px, 2px)", ".foo{transform:translateZ(2px)}", ); minify_test( ".foo { transform: translate3d(2px, 3px, 0px)", ".foo{transform:translate(2px,3px)}", ); minify_test(".foo { transform: scale(2, 3)", ".foo{transform:scale(2,3)}"); minify_test(".foo { transform: scale(10%, 20%)", ".foo{transform:scale(.1,.2)}"); minify_test(".foo { transform: scale(2, 2)", ".foo{transform:scale(2)}"); minify_test(".foo { transform: scale(2, 1)", ".foo{transform:scaleX(2)}"); minify_test(".foo { transform: scale(1, 2)", ".foo{transform:scaleY(2)}"); minify_test(".foo { transform: scaleX(2)", ".foo{transform:scaleX(2)}"); minify_test(".foo { transform: scaleY(2)", ".foo{transform:scaleY(2)}"); minify_test(".foo { transform: scaleZ(2)", ".foo{transform:scaleZ(2)}"); minify_test(".foo { transform: scale3d(2, 3, 4)", ".foo{transform:scale3d(2,3,4)}"); minify_test(".foo { transform: scale3d(2, 1, 1)", ".foo{transform:scaleX(2)}"); minify_test(".foo { transform: scale3d(1, 2, 1)", ".foo{transform:scaleY(2)}"); minify_test(".foo { transform: scale3d(1, 1, 2)", ".foo{transform:scaleZ(2)}"); minify_test(".foo { transform: scale3d(2, 2, 1)", ".foo{transform:scale(2)}"); minify_test(".foo { transform: rotate(20deg)", ".foo{transform:rotate(20deg)}"); minify_test(".foo { transform: rotateX(20deg)", ".foo{transform:rotateX(20deg)}"); minify_test(".foo { transform: rotateY(20deg)", ".foo{transform:rotateY(20deg)}"); minify_test(".foo { transform: rotateZ(20deg)", ".foo{transform:rotate(20deg)}"); minify_test(".foo { transform: rotate(360deg)", ".foo{transform:rotate(360deg)}"); minify_test( ".foo { transform: rotate3d(2, 3, 4, 20deg)", ".foo{transform:rotate3d(2,3,4,20deg)}", ); minify_test( ".foo { transform: rotate3d(1, 0, 0, 20deg)", ".foo{transform:rotateX(20deg)}", ); minify_test( ".foo { transform: rotate3d(0, 1, 0, 20deg)", ".foo{transform:rotateY(20deg)}", ); minify_test( ".foo { transform: rotate3d(0, 0, 1, 20deg)", ".foo{transform:rotate(20deg)}", ); minify_test(".foo { transform: rotate(405deg)}", ".foo{transform:rotate(405deg)}"); minify_test(".foo { transform: rotateX(405deg)}", ".foo{transform:rotateX(405deg)}"); minify_test(".foo { transform: rotateY(405deg)}", ".foo{transform:rotateY(405deg)}"); minify_test(".foo { transform: rotate(-200deg)}", ".foo{transform:rotate(-200deg)}"); minify_test(".foo { transform: rotate(0)", ".foo{transform:rotate(0)}"); minify_test(".foo { transform: rotate(0deg)", ".foo{transform:rotate(0)}"); minify_test( ".foo { transform: rotateX(-200deg)}", ".foo{transform:rotateX(-200deg)}", ); minify_test( ".foo { transform: rotateY(-200deg)}", ".foo{transform:rotateY(-200deg)}", ); minify_test( ".foo { transform: rotate3d(1, 1, 0, -200deg)", ".foo{transform:rotate3d(1,1,0,-200deg)}", ); minify_test(".foo { transform: skew(20deg)", ".foo{transform:skew(20deg)}"); minify_test(".foo { transform: skew(20deg, 0deg)", ".foo{transform:skew(20deg)}"); minify_test(".foo { transform: skew(0deg, 20deg)", ".foo{transform:skewY(20deg)}"); minify_test(".foo { transform: skewX(20deg)", ".foo{transform:skew(20deg)}"); minify_test(".foo { transform: skewY(20deg)", ".foo{transform:skewY(20deg)}"); minify_test( ".foo { transform: perspective(10px)", ".foo{transform:perspective(10px)}", ); minify_test( ".foo { transform: matrix(1, 2, -1, 1, 80, 80)", ".foo{transform:matrix(1,2,-1,1,80,80)}", ); minify_test( ".foo { transform: matrix3d(1, 0, 0, 0, 0, 1, 6, 0, 0, 0, 1, 0, 50, 100, 0, 1.1)", ".foo{transform:matrix3d(1,0,0,0,0,1,6,0,0,0,1,0,50,100,0,1.1)}", ); minify_test( ".foo{transform:translate(100px,200px) rotate(45deg) skew(10deg) scale(2)}", ".foo{transform:matrix(1.41421,1.41421,-1.16485,1.66358,100,200)}", ); minify_test( ".foo{transform:translate(200px,300px) translate(100px,200px) scale(2)}", ".foo{transform:matrix(2,0,0,2,300,500)}", ); minify_test( ".foo{transform:translate(100px,200px) rotate(45deg)}", ".foo{transform:translate(100px,200px)rotate(45deg)}", ); minify_test( ".foo{transform:rotate3d(1, 1, 1, 45deg) translate3d(100px, 100px, 10px)}", ".foo{transform:rotate3d(1,1,1,45deg)translate3d(100px,100px,10px)}", ); minify_test( ".foo{transform:translate3d(100px, 100px, 10px) skew(10deg) scale3d(2, 3, 4)}", ".foo{transform:matrix3d(2,0,0,0,.528981,3,0,0,0,0,4,0,100,100,10,1)}", ); minify_test( ".foo{transform:matrix3d(0.804737854124365, 0.5058793634016805, -0.31061721752604554, 0, -0.31061721752604554, 0.804737854124365, 0.5058793634016805, 0, 0.5058793634016805, -0.31061721752604554, 0.804737854124365, 0, 100, 100, 10, 1)}", ".foo{transform:translate3d(100px,100px,10px)rotate3d(1,1,1,45deg)}" ); minify_test( ".foo{transform:matrix3d(1, 0, 0, 0, 0, 0.7071067811865476, 0.7071067811865475, 0, 0, -0.7071067811865475, 0.7071067811865476, 0, 100, 100, 10, 1)}", ".foo{transform:translate3d(100px,100px,10px)rotateX(45deg)}" ); minify_test( ".foo{transform:translate3d(100px, 200px, 10px) translate(100px, 100px)}", ".foo{transform:translate3d(200px,300px,10px)}", ); minify_test( ".foo{transform:rotate(45deg) rotate(45deg)}", ".foo{transform:rotate(90deg)}", ); minify_test( ".foo{transform:matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 100, 100)}", ".foo{transform:translate(100px,100px)rotate(45deg)}" ); minify_test( ".foo{transform:translateX(2in) translateX(50px)}", ".foo{transform:translate(242px)}", ); minify_test( ".foo{transform:translateX(calc(2in + 50px))}", ".foo{transform:translate(242px)}", ); minify_test(".foo{transform:translateX(50%)}", ".foo{transform:translate(50%)}"); minify_test( ".foo{transform:translateX(calc(50% - 100px + 20px))}", ".foo{transform:translate(calc(50% - 80px))}", ); minify_test( ".foo{transform:rotate(calc(10deg + 20deg))}", ".foo{transform:rotate(30deg)}", ); minify_test( ".foo{transform:rotate(calc(10deg + 0.349066rad))}", ".foo{transform:rotate(30deg)}", ); minify_test( ".foo{transform:rotate(calc(10deg + 1.5turn))}", ".foo{transform:rotate(550deg)}", ); minify_test( ".foo{transform:rotate(calc(10deg * 2))}", ".foo{transform:rotate(20deg)}", ); minify_test( ".foo{transform:rotate(calc(-10deg * 2))}", ".foo{transform:rotate(-20deg)}", ); minify_test( ".foo{transform:rotate(calc(10deg + var(--test)))}", ".foo{transform:rotate(calc(10deg + var(--test)))}", ); minify_test(".foo { transform: scale(calc(10% + 20%))", ".foo{transform:scale(.3)}"); minify_test(".foo { transform: scale(calc(.1 + .2))", ".foo{transform:scale(.3)}"); minify_test( ".foo { -webkit-transform: scale(calc(10% + 20%))", ".foo{-webkit-transform:scale(.3)}", ); minify_test(".foo { translate: 1px 2px 3px }", ".foo{translate:1px 2px 3px}"); minify_test(".foo { translate: 1px 0px 0px }", ".foo{translate:1px}"); minify_test(".foo { translate: 1px 2px 0px }", ".foo{translate:1px 2px}"); minify_test(".foo { translate: 1px 0px 2px }", ".foo{translate:1px 0 2px}"); minify_test(".foo { translate: none }", ".foo{translate:0}"); minify_test(".foo { rotate: 10deg }", ".foo{rotate:10deg}"); minify_test(".foo { rotate: z 10deg }", ".foo{rotate:10deg}"); minify_test(".foo { rotate: 0 0 1 10deg }", ".foo{rotate:10deg}"); minify_test(".foo { rotate: x 10deg }", ".foo{rotate:x 10deg}"); minify_test(".foo { rotate: 1 0 0 10deg }", ".foo{rotate:x 10deg}"); minify_test(".foo { rotate: y 10deg }", ".foo{rotate:y 10deg}"); minify_test(".foo { rotate: 0 1 0 10deg }", ".foo{rotate:y 10deg}"); minify_test(".foo { rotate: 1 1 1 10deg }", ".foo{rotate:1 1 1 10deg}"); minify_test(".foo { rotate: 0 0 1 0deg }", ".foo{rotate:none}"); minify_test(".foo { rotate: none }", ".foo{rotate:none}"); minify_test(".foo { scale: 1 }", ".foo{scale:1}"); minify_test(".foo { scale: 1 1 }", ".foo{scale:1}"); minify_test(".foo { scale: 1 1 1 }", ".foo{scale:1}"); minify_test(".foo { scale: none }", ".foo{scale:1}"); minify_test(".foo { scale: 1 0 }", ".foo{scale:1 0}"); minify_test(".foo { scale: 1 0 1 }", ".foo{scale:1 0}"); minify_test(".foo { scale: 1 0 0 }", ".foo{scale:1 0 0}"); minify_test(".foo { transform: scale(3); scale: 0.5 }", ".foo{transform:scale(1.5)}"); minify_test(".foo { scale: 0.5; transform: scale(3); }", ".foo{transform:scale(3)}"); prefix_test( r#" .foo { transform: scale(0.5); } "#, indoc! {r#" .foo { -webkit-transform: scale(.5); -moz-transform: scale(.5); transform: scale(.5); } "#}, Browsers { firefox: Some(6 << 16), safari: Some(6 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transform: var(--transform); } "#, indoc! {r#" .foo { -webkit-transform: var(--transform); -moz-transform: var(--transform); transform: var(--transform); } "#}, Browsers { firefox: Some(6 << 16), safari: Some(6 << 16), ..Browsers::default() }, ); } #[test] pub fn test_gradients() { minify_test( ".foo { background: linear-gradient(yellow, blue) }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(to bottom, yellow, blue); }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(180deg, yellow, blue); }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(0.5turn, yellow, blue); }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow 10%, blue 20%) }", ".foo{background:linear-gradient(#ff0 10%,#00f 20%)}", ); minify_test( ".foo { background: linear-gradient(to top, blue, yellow); }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(to top, blue 10%, yellow 20%); }", ".foo{background:linear-gradient(#ff0 80%,#00f 90%)}", ); minify_test( ".foo { background: linear-gradient(to top, blue 10px, yellow 20px); }", ".foo{background:linear-gradient(0deg,#00f 10px,#ff0 20px)}", ); minify_test( ".foo { background: linear-gradient(135deg, yellow, blue); }", ".foo{background:linear-gradient(135deg,#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, blue 20%, #0f0); }", ".foo{background:linear-gradient(#ff0,#00f 20%,#0f0)}", ); minify_test( ".foo { background: linear-gradient(to top right, red, white, blue) }", ".foo{background:linear-gradient(to top right,red,#fff,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, blue calc(10% * 2), #0f0); }", ".foo{background:linear-gradient(#ff0,#00f 20%,#0f0)}", ); minify_test( ".foo { background: linear-gradient(yellow, 20%, blue); }", ".foo{background:linear-gradient(#ff0,20%,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, 50%, blue); }", ".foo{background:linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, 20px, blue); }", ".foo{background:linear-gradient(#ff0,20px,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, 50px, blue); }", ".foo{background:linear-gradient(#ff0,50px,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, 50px, blue); }", ".foo{background:linear-gradient(#ff0,50px,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, red 30% 40%, blue); }", ".foo{background:linear-gradient(#ff0,red 30% 40%,#00f)}", ); minify_test( ".foo { background: linear-gradient(yellow, red 30%, red 40%, blue); }", ".foo{background:linear-gradient(#ff0,red 30% 40%,#00f)}", ); minify_test( ".foo { background: linear-gradient(0, yellow, blue); }", ".foo{background:linear-gradient(#00f,#ff0)}", ); minify_test( ".foo { background: -webkit-linear-gradient(yellow, blue) }", ".foo{background:-webkit-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -webkit-linear-gradient(bottom, yellow, blue); }", ".foo{background:-webkit-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -webkit-linear-gradient(top right, red, white, blue) }", ".foo{background:-webkit-linear-gradient(top right,red,#fff,#00f)}", ); minify_test( ".foo { background: -moz-linear-gradient(yellow, blue) }", ".foo{background:-moz-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -moz-linear-gradient(bottom, yellow, blue); }", ".foo{background:-moz-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -moz-linear-gradient(top right, red, white, blue) }", ".foo{background:-moz-linear-gradient(top right,red,#fff,#00f)}", ); minify_test( ".foo { background: -o-linear-gradient(yellow, blue) }", ".foo{background:-o-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -o-linear-gradient(bottom, yellow, blue); }", ".foo{background:-o-linear-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -o-linear-gradient(top right, red, white, blue) }", ".foo{background:-o-linear-gradient(top right,red,#fff,#00f)}", ); minify_test( ".foo { background: -webkit-gradient(linear, left top, left bottom, from(blue), to(yellow)) }", ".foo{background:-webkit-gradient(linear,0 0,0 100%,from(#00f),to(#ff0))}", ); minify_test( ".foo { background: -webkit-gradient(linear, left top, left bottom, from(blue), color-stop(50%, red), to(yellow)) }", ".foo{background:-webkit-gradient(linear,0 0,0 100%,from(#00f),color-stop(.5,red),to(#ff0))}" ); minify_test( ".foo { background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, blue), color-stop(50%, red), color-stop(100%, yellow)) }", ".foo{background:-webkit-gradient(linear,0 0,0 100%,from(#00f),color-stop(.5,red),to(#ff0))}" ); minify_test( ".foo { background: repeating-linear-gradient(yellow 10px, blue 50px) }", ".foo{background:repeating-linear-gradient(#ff0 10px,#00f 50px)}", ); minify_test( ".foo { background: -webkit-repeating-linear-gradient(yellow 10px, blue 50px) }", ".foo{background:-webkit-repeating-linear-gradient(#ff0 10px,#00f 50px)}", ); minify_test( ".foo { background: -moz-repeating-linear-gradient(yellow 10px, blue 50px) }", ".foo{background:-moz-repeating-linear-gradient(#ff0 10px,#00f 50px)}", ); minify_test( ".foo { background: -o-repeating-linear-gradient(yellow 10px, blue 50px) }", ".foo{background:-o-repeating-linear-gradient(#ff0 10px,#00f 50px)}", ); minify_test( ".foo { background: radial-gradient(yellow, blue) }", ".foo{background:radial-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(at top left, yellow, blue) }", ".foo{background:radial-gradient(at 0 0,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(5em circle at top left, yellow, blue) }", ".foo{background:radial-gradient(5em at 0 0,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(circle at 100%, #333, #333 50%, #eee 75%, #333 75%) }", ".foo{background:radial-gradient(circle at 100%,#333,#333 50%,#eee 75%,#333 75%)}", ); minify_test( ".foo { background: radial-gradient(farthest-corner circle at 100% 50%, #333, #333 50%, #eee 75%, #333 75%) }", ".foo{background:radial-gradient(circle at 100%,#333,#333 50%,#eee 75%,#333 75%)}" ); minify_test( ".foo { background: radial-gradient(farthest-corner circle at 50% 50%, #333, #333 50%, #eee 75%, #333 75%) }", ".foo{background:radial-gradient(circle,#333,#333 50%,#eee 75%,#333 75%)}" ); minify_test( ".foo { background: radial-gradient(ellipse at top, #e66465, transparent) }", ".foo{background:radial-gradient(at top,#e66465,#0000)}", ); minify_test( ".foo { background: radial-gradient(20px, yellow, blue) }", ".foo{background:radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(circle 20px, yellow, blue) }", ".foo{background:radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(20px 40px, yellow, blue) }", ".foo{background:radial-gradient(20px 40px,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(ellipse 20px 40px, yellow, blue) }", ".foo{background:radial-gradient(20px 40px,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(ellipse calc(20px + 10px) 40px, yellow, blue) }", ".foo{background:radial-gradient(30px 40px,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(circle farthest-side, yellow, blue) }", ".foo{background:radial-gradient(circle farthest-side,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(farthest-side circle, yellow, blue) }", ".foo{background:radial-gradient(circle farthest-side,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(ellipse farthest-side, yellow, blue) }", ".foo{background:radial-gradient(farthest-side,#ff0,#00f)}", ); minify_test( ".foo { background: radial-gradient(farthest-side ellipse, yellow, blue) }", ".foo{background:radial-gradient(farthest-side,#ff0,#00f)}", ); minify_test( ".foo { background: -webkit-radial-gradient(yellow, blue) }", ".foo{background:-webkit-radial-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -moz-radial-gradient(yellow, blue) }", ".foo{background:-moz-radial-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: -o-radial-gradient(yellow, blue) }", ".foo{background:-o-radial-gradient(#ff0,#00f)}", ); minify_test( ".foo { background: repeating-radial-gradient(circle 20px, yellow, blue) }", ".foo{background:repeating-radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: -webkit-repeating-radial-gradient(circle 20px, yellow, blue) }", ".foo{background:-webkit-repeating-radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: -moz-repeating-radial-gradient(circle 20px, yellow, blue) }", ".foo{background:-moz-repeating-radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: -o-repeating-radial-gradient(circle 20px, yellow, blue) }", ".foo{background:-o-repeating-radial-gradient(20px,#ff0,#00f)}", ); minify_test( ".foo { background: -webkit-gradient(radial, center center, 0, center center, 100, from(blue), to(yellow)) }", ".foo{background:-webkit-gradient(radial,50% 50%,0,50% 50%,100,from(#00f),to(#ff0))}" ); minify_test( ".foo { background: conic-gradient(#f06, gold) }", ".foo{background:conic-gradient(#f06,gold)}", ); minify_test( ".foo { background: conic-gradient(at 50% 50%, #f06, gold) }", ".foo{background:conic-gradient(#f06,gold)}", ); minify_test( ".foo { background: conic-gradient(from 0deg, #f06, gold) }", ".foo{background:conic-gradient(#f06,gold)}", ); minify_test( ".foo { background: conic-gradient(from 0, #f06, gold) }", ".foo{background:conic-gradient(#f06,gold)}", ); minify_test( ".foo { background: conic-gradient(from 0deg at center, #f06, gold) }", ".foo{background:conic-gradient(#f06,gold)}", ); minify_test( ".foo { background: conic-gradient(white -50%, black 150%) }", ".foo{background:conic-gradient(#fff -50%,#000 150%)}", ); minify_test( ".foo { background: conic-gradient(white -180deg, black 540deg) }", ".foo{background:conic-gradient(#fff -180deg,#000 540deg)}", ); minify_test( ".foo { background: conic-gradient(from 45deg, white, black, white) }", ".foo{background:conic-gradient(from 45deg,#fff,#000,#fff)}", ); minify_test( ".foo { background: repeating-conic-gradient(from 45deg, white, black, white) }", ".foo{background:repeating-conic-gradient(from 45deg,#fff,#000,#fff)}", ); minify_test( ".foo { background: repeating-conic-gradient(black 0deg 25%, white 0deg 50%) }", ".foo{background:repeating-conic-gradient(#000 0deg 25%,#fff 0deg 50%)}", ); test( r#" .foo { background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); background: -webkit-linear-gradient(red, blue); background: -moz-linear-gradient(red, blue); background: -o-linear-gradient(red, blue); background: linear-gradient(red, blue); } "#, indoc! {r#" .foo { background: -webkit-gradient(linear, left top, left bottom, from(red), to(#00f)); background: -webkit-linear-gradient(red, #00f); background: -moz-linear-gradient(red, #00f); background: -o-linear-gradient(red, #00f); background: linear-gradient(red, #00f); } "#}, ); prefix_test( r#" .foo { background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); background: -webkit-linear-gradient(red, blue); background: -moz-linear-gradient(red, blue); background: -o-linear-gradient(red, blue); background: linear-gradient(red, blue); } "#, indoc! {r#" .foo { background: linear-gradient(red, #00f); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); background: -webkit-linear-gradient(red, blue); background: -moz-linear-gradient(red, blue); background: -o-linear-gradient(red, blue); } "#, indoc! {r#" .foo { background: -webkit-gradient(linear, left top, left bottom, from(red), to(#00f)); background: -webkit-linear-gradient(red, #00f); background: -moz-linear-gradient(red, #00f); background: -o-linear-gradient(red, #00f); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 0 0, 0 100%, from(red), to(#00f)); background-image: -webkit-linear-gradient(red, #00f); background-image: linear-gradient(red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(to right, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 0 0, 100% 0, from(red), to(#00f)); background-image: -webkit-linear-gradient(right, red, #00f); background-image: linear-gradient(to right, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(to top, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 0 100%, 0 0, from(red), to(#00f)); background-image: -webkit-linear-gradient(top, red, #00f); background-image: linear-gradient(to top, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(to left, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 100% 0, 0 0, from(red), to(#00f)); background-image: -webkit-linear-gradient(left, red, #00f); background-image: linear-gradient(to left, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(to left bottom, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 100% 0, 0 100%, from(red), to(#00f)); background-image: -webkit-linear-gradient(bottom left, red, #00f); background-image: linear-gradient(to bottom left, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(to top right, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 0 100%, 100% 0, from(red), to(#00f)); background-image: -webkit-linear-gradient(top right, red, #00f); background-image: linear-gradient(to top right, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(90deg, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(linear, 0 0, 100% 0, from(red), to(#00f)); background-image: -webkit-linear-gradient(90deg, red, #00f); background-image: linear-gradient(90deg, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(45deg, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-linear-gradient(45deg, red, #00f); background-image: linear-gradient(45deg, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: linear-gradient(red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-linear-gradient(red, #00f); background-image: linear-gradient(red, #00f); } "#}, Browsers { chrome: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: radial-gradient(20px, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(radial, center center, 0, center center, 20, from(red), to(#00f)); background-image: -webkit-radial-gradient(20px, red, #00f); background-image: radial-gradient(20px, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: radial-gradient(20px at top left, red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-gradient(radial, left top, 0, left top, 20, from(red), to(#00f)); background-image: -webkit-radial-gradient(20px at 0 0, red, #00f); background-image: radial-gradient(20px at 0 0, red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: radial-gradient(red, blue); } "#, indoc! {r#" .foo { background-image: -webkit-radial-gradient(red, #00f); background-image: radial-gradient(red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background-image: -webkit-gradient(radial, left top, 0, left top, 20, from(red), to(#00f)); background-image: -webkit-radial-gradient(20px at 0% 0%, red, #00f); background-image: radial-gradient(20px at 0% 0%, red, #00f); } "#, indoc! {r#" .foo { background-image: radial-gradient(20px at 0 0, red, #00f); } "#}, Browsers { chrome: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: -webkit-gradient(radial, left top, 0, left top, 20, from(red), to(#00f)); background: -webkit-radial-gradient(20px at 0% 0%, red, #00f); background: radial-gradient(20px at 0% 0%, red, #00f); } "#, indoc! {r#" .foo { background: radial-gradient(20px at 0 0, red, #00f); } "#}, Browsers { chrome: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: radial-gradient(red, blue); } "#, indoc! {r#" .foo { background: -webkit-radial-gradient(red, #00f); background: radial-gradient(red, #00f); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: radial-gradient(red, blue), linear-gradient(yellow, red), url(bg.jpg); } "#, indoc! {r#" .foo { background: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0), to(red)), url("bg.jpg"); background: -webkit-radial-gradient(red, #00f), -webkit-linear-gradient(#ff0, red), url("bg.jpg"); background: -moz-radial-gradient(red, #00f), -moz-linear-gradient(#ff0, red), url("bg.jpg"); background: -o-radial-gradient(red, #00f), -o-linear-gradient(#ff0, red), url("bg.jpg"); background: radial-gradient(red, #00f), linear-gradient(#ff0, red), url("bg.jpg"); } "#}, Browsers { chrome: Some(8 << 16), firefox: Some(4 << 16), opera: Some(11 << 16 | 5 << 8), ..Browsers::default() }, ); prefix_test( r#" .foo { background: linear-gradient(yellow, red 30% 40%, blue); } "#, indoc! {r#" .foo { background: linear-gradient(#ff0, red 30%, red 40%, #00f); } "#}, Browsers { chrome: Some(70 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: linear-gradient(yellow, red 30% 40%, blue); } "#, indoc! {r#" .foo { background: linear-gradient(#ff0, red 30% 40%, #00f); } "#}, Browsers { chrome: Some(71 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background: linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background: linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(color(display-p3 1 .0000153435 -.00000303562), color(display-p3 .440289 .28452 1.23485)); background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background: -webkit-linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(20 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)); background: -webkit-linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(#ff0f0e, #7773ff); background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(oklab(59.686% 0.1009 0.1192), oklab(54.0% -0.10 -0.02)); }", indoc! { r#" .foo { background-image: linear-gradient(lab(52.2319% 40.1449 59.9171), lab(47.7776% -34.2947 -7.65904)); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background-image: linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background-image: linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(color(display-p3 1 .0000153435 -.00000303562), color(display-p3 .440289 .28452 1.23485)); background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background-image: -webkit-linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(20 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)); background-image: -webkit-linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(#ff0f0e, #7773ff); background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { background-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-image: linear-gradient(oklab(59.686% 0.1009 0.1192), oklab(54.0% -0.10 -0.02)); }", indoc! { r#" .foo { background-image: linear-gradient(lab(52.2319% 40.1449 59.9171), lab(47.7776% -34.2947 -7.65904)); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); } #[test] fn test_font_face() { minify_test( r#"@font-face { src: url("test.woff"); font-family: "Helvetica"; font-weight: bold; font-style: italic; }"#, "@font-face{src:url(test.woff);font-family:Helvetica;font-weight:700;font-style:italic}", ); minify_test("@font-face {src: url(test.woff);}", "@font-face{src:url(test.woff)}"); minify_test("@font-face {src: local(\"Test\");}", "@font-face{src:local(Test)}"); minify_test( "@font-face {src: local(\"Foo Bar\");}", "@font-face{src:local(Foo Bar)}", ); minify_test("@font-face {src: local(Test);}", "@font-face{src:local(Test)}"); minify_test("@font-face {src: local(Foo Bar);}", "@font-face{src:local(Foo Bar)}"); minify_test( "@font-face {src: url(\"test.woff\") format(woff);}", "@font-face{src:url(test.woff)format(\"woff\")}", ); minify_test( "@font-face {src: url(\"test.ttc\") format(collection), url(test.ttf) format(truetype);}", "@font-face{src:url(test.ttc)format(\"collection\"),url(test.ttf)format(\"truetype\")}", ); minify_test( "@font-face {src: url(\"test.otf\") format(opentype) tech(features-aat);}", "@font-face{src:url(test.otf)format(\"opentype\")tech(features-aat)}", ); minify_test( "@font-face {src: url(\"test.woff\") format(woff) tech(color-colrv1);}", "@font-face{src:url(test.woff)format(\"woff\")tech(color-colrv1)}", ); minify_test( "@font-face {src: url(\"test.woff2\") format(woff2) tech(variations);}", "@font-face{src:url(test.woff2)format(\"woff2\")tech(variations)}", ); minify_test( "@font-face {src: url(\"test.woff\") format(woff) tech(palettes);}", "@font-face{src:url(test.woff)format(\"woff\")tech(palettes)}", ); // multiple tech minify_test( "@font-face {src: url(\"test.woff\") format(woff) tech(features-opentype, color-sbix);}", "@font-face{src:url(test.woff)format(\"woff\")tech(features-opentype,color-sbix)}", ); minify_test( "@font-face {src: url(\"test.woff\") format(woff) tech(incremental, color-svg, features-graphite, features-aat);}", "@font-face{src:url(test.woff)format(\"woff\")tech(incremental,color-svg,features-graphite,features-aat)}", ); // format() function must precede tech() if both are present minify_test( "@font-face {src: url(\"foo.ttf\") format(opentype) tech(color-colrv1);}", "@font-face{src:url(foo.ttf)format(\"opentype\")tech(color-colrv1)}", ); // only have tech is valid minify_test( "@font-face {src: url(\"foo.ttf\") tech(color-SVG);}", "@font-face{src:url(foo.ttf)tech(color-svg)}", ); // CGQAQ: if tech and format both presence, order is matter, tech before format is invalid // but now just return raw token, we don't have strict mode yet. // ref: https://github.com/parcel-bundler/lightningcss/pull/255#issuecomment-1219049998 minify_test( "@font-face {src: url(\"foo.ttf\") tech(palettes color-colrv0 variations) format(opentype);}", "@font-face{src:url(foo.ttf) tech(palettes color-colrv0 variations)format(opentype)}", ); // TODO(CGQAQ): make this test pass when we have strict mode // ref: https://github.com/web-platform-tests/wpt/blob/9f8a6ccc41aa725e8f51f4f096f686313bb88d8d/css/css-fonts/parsing/font-face-src-tech.html#L45 // error_test( // "@font-face {src: url(\"foo.ttf\") tech(features-opentype) format(opentype);}", // ParserError::AtRuleBodyInvalid, // ); // error_test( // "@font-face {src: url(\"foo.ttf\") tech();}", // ParserError::AtRuleBodyInvalid, // ); // error_test( // "@font-face {src: url(\"foo.ttf\") tech(\"features-opentype\");}", // ParserError::AtRuleBodyInvalid, // ); // error_test( // "@font-face {src: url(\"foo.ttf\") tech(\"color-colrv0\");}", // ParserError::AtRuleBodyInvalid, // ); minify_test( "@font-face {src: local(\"\") url(\"test.woff\");}", "@font-face{src:local(\"\")url(test.woff)}", ); minify_test("@font-face {font-weight: 200 400}", "@font-face{font-weight:200 400}"); minify_test("@font-face {font-weight: 400 400}", "@font-face{font-weight:400}"); minify_test( "@font-face {font-stretch: 50% 200%}", "@font-face{font-stretch:50% 200%}", ); minify_test("@font-face {font-stretch: 50% 50%}", "@font-face{font-stretch:50%}"); minify_test("@font-face {unicode-range: U+26;}", "@font-face{unicode-range:U+26}"); minify_test("@font-face {unicode-range: u+26;}", "@font-face{unicode-range:U+26}"); minify_test( "@font-face {unicode-range: U+0-7F;}", "@font-face{unicode-range:U+0-7F}", ); minify_test( "@font-face {unicode-range: U+0025-00FF;}", "@font-face{unicode-range:U+25-FF}", ); minify_test("@font-face {unicode-range: U+4??;}", "@font-face{unicode-range:U+4??}"); minify_test( "@font-face {unicode-range: U+400-4FF;}", "@font-face{unicode-range:U+4??}", ); minify_test( "@font-face {unicode-range: U+0025-00FF, U+4??;}", "@font-face{unicode-range:U+25-FF,U+4??}", ); minify_test( "@font-face {unicode-range: U+A5, U+4E00-9FFF, U+30??, U+FF00-FF9F;}", "@font-face{unicode-range:U+A5,U+4E00-9FFF,U+30??,U+FF00-FF9F}", ); minify_test( "@font-face {unicode-range: U+????;}", "@font-face{unicode-range:U+????}", ); minify_test( "@font-face {unicode-range: U+0000-FFFF;}", "@font-face{unicode-range:U+????}", ); minify_test( "@font-face {unicode-range: U+10????;}", "@font-face{unicode-range:U+10????}", ); minify_test( "@font-face {unicode-range: U+100000-10FFFF;}", "@font-face{unicode-range:U+10????}", ); minify_test( "@font-face {unicode-range: U+1e1e?;}", "@font-face{unicode-range:U+1E1E?}", ); minify_test( "@font-face {unicode-range: u+????, U+1????, U+10????;}", "@font-face{unicode-range:U+????,U+1????,U+10????}", ); minify_test(r#" @font-face { font-family: Inter; font-style: oblique 0deg 10deg; font-weight: 100 900; src: url("../fonts/Inter.var.woff2?v=3.19") format("woff2"); font-display: swap; } "#, "@font-face{font-family:Inter;font-style:oblique 0deg 10deg;font-weight:100 900;src:url(../fonts/Inter.var.woff2?v=3.19)format(\"woff2\");font-display:swap}"); minify_test(r#" @font-face { font-family: Inter; font-style: oblique 14deg 14deg; font-weight: 100 900; src: url("../fonts/Inter.var.woff2?v=3.19") format("woff2"); font-display: swap; } "#, "@font-face{font-family:Inter;font-style:oblique;font-weight:100 900;src:url(../fonts/Inter.var.woff2?v=3.19)format(\"woff2\");font-display:swap}"); } #[test] fn test_font_palette_values() { minify_test( r#"@font-palette-values --Cooler { font-family: Bixa; base-palette: 1; override-colors: 1 #7EB7E4; }"#, "@font-palette-values --Cooler{font-family:Bixa;base-palette:1;override-colors:1 #7eb7e4}", ); minify_test( r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 rgb(43, 12, 9), 3 lime; }"#, "@font-palette-values --Cooler{font-family:Handover Sans;base-palette:3;override-colors:1 #2b0c09,3 #0f0}", ); minify_test(r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 rgb(43, 12, 9), 3 var(--highlight); }"#, "@font-palette-values --Cooler{font-family:Handover Sans;base-palette:3;override-colors:1 #2b0c09,3 var(--highlight)}"); prefix_test( r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 rgb(43, 12, 9), 3 lch(50.998% 135.363 338); }"#, indoc! {r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 #2b0c09, 3 #ee00be; override-colors: 1 #2b0c09, 3 lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 var(--foo), 3 lch(50.998% 135.363 338); }"#, indoc! {r#"@font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 var(--foo), 3 #ee00be; } @supports (color: lab(0% 0 0)) { @font-palette-values --Cooler { font-family: Handover Sans; base-palette: 3; override-colors: 1 var(--foo), 3 lab(50.998% 125.506 -50.7078); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); minify_test(".foo { font-palette: --Custom; }", ".foo{font-palette:--Custom}"); } #[test] fn test_page_rule() { minify_test("@page {margin: 0.5cm}", "@page{margin:.5cm}"); minify_test("@page :left {margin: 0.5cm}", "@page:left{margin:.5cm}"); minify_test("@page :right {margin: 0.5cm}", "@page:right{margin:.5cm}"); minify_test( "@page LandscapeTable {margin: 0.5cm}", "@page LandscapeTable{margin:.5cm}", ); minify_test( "@page CompanyLetterHead:first {margin: 0.5cm}", "@page CompanyLetterHead:first{margin:.5cm}", ); minify_test("@page:first {margin: 0.5cm}", "@page:first{margin:.5cm}"); minify_test("@page :blank:first {margin: 0.5cm}", "@page:blank:first{margin:.5cm}"); minify_test("@page toc, index {margin: 0.5cm}", "@page toc,index{margin:.5cm}"); minify_test( r#" @page :right { @bottom-left { margin: 10pt; } } "#, "@page:right{@bottom-left{margin:10pt}}", ); minify_test( r#" @page :right { margin: 1in; @bottom-left { margin: 10pt; } } "#, "@page:right{margin:1in;@bottom-left{margin:10pt}}", ); test( r#" @page :right { @bottom-left { margin: 10pt; } } "#, indoc! {r#" @page :right { @bottom-left { margin: 10pt; } } "#}, ); test( r#" @page :right { margin: 1in; @bottom-left-corner { content: "Foo"; } @bottom-right-corner { content: "Bar"; } } "#, indoc! {r#" @page :right { margin: 1in; @bottom-left-corner { content: "Foo"; } @bottom-right-corner { content: "Bar"; } } "#}, ); error_test( r#" @page { @foo { margin: 1in; } } "#, ParserError::AtRuleInvalid("foo".into()), ); error_test( r#" @page { @top-left-corner { @bottom-left { margin: 1in; } } } "#, ParserError::AtRuleInvalid("bottom-left".into()), ); } #[test] fn test_supports_rule() { test( r#" @supports (foo: bar) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) { .test { foo: bar; } } "#}, ); test( r#" @supports not (foo: bar) { .test { foo: bar; } } "#, indoc! { r#" @supports not (foo: bar) { .test { foo: bar; } } "#}, ); test( r#" @supports (foo: bar) or (bar: baz) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) or (bar: baz) { .test { foo: bar; } } "#}, ); test( r#" @supports (((foo: bar) or (bar: baz))) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) or (bar: baz) { .test { foo: bar; } } "#}, ); test( r#" @supports (foo: bar) and (bar: baz) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) and (bar: baz) { .test { foo: bar; } } "#}, ); test( r#" @supports (((foo: bar) and (bar: baz))) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) and (bar: baz) { .test { foo: bar; } } "#}, ); test( r#" @supports (foo: bar) and (((bar: baz) or (test: foo))) { .test { foo: bar; } } "#, indoc! { r#" @supports (foo: bar) and ((bar: baz) or (test: foo)) { .test { foo: bar; } } "#}, ); test( r#" @supports not (((foo: bar) and (bar: baz))) { .test { foo: bar; } } "#, indoc! { r#" @supports not ((foo: bar) and (bar: baz)) { .test { foo: bar; } } "#}, ); test( r#" @supports selector(a > b) { .test { foo: bar; } } "#, indoc! { r#" @supports selector(a > b) { .test { foo: bar; } } "#}, ); test( r#" @supports unknown(test) { .test { foo: bar; } } "#, indoc! { r#" @supports unknown(test) { .test { foo: bar; } } "#}, ); test( r#" @supports (unknown) { .test { foo: bar; } } "#, indoc! { r#" @supports (unknown) { .test { foo: bar; } } "#}, ); test( r#" @supports (display: grid) and (not (display: inline-grid)) { .test { foo: bar; } } "#, indoc! { r#" @supports (display: grid) and (not (display: inline-grid)) { .test { foo: bar; } } "#}, ); prefix_test( r#" @supports (backdrop-filter: blur(10px)) { div { backdrop-filter: blur(10px); } } "#, indoc! { r#" @supports ((-webkit-backdrop-filter: blur(10px)) or (backdrop-filter: blur(10px))) { div { -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); } } "#}, Browsers { safari: Some(14 << 16), ..Default::default() }, ); minify_test( r#" @supports (width: calc(10px * 2)) { .test { width: calc(10px * 2); } } "#, "@supports (width:calc(10px * 2)){.test{width:20px}}", ); minify_test( r#" @supports (color: hsl(0deg, 0%, 0%)) { .test { color: hsl(0deg, 0%, 0%); } } "#, "@supports (color:hsl(0deg, 0%, 0%)){.test{color:#000}}", ); } #[test] fn test_counter_style() { test( r#" @counter-style circled-alpha { system: fixed; symbols: Ⓐ Ⓑ Ⓒ; suffix: " "; } "#, indoc! { r#" @counter-style circled-alpha { system: fixed; symbols: Ⓐ Ⓑ Ⓒ; suffix: " "; } "#}, ); } #[test] fn test_namespace() { minify_test( "@namespace url(http://toto.example.org);", "@namespace \"http://toto.example.org\";", ); minify_test( "@namespace \"http://toto.example.org\";", "@namespace \"http://toto.example.org\";", ); minify_test( "@namespace toto \"http://toto.example.org\";", "@namespace toto \"http://toto.example.org\";", ); minify_test( "@namespace toto url(http://toto.example.org);", "@namespace toto \"http://toto.example.org\";", ); test( r#" @namespace "http://example.com/foo"; x { color: red; } "#, indoc! {r#" @namespace "http://example.com/foo"; x { color: red; } "#}, ); test( r#" @namespace toto "http://toto.example.org"; toto|x { color: red; } [toto|att=val] { color: blue } "#, indoc! {r#" @namespace toto "http://toto.example.org"; toto|x { color: red; } [toto|att="val"] { color: #00f; } "#}, ); test( r#" @namespace "http://example.com/foo"; |x { color: red; } [|att=val] { color: blue } "#, indoc! {r#" @namespace "http://example.com/foo"; |x { color: red; } [att="val"] { color: #00f; } "#}, ); test( r#" @namespace "http://example.com/foo"; *|x { color: red; } [*|att=val] { color: blue } "#, indoc! {r#" @namespace "http://example.com/foo"; *|x { color: red; } [*|att="val"] { color: #00f; } "#}, ); error_test( ".foo { color: red } @namespace \"http://example.com/foo\";", ParserError::UnexpectedNamespaceRule, ); } #[test] fn test_import() { minify_test("@import url(foo.css);", "@import \"foo.css\";"); minify_test("@import \"foo.css\";", "@import \"foo.css\";"); minify_test("@import url(foo.css) print;", "@import \"foo.css\" print;"); minify_test("@import \"foo.css\" print;", "@import \"foo.css\" print;"); minify_test( "@import \"foo.css\" screen and (orientation: landscape);", "@import \"foo.css\" screen and (orientation:landscape);", ); minify_test( "@import url(foo.css) supports(display: flex);", "@import \"foo.css\" supports(display:flex);", ); minify_test( "@import url(foo.css) supports(display: flex) print;", "@import \"foo.css\" supports(display:flex) print;", ); minify_test( "@import url(foo.css) supports(not (display: flex));", "@import \"foo.css\" supports(not (display:flex));", ); minify_test( "@import url(foo.css) supports((display: flex));", "@import \"foo.css\" supports(display:flex);", ); minify_test("@charset \"UTF-8\"; @import url(foo.css);", "@import \"foo.css\";"); minify_test("@layer foo; @import url(foo.css);", "@layer foo;@import \"foo.css\";"); error_test( ".foo { color: red } @import url(bar.css);", ParserError::UnexpectedImportRule, ); error_test( "@namespace \"http://example.com/foo\"; @import url(bar.css);", ParserError::UnexpectedImportRule, ); error_test( "@media print { .foo { color: red }} @import url(bar.css);", ParserError::UnexpectedImportRule, ); error_test( "@layer foo; @import url(foo.css); @layer bar; @import url(bar.css)", ParserError::UnexpectedImportRule, ); } #[test] fn test_prefixes() { prefix_test( r#" .foo { -webkit-transition: opacity 200ms; -moz-transition: opacity 200ms; transition: opacity 200ms; } "#, indoc! {r#" .foo { transition: opacity .2s; } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo{transition:opacity 200ms}", indoc! {r#" .foo { -webkit-transition: opacity .2s; -moz-transition: opacity .2s; transition: opacity .2s; } "#}, Browsers { safari: Some(5 << 16), firefox: Some(14 << 16), ..Browsers::default() }, ); } #[test] fn test_display() { minify_test(".foo { display: block }", ".foo{display:block}"); minify_test(".foo { display: block flow }", ".foo{display:block}"); minify_test(".foo { display: flow-root }", ".foo{display:flow-root}"); minify_test(".foo { display: block flow-root }", ".foo{display:flow-root}"); minify_test(".foo { display: inline }", ".foo{display:inline}"); minify_test(".foo { display: inline flow }", ".foo{display:inline}"); minify_test(".foo { display: inline-block }", ".foo{display:inline-block}"); minify_test(".foo { display: inline flow-root }", ".foo{display:inline-block}"); minify_test(".foo { display: run-in }", ".foo{display:run-in}"); minify_test(".foo { display: run-in flow }", ".foo{display:run-in}"); minify_test(".foo { display: list-item }", ".foo{display:list-item}"); minify_test(".foo { display: block flow list-item }", ".foo{display:list-item}"); minify_test(".foo { display: inline list-item }", ".foo{display:inline list-item}"); minify_test( ".foo { display: inline flow list-item }", ".foo{display:inline list-item}", ); minify_test(".foo { display: flex }", ".foo{display:flex}"); minify_test(".foo { display: block flex }", ".foo{display:flex}"); minify_test(".foo { display: inline-flex }", ".foo{display:inline-flex}"); minify_test(".foo { display: inline flex }", ".foo{display:inline-flex}"); minify_test(".foo { display: grid }", ".foo{display:grid}"); minify_test(".foo { display: block grid }", ".foo{display:grid}"); minify_test(".foo { display: inline-grid }", ".foo{display:inline-grid}"); minify_test(".foo { display: inline grid }", ".foo{display:inline-grid}"); minify_test(".foo { display: ruby }", ".foo{display:ruby}"); minify_test(".foo { display: inline ruby }", ".foo{display:ruby}"); minify_test(".foo { display: block ruby }", ".foo{display:block ruby}"); minify_test(".foo { display: table }", ".foo{display:table}"); minify_test(".foo { display: block table }", ".foo{display:table}"); minify_test(".foo { display: inline-table }", ".foo{display:inline-table}"); minify_test(".foo { display: inline table }", ".foo{display:inline-table}"); minify_test(".foo { display: table-row-group }", ".foo{display:table-row-group}"); minify_test(".foo { display: contents }", ".foo{display:contents}"); minify_test(".foo { display: none }", ".foo{display:none}"); minify_test(".foo { display: -webkit-flex }", ".foo{display:-webkit-flex}"); minify_test(".foo { display: -ms-flexbox }", ".foo{display:-ms-flexbox}"); minify_test(".foo { display: -webkit-box }", ".foo{display:-webkit-box}"); minify_test(".foo { display: -moz-box }", ".foo{display:-moz-box}"); minify_test( ".foo { display: -webkit-flex; display: -moz-box; display: flex }", ".foo{display:-webkit-flex;display:-moz-box;display:flex}", ); minify_test( ".foo { display: -webkit-flex; display: flex; display: -moz-box }", ".foo{display:-webkit-flex;display:flex;display:-moz-box}", ); minify_test(".foo { display: flex; display: grid }", ".foo{display:grid}"); minify_test( ".foo { display: -webkit-inline-flex; display: -moz-inline-box; display: inline-flex }", ".foo{display:-webkit-inline-flex;display:-moz-inline-box;display:inline-flex}", ); minify_test( ".foo { display: flex; display: var(--grid); }", ".foo{display:flex;display:var(--grid)}", ); prefix_test( ".foo{ display: flex }", indoc! {r#" .foo { display: -webkit-box; display: -moz-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(14 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( ".foo{ display: flex; display: -webkit-box; }", indoc! {r#" .foo { display: -webkit-box; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(14 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( ".foo{ display: -webkit-box; display: flex; }", indoc! {r#" .foo { display: -webkit-box; display: -moz-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(14 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { display: -webkit-box; display: -moz-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } "#, indoc! {r#" .foo { display: flex; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { display: -webkit-box; display: flex; display: -moz-box; display: -webkit-flex; display: -ms-flexbox; } "#, indoc! {r#" .foo { display: -moz-box; display: -webkit-flex; display: -ms-flexbox; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo{ display: inline-flex }", indoc! {r#" .foo { display: -webkit-inline-box; display: -moz-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } "#}, Browsers { safari: Some(4 << 16), firefox: Some(14 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { display: -webkit-inline-box; display: -moz-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } "#, indoc! {r#" .foo { display: inline-flex; } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); } #[test] fn test_visibility() { minify_test(".foo { visibility: visible }", ".foo{visibility:visible}"); minify_test(".foo { visibility: hidden }", ".foo{visibility:hidden}"); minify_test(".foo { visibility: collapse }", ".foo{visibility:collapse}"); minify_test(".foo { visibility: Visible }", ".foo{visibility:visible}"); } #[test] fn test_text_transform() { minify_test(".foo { text-transform: uppercase }", ".foo{text-transform:uppercase}"); minify_test(".foo { text-transform: lowercase }", ".foo{text-transform:lowercase}"); minify_test(".foo { text-transform: capitalize }", ".foo{text-transform:capitalize}"); minify_test(".foo { text-transform: none }", ".foo{text-transform:none}"); minify_test(".foo { text-transform: full-width }", ".foo{text-transform:full-width}"); minify_test( ".foo { text-transform: full-size-kana }", ".foo{text-transform:full-size-kana}", ); minify_test( ".foo { text-transform: uppercase full-width }", ".foo{text-transform:uppercase full-width}", ); minify_test( ".foo { text-transform: full-width uppercase }", ".foo{text-transform:uppercase full-width}", ); minify_test( ".foo { text-transform: uppercase full-width full-size-kana }", ".foo{text-transform:uppercase full-width full-size-kana}", ); minify_test( ".foo { text-transform: full-width uppercase full-size-kana }", ".foo{text-transform:uppercase full-width full-size-kana}", ); } #[test] fn test_whitespace() { minify_test(".foo { white-space: normal }", ".foo{white-space:normal}"); minify_test(".foo { white-space: pre }", ".foo{white-space:pre}"); minify_test(".foo { white-space: nowrap }", ".foo{white-space:nowrap}"); minify_test(".foo { white-space: pre-wrap }", ".foo{white-space:pre-wrap}"); minify_test(".foo { white-space: break-spaces }", ".foo{white-space:break-spaces}"); minify_test(".foo { white-space: pre-line }", ".foo{white-space:pre-line}"); minify_test(".foo { white-space: NoWrAp }", ".foo{white-space:nowrap}"); } #[test] fn test_tab_size() { minify_test(".foo { tab-size: 8 }", ".foo{tab-size:8}"); minify_test(".foo { tab-size: 4px }", ".foo{tab-size:4px}"); minify_test(".foo { -moz-tab-size: 4px }", ".foo{-moz-tab-size:4px}"); minify_test(".foo { -o-tab-size: 4px }", ".foo{-o-tab-size:4px}"); prefix_test( ".foo{ tab-size: 4 }", indoc! {r#" .foo { -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(50 << 16), opera: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; } "#, indoc! {r#" .foo { tab-size: 4; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(94 << 16), opera: Some(30 << 16), ..Browsers::default() }, ); } #[test] fn test_word_break() { minify_test(".foo { word-break: normal }", ".foo{word-break:normal}"); minify_test(".foo { word-break: keep-all }", ".foo{word-break:keep-all}"); minify_test(".foo { word-break: break-all }", ".foo{word-break:break-all}"); minify_test(".foo { word-break: break-word }", ".foo{word-break:break-word}"); } #[test] fn test_line_break() { minify_test(".foo { line-break: auto }", ".foo{line-break:auto}"); minify_test(".foo { line-break: Loose }", ".foo{line-break:loose}"); minify_test(".foo { line-break: anywhere }", ".foo{line-break:anywhere}"); } #[test] fn test_wrap() { minify_test(".foo { overflow-wrap: nOrmal }", ".foo{overflow-wrap:normal}"); minify_test(".foo { overflow-wrap: break-Word }", ".foo{overflow-wrap:break-word}"); minify_test(".foo { overflow-wrap: Anywhere }", ".foo{overflow-wrap:anywhere}"); minify_test(".foo { word-wrap: Normal }", ".foo{word-wrap:normal}"); minify_test(".foo { word-wrap: Break-wOrd }", ".foo{word-wrap:break-word}"); minify_test(".foo { word-wrap: Anywhere }", ".foo{word-wrap:anywhere}"); } #[test] fn test_hyphens() { minify_test(".foo { hyphens: manual }", ".foo{hyphens:manual}"); minify_test(".foo { hyphens: auto }", ".foo{hyphens:auto}"); minify_test(".foo { hyphens: none }", ".foo{hyphens:none}"); minify_test(".foo { -webkit-hyphens: manual }", ".foo{-webkit-hyphens:manual}"); minify_test(".foo { -moz-hyphens: manual }", ".foo{-moz-hyphens:manual}"); minify_test(".foo { -ms-hyphens: manual }", ".foo{-ms-hyphens:manual}"); prefix_test( ".foo{ hyphens: manual }", indoc! {r#" .foo { -webkit-hyphens: manual; -moz-hyphens: manual; -ms-hyphens: manual; hyphens: manual; } "#}, Browsers { safari: Some(14 << 16), firefox: Some(40 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-hyphens: manual; -moz-hyphens: manual; -ms-hyphens: manual; hyphens: manual; } "#, indoc! {r#" .foo { -webkit-hyphens: manual; hyphens: manual; } "#}, Browsers { safari: Some(14 << 16), chrome: Some(88 << 16), firefox: Some(88 << 16), edge: Some(79 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-hyphens: manual; -moz-hyphens: manual; -ms-hyphens: manual; hyphens: manual; } "#, indoc! {r#" .foo { hyphens: manual; } "#}, Browsers { chrome: Some(88 << 16), firefox: Some(88 << 16), edge: Some(79 << 16), ..Browsers::default() }, ); } #[test] fn test_text_align() { minify_test(".foo { text-align: left }", ".foo{text-align:left}"); minify_test(".foo { text-align: Left }", ".foo{text-align:left}"); minify_test(".foo { text-align: END }", ".foo{text-align:end}"); minify_test(".foo { text-align: left }", ".foo{text-align:left}"); prefix_test( r#" .foo { text-align: start; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { text-align: left; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { text-align: right; } "# }, Browsers { safari: Some(2 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-align: end; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { text-align: right; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { text-align: left; } "# }, Browsers { safari: Some(2 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-align: start; } "#, indoc! {r#" .foo { text-align: start; } "# }, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo > .bar { text-align: start; } "#, indoc! {r#" .foo > .bar:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { text-align: left; } .foo > .bar:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { text-align: right; } "# }, Browsers { safari: Some(2 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:after { text-align: start; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)):after { text-align: left; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)):after { text-align: right; } "# }, Browsers { safari: Some(2 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo:hover { text-align: start; } "#, indoc! {r#" .foo:hover:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { text-align: left; } .foo:hover:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { text-align: right; } "# }, Browsers { safari: Some(2 << 16), ..Browsers::default() }, ); } #[test] fn test_text_align_last() { minify_test(".foo { text-align-last: left }", ".foo{text-align-last:left}"); minify_test(".foo { text-align-last: justify }", ".foo{text-align-last:justify}"); prefix_test( ".foo{ text-align-last: left }", indoc! {r#" .foo { -moz-text-align-last: left; text-align-last: left; } "#}, Browsers { firefox: Some(40 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -moz-text-align-last: left; text-align-last: left; } "#, indoc! {r#" .foo { text-align-last: left; } "#}, Browsers { firefox: Some(88 << 16), ..Browsers::default() }, ); } #[test] fn test_text_justify() { minify_test(".foo { text-justify: auto }", ".foo{text-justify:auto}"); minify_test(".foo { text-justify: inter-word }", ".foo{text-justify:inter-word}"); } #[test] fn test_word_spacing() { minify_test(".foo { word-spacing: normal }", ".foo{word-spacing:normal}"); minify_test(".foo { word-spacing: 3px }", ".foo{word-spacing:3px}"); } #[test] fn test_letter_spacing() { minify_test(".foo { letter-spacing: normal }", ".foo{letter-spacing:normal}"); minify_test(".foo { letter-spacing: 3px }", ".foo{letter-spacing:3px}"); } #[test] fn test_text_indent() { minify_test(".foo { text-indent: 20px }", ".foo{text-indent:20px}"); minify_test(".foo { text-indent: 10% }", ".foo{text-indent:10%}"); minify_test(".foo { text-indent: 3em hanging }", ".foo{text-indent:3em hanging}"); minify_test(".foo { text-indent: 3em each-line }", ".foo{text-indent:3em each-line}"); minify_test( ".foo { text-indent: 3em hanging each-line }", ".foo{text-indent:3em hanging each-line}", ); minify_test( ".foo { text-indent: 3em each-line hanging }", ".foo{text-indent:3em hanging each-line}", ); minify_test( ".foo { text-indent: each-line 3em hanging }", ".foo{text-indent:3em hanging each-line}", ); minify_test( ".foo { text-indent: each-line hanging 3em }", ".foo{text-indent:3em hanging each-line}", ); } #[test] fn test_text_size_adjust() { minify_test(".foo { text-size-adjust: none }", ".foo{text-size-adjust:none}"); minify_test(".foo { text-size-adjust: auto }", ".foo{text-size-adjust:auto}"); minify_test(".foo { text-size-adjust: 80% }", ".foo{text-size-adjust:80%}"); prefix_test( r#" .foo { text-size-adjust: none; } "#, indoc! {r#" .foo { -webkit-text-size-adjust: none; -moz-text-size-adjust: none; -ms-text-size-adjust: none; text-size-adjust: none; } "#}, Browsers { ios_saf: Some(16 << 16), edge: Some(15 << 16), firefox: Some(20 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-text-size-adjust: none; -moz-text-size-adjust: none; -ms-text-size-adjust: none; text-size-adjust: none; } "#, indoc! {r#" .foo { text-size-adjust: none; } "#}, Browsers { chrome: Some(110 << 16), ..Browsers::default() }, ); } #[test] fn test_text_decoration() { minify_test(".foo { text-decoration-line: none }", ".foo{text-decoration-line:none}"); minify_test( ".foo { text-decoration-line: underline }", ".foo{text-decoration-line:underline}", ); minify_test( ".foo { text-decoration-line: overline }", ".foo{text-decoration-line:overline}", ); minify_test( ".foo { text-decoration-line: line-through }", ".foo{text-decoration-line:line-through}", ); minify_test( ".foo { text-decoration-line: blink }", ".foo{text-decoration-line:blink}", ); minify_test( ".foo { text-decoration-line: underline overline }", ".foo{text-decoration-line:underline overline}", ); minify_test( ".foo { text-decoration-line: overline underline }", ".foo{text-decoration-line:underline overline}", ); minify_test( ".foo { text-decoration-line: overline line-through underline }", ".foo{text-decoration-line:underline overline line-through}", ); minify_test( ".foo { text-decoration-line: spelling-error }", ".foo{text-decoration-line:spelling-error}", ); minify_test( ".foo { text-decoration-line: grammar-error }", ".foo{text-decoration-line:grammar-error}", ); minify_test( ".foo { -webkit-text-decoration-line: overline underline }", ".foo{-webkit-text-decoration-line:underline overline}", ); minify_test( ".foo { -moz-text-decoration-line: overline underline }", ".foo{-moz-text-decoration-line:underline overline}", ); minify_test( ".foo { text-decoration-style: solid }", ".foo{text-decoration-style:solid}", ); minify_test( ".foo { text-decoration-style: dotted }", ".foo{text-decoration-style:dotted}", ); minify_test( ".foo { -webkit-text-decoration-style: solid }", ".foo{-webkit-text-decoration-style:solid}", ); minify_test( ".foo { text-decoration-color: yellow }", ".foo{text-decoration-color:#ff0}", ); minify_test( ".foo { -webkit-text-decoration-color: yellow }", ".foo{-webkit-text-decoration-color:#ff0}", ); minify_test(".foo { text-decoration: none }", ".foo{text-decoration:none}"); minify_test( ".foo { text-decoration: underline dotted }", ".foo{text-decoration:underline dotted}", ); minify_test( ".foo { text-decoration: underline dotted yellow }", ".foo{text-decoration:underline dotted #ff0}", ); minify_test( ".foo { text-decoration: yellow dotted underline }", ".foo{text-decoration:underline dotted #ff0}", ); minify_test( ".foo { text-decoration: underline overline dotted yellow }", ".foo{text-decoration:underline overline dotted #ff0}", ); minify_test( ".foo { -webkit-text-decoration: yellow dotted underline }", ".foo{-webkit-text-decoration:underline dotted #ff0}", ); minify_test( ".foo { -moz-text-decoration: yellow dotted underline }", ".foo{-moz-text-decoration:underline dotted #ff0}", ); test( r#" .foo { text-decoration-line: underline; text-decoration-style: dotted; text-decoration-color: yellow; text-decoration-thickness: 2px; } "#, indoc! {r#" .foo { text-decoration: underline 2px dotted #ff0; } "#}, ); test( r#" .foo { text-decoration: underline; text-decoration-style: dotted; } "#, indoc! {r#" .foo { text-decoration: underline dotted; } "#}, ); test( r#" .foo { text-decoration: underline; text-decoration-style: var(--style); } "#, indoc! {r#" .foo { text-decoration: underline; text-decoration-style: var(--style); } "#}, ); test( r#" .foo { -webkit-text-decoration: underline; -webkit-text-decoration-style: dotted; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline dotted; } "#}, ); prefix_test( r#" .foo { text-decoration: underline dotted; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-line: underline; } "#, indoc! {r#" .foo { -webkit-text-decoration-line: underline; -moz-text-decoration-line: underline; text-decoration-line: underline; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-style: dotted; } "#, indoc! {r#" .foo { -webkit-text-decoration-style: dotted; -moz-text-decoration-style: dotted; text-decoration-style: dotted; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-color: yellow; } "#, indoc! {r#" .foo { -webkit-text-decoration-color: #ff0; -moz-text-decoration-color: #ff0; text-decoration-color: #ff0; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline; } "#, indoc! {r#" .foo { text-decoration: underline; } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } "#}, Browsers { safari: Some(14 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: double underline; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline double; text-decoration: underline double; } "#}, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline; text-decoration-style: double; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline double; text-decoration: underline double; } "#}, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline; text-decoration-color: red; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline red; text-decoration: underline red; } "#}, Browsers { safari: Some(16 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: var(--test); } "#, indoc! {r#" .foo { -webkit-text-decoration: var(--test); text-decoration: var(--test); } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); minify_test( ".foo { text-decoration-skip-ink: all }", ".foo{text-decoration-skip-ink:all}", ); minify_test( ".foo { -webkit-text-decoration-skip-ink: all }", ".foo{-webkit-text-decoration-skip-ink:all}", ); prefix_test( r#" .foo { text-decoration: lch(50.998% 135.363 338) underline; } "#, indoc! {r#" .foo { -webkit-text-decoration: underline #ee00be; text-decoration: underline #ee00be; -webkit-text-decoration: underline lch(50.998% 135.363 338); text-decoration: underline lch(50.998% 135.363 338); } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-color: lch(50.998% 135.363 338); } "#, indoc! {r#" .foo { -webkit-text-decoration-color: #ee00be; -moz-text-decoration-color: #ee00be; text-decoration-color: #ee00be; -webkit-text-decoration-color: lch(50.998% 135.363 338); -moz-text-decoration-color: lch(50.998% 135.363 338); text-decoration-color: lch(50.998% 135.363 338); } "#}, Browsers { safari: Some(8 << 16), firefox: Some(30 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: lch(50.998% 135.363 338) var(--style); } "#, indoc! {r#" .foo { text-decoration: #ee00be var(--style); } @supports (color: lab(0% 0 0)) { .foo { text-decoration: lab(50.998% 125.506 -50.7078) var(--style); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline 10px; } "#, indoc! {r#" .foo { text-decoration: underline; text-decoration-thickness: 10px; } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline 10px; } "#, indoc! {r#" .foo { text-decoration: underline 10px; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline 10%; } "#, indoc! {r#" .foo { text-decoration: underline; text-decoration-thickness: calc(1em / 10); } "#}, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline 10%; } "#, indoc! {r#" .foo { text-decoration: underline 10%; } "#}, Browsers { firefox: Some(89 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-thickness: 10%; } "#, indoc! {r#" .foo { text-decoration-thickness: calc(1em / 10); } "#}, Browsers { safari: Some(12 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration-thickness: 10%; } "#, indoc! {r#" .foo { text-decoration-thickness: 10%; } "#}, Browsers { firefox: Some(89 << 16), ..Browsers::default() }, ); } #[test] fn test_text_emphasis() { minify_test(".foo { text-emphasis-style: none }", ".foo{text-emphasis-style:none}"); minify_test( ".foo { text-emphasis-style: filled }", ".foo{text-emphasis-style:filled}", ); minify_test(".foo { text-emphasis-style: open }", ".foo{text-emphasis-style:open}"); minify_test(".foo { text-emphasis-style: dot }", ".foo{text-emphasis-style:dot}"); minify_test( ".foo { text-emphasis-style: filled dot }", ".foo{text-emphasis-style:dot}", ); minify_test( ".foo { text-emphasis-style: dot filled }", ".foo{text-emphasis-style:dot}", ); minify_test( ".foo { text-emphasis-style: open dot }", ".foo{text-emphasis-style:open dot}", ); minify_test( ".foo { text-emphasis-style: dot open }", ".foo{text-emphasis-style:open dot}", ); minify_test(".foo { text-emphasis-style: \"x\" }", ".foo{text-emphasis-style:\"x\"}"); minify_test(".foo { text-emphasis-color: yellow }", ".foo{text-emphasis-color:#ff0}"); minify_test(".foo { text-emphasis: none }", ".foo{text-emphasis:none}"); minify_test(".foo { text-emphasis: filled }", ".foo{text-emphasis:filled}"); minify_test( ".foo { text-emphasis: filled yellow }", ".foo{text-emphasis:filled #ff0}", ); minify_test( ".foo { text-emphasis: dot filled yellow }", ".foo{text-emphasis:dot #ff0}", ); test( r#" .foo { text-emphasis-style: filled; text-emphasis-color: yellow; } "#, indoc! {r#" .foo { text-emphasis: filled #ff0; } "#}, ); test( r#" .foo { text-emphasis: filled red; text-emphasis-color: yellow; } "#, indoc! {r#" .foo { text-emphasis: filled #ff0; } "#}, ); test( r#" .foo { text-emphasis: filled yellow; text-emphasis-color: var(--color); } "#, indoc! {r#" .foo { text-emphasis: filled #ff0; text-emphasis-color: var(--color); } "#}, ); prefix_test( r#" .foo { text-emphasis-style: filled; } "#, indoc! {r#" .foo { -webkit-text-emphasis-style: filled; text-emphasis-style: filled; } "#}, Browsers { safari: Some(10 << 16), chrome: Some(30 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-text-emphasis-style: filled; text-emphasis-style: filled; } "#, indoc! {r#" .foo { text-emphasis-style: filled; } "#}, Browsers { safari: Some(10 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); minify_test( ".foo { text-emphasis-position: over }", ".foo{text-emphasis-position:over}", ); minify_test( ".foo { text-emphasis-position: under }", ".foo{text-emphasis-position:under}", ); minify_test( ".foo { text-emphasis-position: over right }", ".foo{text-emphasis-position:over}", ); minify_test( ".foo { text-emphasis-position: over left }", ".foo{text-emphasis-position:over left}", ); prefix_test( r#" .foo { text-emphasis-position: over; } "#, indoc! {r#" .foo { -webkit-text-emphasis-position: over; text-emphasis-position: over; } "#}, Browsers { safari: Some(10 << 16), chrome: Some(30 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-emphasis-position: over left; } "#, indoc! {r#" .foo { text-emphasis-position: over left; } "#}, Browsers { safari: Some(10 << 16), chrome: Some(30 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-emphasis-position: var(--test); } "#, indoc! {r#" .foo { -webkit-text-emphasis-position: var(--test); text-emphasis-position: var(--test); } "#}, Browsers { safari: Some(10 << 16), chrome: Some(30 << 16), firefox: Some(45 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-emphasis: filled lch(50.998% 135.363 338); } "#, indoc! {r#" .foo { -webkit-text-emphasis: filled #ee00be; text-emphasis: filled #ee00be; -webkit-text-emphasis: filled lch(50.998% 135.363 338); text-emphasis: filled lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(25 << 16), firefox: Some(48 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-emphasis-color: lch(50.998% 135.363 338); } "#, indoc! {r#" .foo { -webkit-text-emphasis-color: #ee00be; text-emphasis-color: #ee00be; -webkit-text-emphasis-color: lch(50.998% 135.363 338); text-emphasis-color: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(25 << 16), firefox: Some(48 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-emphasis: lch(50.998% 135.363 338) var(--style); } "#, indoc! {r#" .foo { text-emphasis: #ee00be var(--style); } @supports (color: lab(0% 0 0)) { .foo { text-emphasis: lab(50.998% 125.506 -50.7078) var(--style); } } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); } #[test] fn test_text_shadow() { minify_test( ".foo { text-shadow: 1px 1px 2px yellow; }", ".foo{text-shadow:1px 1px 2px #ff0}", ); minify_test( ".foo { text-shadow: 1px 1px 2px 3px yellow; }", ".foo{text-shadow:1px 1px 2px 3px #ff0}", ); minify_test( ".foo { text-shadow: 1px 1px 0 yellow; }", ".foo{text-shadow:1px 1px #ff0}", ); minify_test( ".foo { text-shadow: 1px 1px yellow; }", ".foo{text-shadow:1px 1px #ff0}", ); minify_test( ".foo { text-shadow: 1px 1px yellow, 2px 3px red; }", ".foo{text-shadow:1px 1px #ff0,2px 3px red}", ); prefix_test( ".foo { text-shadow: 12px 12px lab(40% 56.6 39) }", indoc! { r#" .foo { text-shadow: 12px 12px #b32323; text-shadow: 12px 12px lab(40% 56.6 39); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { text-shadow: 12px 12px lab(40% 56.6 39) }", indoc! { r#" .foo { text-shadow: 12px 12px #b32323; text-shadow: 12px 12px color(display-p3 .643308 .192455 .167712); text-shadow: 12px 12px lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { text-shadow: 12px 12px lab(40% 56.6 39), 12px 12px yellow }", indoc! { r#" .foo { text-shadow: 12px 12px #b32323, 12px 12px #ff0; text-shadow: 12px 12px lab(40% 56.6 39), 12px 12px #ff0; } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { text-shadow: var(--foo) 12px lab(40% 56.6 39) }", indoc! { r#" .foo { text-shadow: var(--foo) 12px #b32323; } @supports (color: lab(0% 0 0)) { .foo { text-shadow: var(--foo) 12px lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); } #[test] fn test_break() { prefix_test( r#" .foo { box-decoration-break: clone; } "#, indoc! {r#" .foo { -webkit-box-decoration-break: clone; box-decoration-break: clone; } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { box-decoration-break: clone; } "#, indoc! {r#" .foo { box-decoration-break: clone; } "#}, Browsers { firefox: Some(95 << 16), ..Browsers::default() }, ); } #[test] fn test_position() { test( r#" .foo { position: relative; position: absolute; } "#, indoc! {r#" .foo { position: absolute; } "#}, ); test( r#" .foo { position: -webkit-sticky; position: sticky; } "#, indoc! {r#" .foo { position: -webkit-sticky; position: sticky; } "#}, ); prefix_test( r#" .foo { position: sticky; } "#, indoc! {r#" .foo { position: -webkit-sticky; position: sticky; } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { position: -webkit-sticky; position: sticky; } "#, indoc! {r#" .foo { position: sticky; } "#}, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); test( r#" .foo { top: 0; left: 0; bottom: 0; right: 0; } "#, indoc! {r#" .foo { inset: 0; } "#}, ); test( r#" .foo { top: 2px; left: 4px; bottom: 2px; right: 4px; } "#, indoc! {r#" .foo { inset: 2px 4px; } "#}, ); test( r#" .foo { top: 1px; left: 2px; bottom: 3px; right: 4px; } "#, indoc! {r#" .foo { inset: 1px 4px 3px 2px; } "#}, ); test( r#" .foo { inset-block-start: 2px; inset-block-end: 2px; inset-inline-start: 4px; inset-inline-end: 4px; } "#, indoc! {r#" .foo { inset-block: 2px; inset-inline: 4px; } "#}, ); test( r#" .foo { inset-block-start: 2px; inset-block-end: 3px; inset-inline-start: 4px; inset-inline-end: 5px; } "#, indoc! {r#" .foo { inset-block: 2px 3px; inset-inline: 4px 5px; } "#}, ); test( r#" .foo { inset-block-start: 2px; inset-block-end: 3px; inset: 4px; inset-inline-start: 4px; inset-inline-end: 5px; } "#, indoc! {r#" .foo { inset: 4px; inset-inline: 4px 5px; } "#}, ); prefix_test( r#" .foo { inset-inline-start: 2px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { left: 2px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { inset-inline-start: 2px; inset-inline-end: 4px; } "#, indoc! {r#" .foo:not(:lang(ae)):not(:lang(ar)):not(:lang(arc)):not(:lang(bcc)):not(:lang(bqi)):not(:lang(ckb)):not(:lang(dv)):not(:lang(fa)):not(:lang(glk)):not(:lang(he)):not(:lang(ku)):not(:lang(mzn)):not(:lang(nqo)):not(:lang(pnb)):not(:lang(ps)):not(:lang(sd)):not(:lang(ug)):not(:lang(ur)):not(:lang(yi)) { left: 2px; right: 4px; } .foo:-webkit-any(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { left: 4px; right: 2px; } .foo:is(:lang(ae), :lang(ar), :lang(arc), :lang(bcc), :lang(bqi), :lang(ckb), :lang(dv), :lang(fa), :lang(glk), :lang(he), :lang(ku), :lang(mzn), :lang(nqo), :lang(pnb), :lang(ps), :lang(sd), :lang(ug), :lang(ur), :lang(yi)) { left: 4px; right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { inset-inline: 2px; } "#, indoc! {r#" .foo { left: 2px; right: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { inset-block-start: 2px; } "#, indoc! {r#" .foo { top: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { inset-block-end: 2px; } "#, indoc! {r#" .foo { bottom: 2px; } "# }, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { top: 1px; left: 2px; bottom: 3px; right: 4px; } "#, indoc! {r#" .foo { top: 1px; bottom: 3px; left: 2px; right: 4px; } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); } #[test] fn test_overflow() { minify_test(".foo { overflow: hidden }", ".foo{overflow:hidden}"); minify_test(".foo { overflow: hidden hidden }", ".foo{overflow:hidden}"); minify_test(".foo { overflow: hidden auto }", ".foo{overflow:hidden auto}"); test( r#" .foo { overflow-x: hidden; overflow-y: auto; } "#, indoc! {r#" .foo { overflow: hidden auto; } "#}, ); test( r#" .foo { overflow: hidden; overflow-y: auto; } "#, indoc! {r#" .foo { overflow: hidden auto; } "#}, ); test( r#" .foo { overflow: hidden; overflow-y: var(--y); } "#, indoc! {r#" .foo { overflow: hidden; overflow-y: var(--y); } "#}, ); prefix_test( r#" .foo { overflow: hidden auto; } "#, indoc! {r#" .foo { overflow-x: hidden; overflow-y: auto; } "#}, Browsers { chrome: Some(67 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { overflow: hidden hidden; } "#, indoc! {r#" .foo { overflow: hidden; } "#}, Browsers { chrome: Some(67 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { overflow: hidden auto; } "#, indoc! {r#" .foo { overflow: hidden auto; } "#}, Browsers { chrome: Some(68 << 16), ..Browsers::default() }, ); minify_test(".foo { text-overflow: ellipsis }", ".foo{text-overflow:ellipsis}"); prefix_test( r#" .foo { text-overflow: ellipsis; } "#, indoc! {r#" .foo { -o-text-overflow: ellipsis; text-overflow: ellipsis; } "#}, Browsers { safari: Some(4 << 16), opera: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -o-text-overflow: ellipsis; text-overflow: ellipsis; } "#, indoc! {r#" .foo { text-overflow: ellipsis; } "#}, Browsers { safari: Some(4 << 16), opera: Some(14 << 16), ..Browsers::default() }, ); } #[test] fn test_ui() { minify_test(".foo { resize: both }", ".foo{resize:both}"); minify_test(".foo { resize: Horizontal }", ".foo{resize:horizontal}"); minify_test(".foo { cursor: ew-resize }", ".foo{cursor:ew-resize}"); minify_test( ".foo { cursor: url(\"test.cur\"), ew-resize }", ".foo{cursor:url(test.cur),ew-resize}", ); minify_test( ".foo { cursor: url(\"test.cur\"), url(\"foo.cur\"), ew-resize }", ".foo{cursor:url(test.cur),url(foo.cur),ew-resize}", ); minify_test(".foo { caret-color: auto }", ".foo{caret-color:auto}"); minify_test(".foo { caret-color: yellow }", ".foo{caret-color:#ff0}"); minify_test(".foo { caret-shape: block }", ".foo{caret-shape:block}"); minify_test(".foo { caret: yellow block }", ".foo{caret:#ff0 block}"); minify_test(".foo { caret: block yellow }", ".foo{caret:#ff0 block}"); minify_test(".foo { caret: block }", ".foo{caret:block}"); minify_test(".foo { caret: yellow }", ".foo{caret:#ff0}"); minify_test(".foo { caret: auto auto }", ".foo{caret:auto}"); minify_test(".foo { caret: auto }", ".foo{caret:auto}"); minify_test(".foo { caret: yellow auto }", ".foo{caret:#ff0}"); minify_test(".foo { caret: auto block }", ".foo{caret:block}"); minify_test(".foo { user-select: none }", ".foo{user-select:none}"); minify_test(".foo { -webkit-user-select: none }", ".foo{-webkit-user-select:none}"); minify_test(".foo { accent-color: auto }", ".foo{accent-color:auto}"); minify_test(".foo { accent-color: yellow }", ".foo{accent-color:#ff0}"); minify_test(".foo { appearance: None }", ".foo{appearance:none}"); minify_test( ".foo { -webkit-appearance: textfield }", ".foo{-webkit-appearance:textfield}", ); prefix_test( r#" .foo { user-select: none; } "#, indoc! {r#" .foo { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } "#}, Browsers { safari: Some(8 << 16), opera: Some(5 << 16), firefox: Some(10 << 16), ie: Some(10 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } "#, indoc! {r#" .foo { -webkit-user-select: none; user-select: none; } "#}, Browsers { safari: Some(8 << 16), opera: Some(80 << 16), firefox: Some(80 << 16), edge: Some(80 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } "#, indoc! {r#" .foo { user-select: none; } "#}, Browsers { opera: Some(80 << 16), firefox: Some(80 << 16), edge: Some(80 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { appearance: none; } "#, indoc! {r#" .foo { -webkit-appearance: none; -moz-appearance: none; -ms-appearance: none; appearance: none; } "#}, Browsers { safari: Some(8 << 16), chrome: Some(80 << 16), firefox: Some(10 << 16), ie: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-appearance: none; -moz-appearance: none; -ms-appearance: none; appearance: none; } "#, indoc! {r#" .foo { -webkit-appearance: none; appearance: none; } "#}, Browsers { safari: Some(15 << 16), chrome: Some(85 << 16), firefox: Some(80 << 16), edge: Some(85 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-appearance: none; -moz-appearance: none; -ms-appearance: none; appearance: none; } "#, indoc! {r#" .foo { appearance: none; } "#}, Browsers { chrome: Some(85 << 16), firefox: Some(80 << 16), edge: Some(85 << 16), ..Browsers::default() }, ); prefix_test( ".foo { caret-color: lch(50.998% 135.363 338) }", indoc! { r#" .foo { caret-color: #ee00be; caret-color: color(display-p3 .972962 -.362078 .804206); caret-color: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { caret: lch(50.998% 135.363 338) block }", indoc! { r#" .foo { caret: #ee00be block; caret: color(display-p3 .972962 -.362078 .804206) block; caret: lch(50.998% 135.363 338) block; } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { caret: lch(50.998% 135.363 338) var(--foo) }", indoc! { r#" .foo { caret: #ee00be var(--foo); } @supports (color: lab(0% 0 0)) { .foo { caret: lab(50.998% 125.506 -50.7078) var(--foo); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] fn test_list() { minify_test(".foo { list-style-type: disc; }", ".foo{list-style-type:disc}"); minify_test(".foo { list-style-type: \"★\"; }", ".foo{list-style-type:\"★\"}"); minify_test( ".foo { list-style-type: symbols(cyclic '○' '●'); }", ".foo{list-style-type:symbols(cyclic \"○\" \"●\")}", ); minify_test( ".foo { list-style-type: symbols('○' '●'); }", ".foo{list-style-type:symbols(\"○\" \"●\")}", ); minify_test( ".foo { list-style-type: symbols(symbolic '○' '●'); }", ".foo{list-style-type:symbols(\"○\" \"●\")}", ); minify_test( ".foo { list-style-type: symbols(symbolic url('ellipse.png')); }", ".foo{list-style-type:symbols(url(ellipse.png))}", ); minify_test( ".foo { list-style-image: url('ellipse.png'); }", ".foo{list-style-image:url(ellipse.png)}", ); minify_test( ".foo { list-style-position: outside; }", ".foo{list-style-position:outside}", ); minify_test( ".foo { list-style: \"★\" url(ellipse.png) outside; }", ".foo{list-style:\"★\" url(ellipse.png)}", ); test( r#" .foo { list-style-type: disc; list-style-image: url(ellipse.png); list-style-position: outside; } "#, indoc! {r#" .foo { list-style: url("ellipse.png"); } "#}, ); test( r#" .foo { list-style: \"★\" url(ellipse.png) outside; list-style-image: none; } "#, indoc! {r#" .foo { list-style: \"★\"; } "#}, ); test( r#" .foo { list-style: \"★\" url(ellipse.png) outside; list-style-image: var(--img); } "#, indoc! {r#" .foo { list-style: \"★\" url("ellipse.png"); list-style-image: var(--img); } "#}, ); prefix_test( ".foo { list-style-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { list-style-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)); list-style-image: -webkit-linear-gradient(#ff0f0e, #7773ff); list-style-image: linear-gradient(#ff0f0e, #7773ff); list-style-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { list-style: \"★\" linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { list-style: "★" linear-gradient(#ff0f0e, #7773ff); list-style: "★" linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { list-style: var(--foo) linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { list-style: var(--foo) linear-gradient(#ff0f0e, #7773ff); } @supports (color: lab(0% 0 0)) { .foo { list-style: var(--foo) linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] fn test_image_set() { // Spec: https://drafts.csswg.org/css-images-4/#image-set-notation // WPT: https://github.com/web-platform-tests/wpt/blob/master/css/css-images/image-set/image-set-parsing.html // test image-set() minify_test( ".foo { background: image-set(\"foo.png\" 2x, url(bar.png) 1x) }", ".foo{background:image-set(\"foo.png\" 2x,\"bar.png\" 1x)}", ); // test image-set(type()) minify_test( ".foo { background: image-set('foo.webp' type('webp'), url(foo.jpg)) }", ".foo{background:image-set(\"foo.webp\" 1x type(\"webp\"),\"foo.jpg\" 1x)}", ); minify_test( ".foo { background: image-set('foo.avif' 2x type('image/avif'), url(foo.png)) }", ".foo{background:image-set(\"foo.avif\" 2x type(\"image/avif\"),\"foo.png\" 1x)}", ); minify_test( ".foo { background: image-set(url('example.png') 3x type('image/png')) }", ".foo{background:image-set(\"example.png\" 3x type(\"image/png\"))}", ); minify_test( ".foo { background: image-set(url(example.png) type('image/png') 1x) }", ".foo{background:image-set(\"example.png\" 1x type(\"image/png\"))}", ); minify_test( ".foo { background: -webkit-image-set(url(\"foo.png\") 2x, url(bar.png) 1x) }", ".foo{background:-webkit-image-set(url(foo.png) 2x,url(bar.png) 1x)}", ); test( r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url(bar.png) 1x); background: image-set(url("foo.png") 2x, url(bar.png) 1x); } "#, indoc! {r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url("bar.png") 1x); background: image-set("foo.png" 2x, "bar.png" 1x); } "#}, ); // test image-set() test( r#" .foo { background: image-set(linear-gradient(cornflowerblue, white) 1x, url("detailed-gradient.png") 3x); } "#, indoc! {r#" .foo { background: image-set(linear-gradient(#6495ed, #fff) 1x, "detailed-gradient.png" 3x); } "#}, ); prefix_test( r#" .foo { background: image-set(url("foo.png") 2x, url(bar.png) 1x); } "#, indoc! {r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url("bar.png") 1x); background: image-set("foo.png" 2x, "bar.png" 1x); } "#}, Browsers { chrome: Some(85 << 16), firefox: Some(80 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url(bar.png) 1x); background: image-set(url("foo.png") 2x, url(bar.png) 1x); } "#, indoc! {r#" .foo { background: image-set("foo.png" 2x, "bar.png" 1x); } "#}, Browsers { firefox: Some(80 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url(bar.png) 1x); } "#, indoc! {r#" .foo { background: -webkit-image-set(url("foo.png") 2x, url("bar.png") 1x); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); for property in &[ "background", "background-image", "border-image-source", "border-image", "border-image-source", "-webkit-mask-image", "-webkit-mask", "list-style-image", "list-style", ] { prefix_test( &format!( r#" .foo {{ {}: url(foo.png); {}: image-set(url("foo.png") 2x, url(bar.png) 1x); }} "#, property, property ), &format!( indoc! {r#" .foo {{ {}: url("foo.png"); {}: -webkit-image-set(url("foo.png") 2x, url("bar.png") 1x); {}: image-set("foo.png" 2x, "bar.png" 1x); }} "#}, property, property, property ), Browsers { ie: Some(11 << 16), chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( &format!( r#" .foo {{ {}: url(foo.png); {}: image-set(url("foo.png") 2x, url(bar.png) 1x); }} "#, property, property ), &format!( indoc! {r#" .foo {{ {}: -webkit-image-set(url("foo.png") 2x, url("bar.png") 1x); {}: image-set("foo.png" 2x, "bar.png" 1x); }} "#}, property, property ), Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); } } #[test] fn test_color() { minify_test(".foo { color: yellow }", ".foo{color:#ff0}"); minify_test(".foo { color: rgb(255, 255, 0) }", ".foo{color:#ff0}"); minify_test(".foo { color: rgba(255, 255, 0, 1) }", ".foo{color:#ff0}"); minify_test(".foo { color: rgba(255, 255, 0, 0.8) }", ".foo{color:#ff0c}"); minify_test(".foo { color: rgb(128, 128, 128) }", ".foo{color:gray}"); minify_test(".foo { color: rgb(123, 255, 255) }", ".foo{color:#7bffff}"); minify_test(".foo { color: rgba(123, 255, 255, 0.5) }", ".foo{color:#7bffff80}"); minify_test(".foo { color: rgb(123 255 255) }", ".foo{color:#7bffff}"); minify_test(".foo { color: rgb(123 255 255 / .5) }", ".foo{color:#7bffff80}"); minify_test(".foo { color: rgb(123 255 255 / 50%) }", ".foo{color:#7bffff80}"); minify_test(".foo { color: rgb(48% 100% 100% / 50%) }", ".foo{color:#7affff80}"); minify_test(".foo { color: hsl(100deg, 100%, 50%) }", ".foo{color:#5f0}"); minify_test(".foo { color: hsl(100, 100%, 50%) }", ".foo{color:#5f0}"); minify_test(".foo { color: hsl(100 100% 50%) }", ".foo{color:#5f0}"); minify_test(".foo { color: hsl(100, 100%, 50%, .8) }", ".foo{color:#5f0c}"); minify_test(".foo { color: hsl(100 100% 50% / .8) }", ".foo{color:#5f0c}"); minify_test(".foo { color: hsla(100, 100%, 50%, .8) }", ".foo{color:#5f0c}"); minify_test(".foo { color: hsla(100 100% 50% / .8) }", ".foo{color:#5f0c}"); minify_test(".foo { color: transparent }", ".foo{color:#0000}"); minify_test(".foo { color: currentColor }", ".foo{color:currentColor}"); minify_test(".foo { color: hwb(194 0% 0%) }", ".foo{color:#00c4ff}"); minify_test(".foo { color: hwb(194 0% 0% / 50%) }", ".foo{color:#00c4ff80}"); minify_test(".foo { color: hwb(194 0% 50%) }", ".foo{color:#006280}"); minify_test(".foo { color: hwb(194 50% 0%) }", ".foo{color:#80e1ff}"); minify_test(".foo { color: hwb(194 50% 50%) }", ".foo{color:gray}"); // minify_test(".foo { color: ActiveText }", ".foo{color:ActiveTet}"); minify_test( ".foo { color: lab(29.2345% 39.3825 20.0664); }", ".foo{color:lab(29.2345% 39.3825 20.0664)}", ); minify_test( ".foo { color: lab(29.2345% 39.3825 20.0664 / 100%); }", ".foo{color:lab(29.2345% 39.3825 20.0664)}", ); minify_test( ".foo { color: lab(29.2345% 39.3825 20.0664 / 50%); }", ".foo{color:lab(29.2345% 39.3825 20.0664/.5)}", ); minify_test( ".foo { color: lch(29.2345% 44.2 27); }", ".foo{color:lch(29.2345% 44.2 27)}", ); minify_test( ".foo { color: lch(29.2345% 44.2 45deg); }", ".foo{color:lch(29.2345% 44.2 45)}", ); minify_test( ".foo { color: lch(29.2345% 44.2 .5turn); }", ".foo{color:lch(29.2345% 44.2 180)}", ); minify_test( ".foo { color: lch(29.2345% 44.2 27 / 100%); }", ".foo{color:lch(29.2345% 44.2 27)}", ); minify_test( ".foo { color: lch(29.2345% 44.2 27 / 50%); }", ".foo{color:lch(29.2345% 44.2 27/.5)}", ); minify_test( ".foo { color: oklab(40.101% 0.1147 0.0453); }", ".foo{color:oklab(40.101% .1147 .0453)}", ); minify_test( ".foo { color: oklch(40.101% 0.12332 21.555); }", ".foo{color:oklch(40.101% .12332 21.555)}", ); minify_test( ".foo { color: oklch(40.101% 0.12332 .5turn); }", ".foo{color:oklch(40.101% .12332 180)}", ); minify_test( ".foo { color: color(display-p3 1 0.5 0); }", ".foo{color:color(display-p3 1 .5)}", ); minify_test( ".foo { color: color(display-p3 100% 50% 0%); }", ".foo{color:color(display-p3 1 .5)}", ); minify_test( ".foo { color: color(xyz-d50 0.2005 0.14089 0.4472); }", ".foo{color:color(xyz-d50 .2005 .14089 .4472)}", ); minify_test( ".foo { color: color(xyz-d50 20.05% 14.089% 44.72%); }", ".foo{color:color(xyz-d50 .2005 .14089 .4472)}", ); minify_test( ".foo { color: color(xyz-d65 0.2005 0.14089 0.4472); }", ".foo{color:color(xyz .2005 .14089 .4472)}", ); minify_test( ".foo { color: color(xyz-d65 20.05% 14.089% 44.72%); }", ".foo{color:color(xyz .2005 .14089 .4472)}", ); minify_test( ".foo { color: color(xyz 0.2005 0.14089 0.4472); }", ".foo{color:color(xyz .2005 .14089 .4472)}", ); minify_test( ".foo { color: color(xyz 20.05% 14.089% 44.72%); }", ".foo{color:color(xyz .2005 .14089 .4472)}", ); minify_test(".foo { color: color(xyz 0.2005 0 0); }", ".foo{color:color(xyz .2005)}"); minify_test(".foo { color: color(xyz 0 0 0); }", ".foo{color:color(xyz)}"); minify_test(".foo { color: color(xyz 0 1 0); }", ".foo{color:color(xyz 0 1)}"); minify_test(".foo { color: color(xyz 0 1); }", ".foo{color:color(xyz 0 1)}"); minify_test(".foo { color: color(xyz 1); }", ".foo{color:color(xyz 1)}"); minify_test(".foo { color: color(xyz); }", ".foo{color:color(xyz)}"); minify_test( ".foo { color: color(xyz 0 1 0 / 20%); }", ".foo{color:color(xyz 0 1/.2)}", ); minify_test(".foo { color: color(xyz / 20%); }", ".foo{color:color(xyz/.2)}"); minify_test( ".foo { color: color(display-p3 100% 50% 0 / 20%); }", ".foo{color:color(display-p3 1 .5/.2)}", ); minify_test( ".foo { color: color(display-p3 100% / 20%); }", ".foo{color:color(display-p3 1/.2)}", ); minify_test(".foo { color: hsl(none none none) }", ".foo{color:#000}"); minify_test(".foo { color: hwb(none none none) }", ".foo{color:red}"); minify_test(".foo { color: rgb(none none none) }", ".foo{color:#000}"); // If the browser doesn't support `#rrggbbaa` color syntax, it is converted to `transparent`. attr_test( "color: rgba(0, 0, 0, 0)", "color:transparent", true, Some(Browsers { chrome: Some(61 << 16), // Chrome >= 62 supports `#rrggbbaa` color. ..Browsers::default() }), ); attr_test( "color: #0000", "color:transparent", true, Some(Browsers { chrome: Some(61 << 16), // Chrome >= 62 supports `#rrggbbaa` color. ..Browsers::default() }), ); attr_test( "color: transparent", "color:transparent", true, Some(Browsers { chrome: Some(61 << 16), ..Browsers::default() }), ); attr_test( "color: rgba(0, 0, 0, 0)", "color: rgba(0, 0, 0, 0)", false, Some(Browsers { chrome: Some(61 << 16), ..Browsers::default() }), ); attr_test( "color: rgba(255, 0, 0, 0)", "color:rgba(255,0,0,0)", true, Some(Browsers { chrome: Some(61 << 16), ..Browsers::default() }), ); attr_test( "color: rgba(255, 0, 0, 0)", "color:#f000", true, Some(Browsers { chrome: Some(62 << 16), ..Browsers::default() }), ); prefix_test( ".foo { color: rgba(123, 456, 789, 0.5) }", indoc! { r#" .foo { color: #7bffff80; } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo { color: rgba(123, 255, 255, 0.5) }", indoc! { r#" .foo { color: rgba(123, 255, 255, .5); } "#}, Browsers { ie: Some(11 << 16), ..Browsers::default() }, ); prefix_test( ".foo { color: #7bffff80 }", indoc! { r#" .foo { color: rgba(123, 255, 255, .5); } "#}, Browsers { ie: Some(11 << 16), ..Browsers::default() }, ); prefix_test( ".foo { color: rgba(123, 456, 789, 0.5) }", indoc! { r#" .foo { color: rgba(123, 255, 255, .5); } "#}, Browsers { firefox: Some(48 << 16), safari: Some(10 << 16), ios_saf: Some(9 << 16), ..Browsers::default() }, ); prefix_test( ".foo { color: rgba(123, 456, 789, 0.5) }", indoc! { r#" .foo { color: #7bffff80; } "#}, Browsers { firefox: Some(49 << 16), safari: Some(10 << 16), ios_saf: Some(10 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: lab(40% 56.6 39) }", indoc! { r#" .foo { background-color: #b32323; background-color: lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: lch(40% 68.735435 34.568626) }", indoc! { r#" .foo { background-color: #b32323; background-color: lch(40% 68.7354 34.5686); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: oklab(59.686% 0.1009 0.1192); }", indoc! { r#" .foo { background-color: #c65d07; background-color: lab(52.2319% 40.1449 59.9171); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: oklch(40% 0.1268735435 34.568626) }", indoc! { r#" .foo { background-color: #7e250f; background-color: lab(29.2661% 38.2437 35.3889); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: lab(40% 56.6 39) }", indoc! { r#" .foo { background-color: lab(40% 56.6 39); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: oklab(59.686% 0.1009 0.1192); }", indoc! { r#" .foo { background-color: #c65d07; background-color: lab(52.2319% 40.1449 59.9171); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: oklab(59.686% 0.1009 0.1192); }", indoc! { r#" .foo { background-color: #c65d07; background-color: color(display-p3 .724144 .386777 .148795); background-color: lab(52.2319% 40.1449 59.9171); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: lab(40% 56.6 39) }", indoc! { r#" .foo { background-color: #b32323; background-color: color(display-p3 .643308 .192455 .167712); background-color: lab(40% 56.6 39); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: oklch(59.686% 0.15619 49.7694); }", indoc! { r#" .foo { background-color: #c65d06; background-color: lab(52.2321% 40.1417 59.9527); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(sRGB 0.41587 0.503670 0.36664); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(srgb .41587 .50367 .36664); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(display-p3 0.43313 0.50108 0.37950); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(display-p3 .43313 .50108 .3795); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(display-p3 0.43313 0.50108 0.37950); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(display-p3 .43313 .50108 .3795); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(display-p3 0.43313 0.50108 0.37950); }", indoc! { r#" .foo { background-color: color(display-p3 .43313 .50108 .3795); } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(display-p3 0.43313 0.50108 0.37950); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(display-p3 .43313 .50108 .3795); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(display-p3 0.43313 0.50108 0.37950); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(display-p3 .43313 .50108 .3795); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(a98-rgb 0.44091 0.49971 0.37408); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(a98-rgb .44091 .49971 .37408); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(a98-rgb 0.44091 0.49971 0.37408); }", indoc! { r#" .foo { background-color: color(a98-rgb .44091 .49971 .37408); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(prophoto-rgb 0.36589 0.41717 0.31333); }", indoc! { r#" .foo { background-color: #6a805d; background-color: color(prophoto-rgb .36589 .41717 .31333); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(rec2020 0.42210 0.47580 0.35605); }", indoc! { r#" .foo { background-color: #728765; background-color: color(rec2020 .4221 .4758 .35605); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(xyz-d50 0.2005 0.14089 0.4472); }", indoc! { r#" .foo { background-color: #7654cd; background-color: color(xyz-d50 .2005 .14089 .4472); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: color(xyz-d65 0.21661 0.14602 0.59452); }", indoc! { r#" .foo { background-color: #7654cd; background-color: color(xyz .21661 .14602 .59452); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background-color: lch(50.998% 135.363 338) }", indoc! { r#" .foo { background-color: #ee00be; background-color: color(display-p3 .972962 -.362078 .804206); background-color: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { color: lch(50.998% 135.363 338) }", indoc! { r#" .foo { color: #ee00be; color: color(display-p3 .972962 -.362078 .804206); color: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { background: var(--image) lch(40% 68.735435 34.568626) }", indoc! { r#" .foo { background: var(--image) #b32323; } @supports (color: lab(0% 0 0)) { .foo { background: var(--image) lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --a: rgb(0 0 0 / var(--alpha)); --b: rgb(50% 50% 50% / var(--alpha)); --c: rgb(var(--x) 0 0); --d: rgb(0 var(--x) 0); --e: rgb(0 0 var(--x)); --f: rgb(var(--x) 0 0 / var(--alpha)); --g: rgb(0 var(--x) 0 / var(--alpha)); --h: rgb(0 0 var(--x) / var(--alpha)); --i: rgb(none 0 0 / var(--alpha)); } "#, indoc! { r#" .foo { --a: rgba(0, 0, 0, var(--alpha)); --b: rgba(128, 128, 128, var(--alpha)); --c: rgb(var(--x) 0 0); --d: rgb(0 var(--x) 0); --e: rgb(0 0 var(--x)); --f: rgb(var(--x) 0 0 / var(--alpha)); --g: rgb(0 var(--x) 0 / var(--alpha)); --h: rgb(0 0 var(--x) / var(--alpha)); --i: rgb(none 0 0 / var(--alpha)); } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --a: rgb(0 0 0 / var(--alpha)); --b: rgb(50% 50% 50% / var(--alpha)); --c: rgb(var(--x) 0 0); --d: rgb(0 var(--x) 0); --e: rgb(0 0 var(--x)); --f: rgb(var(--x) 0 0 / var(--alpha)); --g: rgb(0 var(--x) 0 / var(--alpha)); --h: rgb(0 0 var(--x) / var(--alpha)); --i: rgb(none 0 0 / var(--alpha)); } "#, indoc! { r#" .foo { --a: rgb(0 0 0 / var(--alpha)); --b: rgb(128 128 128 / var(--alpha)); --c: rgb(var(--x) 0 0); --d: rgb(0 var(--x) 0); --e: rgb(0 0 var(--x)); --f: rgb(var(--x) 0 0 / var(--alpha)); --g: rgb(0 var(--x) 0 / var(--alpha)); --h: rgb(0 0 var(--x) / var(--alpha)); --i: rgb(none 0 0 / var(--alpha)); } "#}, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --a: hsl(270 100% 50% / var(--alpha)); --b: hsl(var(--x) 0 0); --c: hsl(0 var(--x) 0); --d: hsl(0 0 var(--x)); --e: hsl(var(--x) 0 0 / var(--alpha)); --f: hsl(0 var(--x) 0 / var(--alpha)); --g: hsl(0 0 var(--x) / var(--alpha)); --h: hsl(270 100% 50% / calc(var(--alpha) / 2)); --i: hsl(none 100% 50% / var(--alpha)); } "#, indoc! { r#" .foo { --a: hsla(270, 100%, 50%, var(--alpha)); --b: hsl(var(--x) 0 0); --c: hsl(0 var(--x) 0); --d: hsl(0 0 var(--x)); --e: hsl(var(--x) 0 0 / var(--alpha)); --f: hsl(0 var(--x) 0 / var(--alpha)); --g: hsl(0 0 var(--x) / var(--alpha)); --h: hsla(270, 100%, 50%, calc(var(--alpha) / 2)); --i: hsl(none 100% 50% / var(--alpha)); } "#}, Browsers { safari: Some(11 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --a: hsl(270 100% 50% / var(--alpha)); --b: hsl(var(--x) 0 0); --c: hsl(0 var(--x) 0); --d: hsl(0 0 var(--x)); --e: hsl(var(--x) 0 0 / var(--alpha)); --f: hsl(0 var(--x) 0 / var(--alpha)); --g: hsl(0 0 var(--x) / var(--alpha)); --h: hsl(270 100% 50% / calc(var(--alpha) / 2)); --i: hsl(none 100% 50% / var(--alpha)); } "#, indoc! { r#" .foo { --a: hsl(270 100% 50% / var(--alpha)); --b: hsl(var(--x) 0 0); --c: hsl(0 var(--x) 0); --d: hsl(0 0 var(--x)); --e: hsl(var(--x) 0 0 / var(--alpha)); --f: hsl(0 var(--x) 0 / var(--alpha)); --g: hsl(0 0 var(--x) / var(--alpha)); --h: hsl(270 100% 50% / calc(var(--alpha) / 2)); --i: hsl(none 100% 50% / var(--alpha)); } "#}, Browsers { safari: Some(13 << 16), ..Browsers::default() }, ); test( r#" .foo { --a: rgb(50% 50% 50% / calc(100% / 2)); --b: hsl(calc(360deg / 2) 50% 50%); --c: oklab(40.101% calc(0.1 + 0.2) 0.0453); --d: color(display-p3 0.43313 0.50108 calc(0.1 + 0.2)); --e: rgb(calc(255 / 2), calc(255 / 2), calc(255 / 2)); } "#, indoc! { r#" .foo { --a: #80808080; --b: #40bfbf; --c: oklab(40.101% .3 .0453); --d: color(display-p3 .43313 .50108 .3); --e: gray; } "#}, ); } #[test] fn test_relative_color() { fn test(input: &str, output: &str) { let output = CssColor::parse_string(output) .unwrap() .to_css_string(PrinterOptions { minify: true, ..PrinterOptions::default() }) .unwrap(); minify_test( &format!(".foo {{ color: {} }}", input), &format!(".foo{{color:{}}}", output), ); } test("lab(from indianred calc(l * .8) a b)", "lab(43.1402% 45.7516 23.1557)"); test("lch(from indianred calc(l + 10%) c h)", "lch(63.9252% 51.2776 26.8448)"); test("lch(from indianred l calc(c - 50) h)", "lch(53.9252% 1.27763 26.8448)"); test( "lch(from indianred l c calc(h + 180deg))", "lch(53.9252% 51.2776 206.845)", ); test("lch(from orchid l 30 h)", "lch(62.7526% 30 326.969)"); test("lch(from orchid l 30 h)", "lch(62.7526% 30 326.969)"); test("lch(from peru calc(l * 0.8) c h)", "lch(49.8022% 54.0117 63.6804)"); test("rgb(from indianred 255 g b)", "rgb(255, 92, 92)"); test("rgb(from indianred r g b / .5)", "rgba(205, 92, 92, .5)"); test( "rgb(from rgba(205, 92, 92, .5) r g b / calc(alpha + .2))", "rgba(205, 92, 92, .7)", ); test( "rgb(from rgba(205, 92, 92, .5) r g b / calc(alpha + 20%))", "rgba(205, 92, 92, .7)", ); test("lch(from indianred l sin(c) h)", "lch(53.9252% .84797 26.8448)"); test("lch(from indianred l sqrt(c) h)", "lch(53.9252% 7.16084 26.8448)"); test("lch(from indianred l c sin(h))", "lch(53.9252% 51.2776 .990043)"); // The following tests were converted from WPT: https://github.com/web-platform-tests/wpt/blob/master/css/css-color/parsing/relative-color-valid.html // Find: test_valid_value\(`color`, `(.*?)`,\s*`(.*?)`\) // Replace: test("$1", "$2") // Testing no modifications. test("rgb(from rebeccapurple r g b)", "#639"); test("rgb(from rebeccapurple r g b / alpha)", "#639"); test("rgb(from rgb(20%, 40%, 60%, 80%) r g b / alpha)", "#369c"); test("rgb(from hsl(120deg 20% 50% / .5) r g b / alpha)", "#66996680"); // Test nesting relative colors. test("rgb(from rgb(from rebeccapurple r g b) r g b)", "#639"); // Testing non-sRGB origin colors to see gamut mapping. test("rgb(from color(display-p3 0 1 0) r g b / alpha)", "#00f942"); // Naive clip based mapping would give rgb(0, 255, 0). test("rgb(from lab(100% 104.3 -50.9) r g b)", "#fff"); // Naive clip based mapping would give rgb(255, 150, 255). test("rgb(from lab(0% 104.3 -50.9) r g b)", "#2a0022"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black. test("rgb(from lch(100% 116 334) r g b)", "#fff"); // Naive clip based mapping would give rgb(255, 150, 255). test("rgb(from lch(0% 116 334) r g b)", "#2a0022"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black. test("rgb(from oklab(100% 0.365 -0.16) r g b)", "#fff"); // Naive clip based mapping would give rgb(255, 92, 255). test("rgb(from oklab(0% 0.365 -0.16) r g b)", "#000"); // Naive clip based mapping would give rgb(19, 0, 24). test("rgb(from oklch(100% 0.399 336.3) r g b)", "#fff"); // Naive clip based mapping would give rgb(255, 91, 255). test("rgb(from oklch(0% 0.399 336.3) r g b)", "#000"); // Naive clip based mapping would give rgb(20, 0, 24). // Testing replacement with 0. test("rgb(from rebeccapurple 0 0 0)", "rgb(0, 0, 0)"); test("rgb(from rebeccapurple 0 0 0 / 0)", "rgba(0, 0, 0, 0)"); test("rgb(from rebeccapurple 0 g b / alpha)", "rgb(0, 51, 153)"); test("rgb(from rebeccapurple r 0 b / alpha)", "rgb(102, 0, 153)"); test("rgb(from rebeccapurple r g 0 / alpha)", "rgb(102, 51, 0)"); test("rgb(from rebeccapurple r g b / 0)", "rgba(102, 51, 153, 0)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)", "rgba(0, 102, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)", "rgba(51, 0, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)", "rgba(51, 102, 0, 0.8)", ); test("rgb(from rgb(20%, 40%, 60%, 80%) r g b / 0)", "rgba(51, 102, 153, 0)"); // Testing replacement with a number. test("rgb(from rebeccapurple 25 g b / alpha)", "rgb(25, 51, 153)"); test("rgb(from rebeccapurple r 25 b / alpha)", "rgb(102, 25, 153)"); test("rgb(from rebeccapurple r g 25 / alpha)", "rgb(102, 51, 25)"); test("rgb(from rebeccapurple r g b / .25)", "rgba(102, 51, 153, 0.25)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)", "rgba(25, 102, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)", "rgba(51, 25, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)", "rgba(51, 102, 25, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g b / .20)", "rgba(51, 102, 153, 0.2)", ); // Testing replacement with a percentage. test("rgb(from rebeccapurple 20% g b / alpha)", "rgb(51, 51, 153)"); test("rgb(from rebeccapurple r 20% b / alpha)", "rgb(102, 51, 153)"); test("rgb(from rebeccapurple r g 20% / alpha)", "rgb(102, 51, 51)"); test("rgb(from rebeccapurple r g b / 20%)", "rgba(102, 51, 153, 0.2)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)", "rgba(51, 102, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)", "rgba(51, 51, 153, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)", "rgba(51, 102, 51, 0.8)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g b / 20%)", "rgba(51, 102, 153, 0.2)", ); // Testing replacement with a number for r, g, b but percent for alpha. test("rgb(from rebeccapurple 25 g b / 25%)", "rgba(25, 51, 153, 0.25)"); test("rgb(from rebeccapurple r 25 b / 25%)", "rgba(102, 25, 153, 0.25)"); test("rgb(from rebeccapurple r g 25 / 25%)", "rgba(102, 51, 25, 0.25)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)", "rgba(25, 102, 153, 0.25)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)", "rgba(51, 25, 153, 0.25)", ); test( "rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)", "rgba(51, 102, 25, 0.25)", ); // Testing permutation. test("rgb(from rebeccapurple g b r)", "rgb(51, 153, 102)"); test("rgb(from rebeccapurple b alpha r / g)", "rgba(153, 255, 102, 0.2)"); test("rgb(from rebeccapurple r r r / r)", "rgba(102, 102, 102, 0.4)"); test( "rgb(from rebeccapurple alpha alpha alpha / alpha)", "rgb(255, 255, 255)", ); test("rgb(from rgb(20%, 40%, 60%, 80%) g b r)", "rgb(102, 153, 51)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) b alpha r / g)", "rgba(153, 204, 51, 0.4)", ); test("rgb(from rgb(20%, 40%, 60%, 80%) r r r / r)", "rgba(51, 51, 51, 0.2)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)", "rgba(204, 204, 204, 0.8)", ); // Testing mixes of number and percentage. (These would not be allowed in the non-relative syntax). test("rgb(from rebeccapurple r 20% 10)", "rgb(102, 51, 10)"); test("rgb(from rebeccapurple r 10 20%)", "rgb(102, 10, 51)"); test("rgb(from rebeccapurple 0% 10 10)", "rgb(0, 10, 10)"); test("rgb(from rgb(20%, 40%, 60%, 80%) r 20% 10)", "rgb(51, 51, 10)"); test("rgb(from rgb(20%, 40%, 60%, 80%) r 10 20%)", "rgb(51, 10, 51)"); test("rgb(from rgb(20%, 40%, 60%, 80%) 0% 10 10)", "rgb(0, 10, 10)"); // Testing with calc(). test("rgb(from rebeccapurple calc(r) calc(g) calc(b))", "rgb(102, 51, 153)"); test("rgb(from rebeccapurple r calc(g * 2) 10)", "rgb(102, 102, 10)"); test("rgb(from rebeccapurple b calc(r * .5) 10)", "rgb(153, 51, 10)"); test("rgb(from rebeccapurple r calc(g * .5 + g * .5) 10)", "rgb(102, 51, 10)"); test("rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)", "rgb(102, 51, 10)"); test( "rgb(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))", "rgba(51, 102, 153, 0.8)", ); // Testing with 'none'. test("rgb(from rebeccapurple none none none)", "rgb(0, 0, 0)"); test("rgb(from rebeccapurple none none none / none)", "rgba(0, 0, 0, 0)"); test("rgb(from rebeccapurple r g none)", "rgb(102, 51, 0)"); test("rgb(from rebeccapurple r g none / alpha)", "rgb(102, 51, 0)"); test("rgb(from rebeccapurple r g b / none)", "rgba(102, 51, 153, 0)"); test( "rgb(from rgb(20% 40% 60% / 80%) r g none / alpha)", "rgba(51, 102, 0, 0.8)", ); test("rgb(from rgb(20% 40% 60% / 80%) r g b / none)", "rgba(51, 102, 153, 0)"); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test("rgb(from rgb(none none none) r g b)", "rgb(0, 0, 0)"); test("rgb(from rgb(none none none / none) r g b / alpha)", "rgba(0, 0, 0, 0)"); test("rgb(from rgb(20% none 60%) r g b)", "rgb(51, 0, 153)"); test( "rgb(from rgb(20% 40% 60% / none) r g b / alpha)", "rgba(51, 102, 153, 0)", ); // hsl(from ...) // Testing no modifications. test("hsl(from rebeccapurple h s l)", "rgb(102, 51, 153)"); test("hsl(from rebeccapurple h s l / alpha)", "rgb(102, 51, 153)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) h s l / alpha)", "rgba(51, 102, 153, 0.8)", ); test( "hsl(from hsl(120deg 20% 50% / .5) h s l / alpha)", "rgba(102, 153, 102, 0.5)", ); // Test nesting relative colors. test("hsl(from hsl(from rebeccapurple h s l) h s l)", "rgb(102, 51, 153)"); // Testing non-sRGB origin colors to see gamut mapping. test("hsl(from color(display-p3 0 1 0) h s l / alpha)", "rgb(0, 249, 66)"); // Naive clip based mapping would give rgb(0, 255, 0). test("hsl(from lab(100% 104.3 -50.9) h s l)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 150, 255). test("hsl(from lab(0% 104.3 -50.9) h s l)", "rgb(42, 0, 34)"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, test("hsl(from lch(100% 116 334) h s l)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 150, 255). test("hsl(from lch(0% 116 334) h s l)", "rgb(42, 0, 34)"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, test("hsl(from oklab(100% 0.365 -0.16) h s l)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 92, 255). test("hsl(from oklab(0% 0.365 -0.16) h s l)", "rgb(0, 0, 0)"); // Naive clip based mapping would give rgb(19, 0, 24). test("hsl(from oklch(100% 0.399 336.3) h s l)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 91, 255). test("hsl(from oklch(0% 0.399 336.3) h s l)", "rgb(0, 0, 0)"); // Naive clip based mapping would give rgb(20, 0, 24). // Testing replacement with 0. test("hsl(from rebeccapurple 0 0% 0%)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple 0deg 0% 0%)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple 0 0% 0% / 0)", "rgba(0, 0, 0, 0)"); test("hsl(from rebeccapurple 0deg 0% 0% / 0)", "rgba(0, 0, 0, 0)"); test("hsl(from rebeccapurple 0 s l / alpha)", "rgb(153, 51, 51)"); test("hsl(from rebeccapurple 0deg s l / alpha)", "rgb(153, 51, 51)"); test("hsl(from rebeccapurple h 0% l / alpha)", "rgb(102, 102, 102)"); test("hsl(from rebeccapurple h s 0% / alpha)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple h s l / 0)", "rgba(102, 51, 153, 0)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)", "rgba(153, 51, 51, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)", "rgba(153, 51, 51, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)", "rgba(102, 102, 102, 0.8)", ); test("hsl(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)", "rgba(0, 0, 0, 0.8)"); test("hsl(from rgb(20%, 40%, 60%, 80%) h s l / 0)", "rgba(51, 102, 153, 0)"); // Testing replacement with a constant. test("hsl(from rebeccapurple 25 s l / alpha)", "rgb(153, 93, 51)"); test("hsl(from rebeccapurple 25deg s l / alpha)", "rgb(153, 93, 51)"); test("hsl(from rebeccapurple h 20% l / alpha)", "rgb(102, 82, 122)"); test("hsl(from rebeccapurple h s 20% / alpha)", "rgb(51, 25, 77)"); test("hsl(from rebeccapurple h s l / .25)", "rgba(102, 51, 153, 0.25)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)", "rgba(153, 93, 51, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)", "rgba(153, 93, 51, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)", "rgba(82, 102, 122, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)", "rgba(25, 51, 77, 0.8)", ); test( "hsl(from rgb(20%, 40%, 60%, 80%) h s l / .2)", "rgba(51, 102, 153, 0.2)", ); // Testing valid permutation (types match). test("hsl(from rebeccapurple h l s)", "rgb(128, 77, 179)"); test("hsl(from rebeccapurple h alpha l / s)", "rgba(102, 0, 204, 0.5)"); test("hsl(from rebeccapurple h l l / l)", "rgba(102, 61, 143, 0.4)"); test("hsl(from rebeccapurple h alpha alpha / alpha)", "rgb(255, 255, 255)"); test("hsl(from rgb(20%, 40%, 60%, 80%) h l s)", "rgb(77, 128, 179)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)", "rgba(20, 102, 184, 0.5)", ); test("hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)", "rgba(61, 102, 143, 0.4)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)", "rgba(163, 204, 245, 0.8)", ); // Testing with calc(). test("hsl(from rebeccapurple calc(h) calc(s) calc(l))", "rgb(102, 51, 153)"); test( "hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))", "rgba(51, 102, 153, 0.8)", ); // Testing with 'none'. test("hsl(from rebeccapurple none none none)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple none none none / none)", "rgba(0, 0, 0, 0)"); test("hsl(from rebeccapurple h s none)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple h s none / alpha)", "rgb(0, 0, 0)"); test("hsl(from rebeccapurple h s l / none)", "rgba(102, 51, 153, 0)"); test("hsl(from rebeccapurple none s l / alpha)", "rgb(153, 51, 51)"); test( "hsl(from hsl(120deg 20% 50% / .5) h s none / alpha)", "rgba(0, 0, 0, 0.5)", ); test( "hsl(from hsl(120deg 20% 50% / .5) h s l / none)", "rgba(102, 153, 102, 0)", ); test( "hsl(from hsl(120deg 20% 50% / .5) none s l / alpha)", "rgba(153, 102, 102, 0.5)", ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test("hsl(from hsl(none none none) h s l)", "rgb(0, 0, 0)"); test("hsl(from hsl(none none none / none) h s l / alpha)", "rgba(0, 0, 0, 0)"); test("hsl(from hsl(120deg none 50% / .5) h s l)", "rgb(128, 128, 128)"); test( "hsl(from hsl(120deg 20% 50% / none) h s l / alpha)", "rgba(102, 153, 102, 0)", ); test( "hsl(from hsl(none 20% 50% / .5) h s l / alpha)", "rgba(153, 102, 102, 0.5)", ); // hwb(from ...) // Testing no modifications. test("hwb(from rebeccapurple h w b)", "rgb(102, 51, 153)"); test("hwb(from rebeccapurple h w b / alpha)", "rgb(102, 51, 153)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) h w b / alpha)", "rgba(51, 102, 153, 0.8)", ); test( "hwb(from hsl(120deg 20% 50% / .5) h w b / alpha)", "rgba(102, 153, 102, 0.5)", ); // Test nesting relative colors. test("hwb(from hwb(from rebeccapurple h w b) h w b)", "rgb(102, 51, 153)"); // Testing non-sRGB origin colors to see gamut mapping. test("hwb(from color(display-p3 0 1 0) h w b / alpha)", "rgb(0, 249, 66)"); // Naive clip based mapping would give rgb(0, 255, 0). test("hwb(from lab(100% 104.3 -50.9) h w b)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 150, 255). test("hwb(from lab(0% 104.3 -50.9) h w b)", "rgb(42, 0, 34)"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, test("hwb(from lch(100% 116 334) h w b)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 150, 255). test("hwb(from lch(0% 116 334) h w b)", "rgb(42, 0, 34)"); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, test("hwb(from oklab(100% 0.365 -0.16) h w b)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 92, 255). test("hwb(from oklab(0% 0.365 -0.16) h w b)", "rgb(0, 0, 0)"); // Naive clip based mapping would give rgb(19, 0, 24). test("hwb(from oklch(100% 0.399 336.3) h w b)", "rgb(255, 255, 255)"); // Naive clip based mapping would give rgb(255, 91, 255). test("hwb(from oklch(0% 0.399 336.3) h w b)", "rgb(0, 0, 0)"); // Naive clip based mapping would give rgb(20, 0, 24). // Testing replacement with 0. test("hwb(from rebeccapurple 0 0% 0%)", "rgb(255, 0, 0)"); test("hwb(from rebeccapurple 0deg 0% 0%)", "rgb(255, 0, 0)"); test("hwb(from rebeccapurple 0 0% 0% / 0)", "rgba(255, 0, 0, 0)"); test("hwb(from rebeccapurple 0deg 0% 0% / 0)", "rgba(255, 0, 0, 0)"); test("hwb(from rebeccapurple 0 w b / alpha)", "rgb(153, 51, 51)"); test("hwb(from rebeccapurple 0deg w b / alpha)", "rgb(153, 51, 51)"); test("hwb(from rebeccapurple h 0% b / alpha)", "rgb(77, 0, 153)"); test("hwb(from rebeccapurple h w 0% / alpha)", "rgb(153, 51, 255)"); test("hwb(from rebeccapurple h w b / 0)", "rgba(102, 51, 153, 0)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) 0 w b / alpha)", "rgba(153, 51, 51, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) 0deg w b / alpha)", "rgba(153, 51, 51, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) h 0% b / alpha)", "rgba(0, 77, 153, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) h w 0% / alpha)", "rgba(51, 153, 255, 0.8)", ); test("hwb(from rgb(20%, 40%, 60%, 80%) h w b / 0)", "rgba(51, 102, 153, 0)"); // Testing replacement with a constant. test("hwb(from rebeccapurple 25 w b / alpha)", "rgb(153, 93, 51)"); test("hwb(from rebeccapurple 25deg w b / alpha)", "rgb(153, 93, 51)"); test("hwb(from rebeccapurple h 20% b / alpha)", "rgb(102, 51, 153)"); test("hwb(from rebeccapurple h w 20% / alpha)", "rgb(128, 51, 204)"); test("hwb(from rebeccapurple h w b / .2)", "rgba(102, 51, 153, 0.2)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) 25 w b / alpha)", "rgba(153, 93, 51, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) 25deg w b / alpha)", "rgba(153, 93, 51, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) h 20% b / alpha)", "rgba(51, 102, 153, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) h w 20% / alpha)", "rgba(51, 128, 204, 0.8)", ); test( "hwb(from rgb(20%, 40%, 60%, 80%) h w b / .2)", "rgba(51, 102, 153, 0.2)", ); // Testing valid permutation (types match). test("hwb(from rebeccapurple h b w)", "rgb(153, 102, 204)"); test("hwb(from rebeccapurple h alpha w / b)", "rgba(213, 213, 213, 0.4)"); test("hwb(from rebeccapurple h w w / w)", "rgba(128, 51, 204, 0.2)"); test("hwb(from rebeccapurple h alpha alpha / alpha)", "rgb(128, 128, 128)"); test("hwb(from rgb(20%, 40%, 60%, 80%) h b w)", "rgb(102, 153, 204)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)", "rgba(204, 204, 204, 0.4)", ); test("hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)", "rgba(51, 128, 204, 0.2)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)", "rgba(128, 128, 128, 0.8)", ); // Testing with calc(). test("hwb(from rebeccapurple calc(h) calc(w) calc(b))", "rgb(102, 51, 153)"); test( "hwb(from rgb(20%, 40%, 60%, 80%) calc(h) calc(w) calc(b) / calc(alpha))", "rgba(51, 102, 153, 0.8)", ); // Testing with 'none'. test("hwb(from rebeccapurple none none none)", "rgb(255, 0, 0)"); test("hwb(from rebeccapurple none none none / none)", "rgba(255, 0, 0, 0)"); test("hwb(from rebeccapurple h w none)", "rgb(153, 51, 255)"); test("hwb(from rebeccapurple h w none / alpha)", "rgb(153, 51, 255)"); test("hwb(from rebeccapurple h w b / none)", "rgba(102, 51, 153, 0)"); test("hwb(from rebeccapurple none w b / alpha)", "rgb(153, 51, 51)"); test( "hwb(from hwb(120deg 20% 50% / .5) h w none / alpha)", "rgba(51, 255, 51, 0.5)", ); test( "hwb(from hwb(120deg 20% 50% / .5) h w b / none)", "rgba(51, 128, 51, 0)", ); test( "hwb(from hwb(120deg 20% 50% / .5) none w b / alpha)", "rgba(128, 51, 51, 0.5)", ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test("hwb(from hwb(none none none) h w b)", "rgb(255, 0, 0)"); test( "hwb(from hwb(none none none / none) h w b / alpha)", "rgba(255, 0, 0, 0)", ); test("hwb(from hwb(120deg none 50% / .5) h w b)", "rgb(0, 128, 0)"); test( "hwb(from hwb(120deg 20% 50% / none) h w b / alpha)", "rgba(51, 128, 51, 0)", ); test( "hwb(from hwb(none 20% 50% / .5) h w b / alpha)", "rgba(128, 51, 51, 0.5)", ); for color_space in &["lab", "oklab"] { // Testing no modifications. test( &format!("{}(from {}(25% 20 50) l a b)", color_space, color_space), &format!("{}(25% 20 50)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a b / alpha)", color_space, color_space), &format!("{}(25% 20 50)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a b / alpha)", color_space, color_space), &format!("{}(25% 20 50 / 0.4)", color_space), ); test( &format!( "{}(from {}(200% 300 400 / 500%) l a b / alpha)", color_space, color_space ), &format!("{}(200% 300 400)", color_space), ); test( &format!( "{}(from {}(-200% -300 -400 / -500%) l a b / alpha)", color_space, color_space ), &format!("{}(0% -300 -400 / 0)", color_space), ); // Test nesting relative colors. test( &format!( "{}(from {}(from {}(25% 20 50) l a b) l a b)", color_space, color_space, color_space ), &format!("{}(25% 20 50)", color_space), ); // Testing non-${colorSpace} origin to see conversion. test( &format!("{}(from color(display-p3 0 0 0) l a b / alpha)", color_space), &format!("{}(0% 0 0)", color_space), ); // Testing replacement with 0. test( &format!("{}(from {}(25% 20 50) 0% 0 0)", color_space, color_space), &format!("{}(0% 0 0)", color_space), ); test( &format!("{}(from {}(25% 20 50) 0% 0 0 / 0)", color_space, color_space), &format!("{}(0% 0 0 / 0)", color_space), ); test( &format!("{}(from {}(25% 20 50) 0% a b / alpha)", color_space, color_space), &format!("{}(0% 20 50)", color_space), ); test( &format!("{}(from {}(25% 20 50) l 0 b / alpha)", color_space, color_space), &format!("{}(25% 0 50)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a 0 / alpha)", color_space, color_space), &format!("{}(25% 20 0)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a b / 0)", color_space, color_space), &format!("{}(25% 20 50 / 0)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) 0% a b / alpha)", color_space, color_space), &format!("{}(0% 20 50 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l 0 b / alpha)", color_space, color_space), &format!("{}(25% 0 50 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a 0 / alpha)", color_space, color_space), &format!("{}(25% 20 0 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a b / 0)", color_space, color_space), &format!("{}(25% 20 50 / 0)", color_space), ); // Testing replacement with a constant. test( &format!("{}(from {}(25% 20 50) 35% a b / alpha)", color_space, color_space), &format!("{}(35% 20 50)", color_space), ); test( &format!("{}(from {}(25% 20 50) l 35 b / alpha)", color_space, color_space), &format!("{}(25% 35 50)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a 35 / alpha)", color_space, color_space), &format!("{}(25% 20 35)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a b / .35)", color_space, color_space), &format!("{}(25% 20 50 / 0.35)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) 35% a b / alpha)", color_space, color_space), &format!("{}(35% 20 50 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l 35 b / alpha)", color_space, color_space), &format!("{}(25% 35 50 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a 35 / alpha)", color_space, color_space), &format!("{}(25% 20 35 / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a b / .35)", color_space, color_space), &format!("{}(25% 20 50 / 0.35)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) 200% 300 400 / 500)", color_space, color_space ), &format!("{}(200% 300 400)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) -200% -300 -400 / -500)", color_space, color_space ), &format!("{}(0% -300 -400 / 0)", color_space), ); // Testing valid permutation (types match). test( &format!("{}(from {}(25% 20 50) l b a)", color_space, color_space), &format!("{}(25% 50 20)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a a / a)", color_space, color_space), &format!("{}(25% 20 20)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l b a)", color_space, color_space), &format!("{}(25% 50 20)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a a / a)", color_space, color_space), &format!("{}(25% 20 20)", color_space), ); // Testing with calc(). test( &format!( "{}(from {}(25% 20 50) calc(l) calc(a) calc(b))", color_space, color_space ), &format!("{}(25% 20 50)", color_space), ); test( &format!( "{}(from {}(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))", color_space, color_space ), &format!("{}(25% 20 50 / 0.4)", color_space), ); // Testing with 'none'. test( &format!("{}(from {}(25% 20 50) none none none)", color_space, color_space), &format!("{}(none none none)", color_space), ); test( &format!("{}(from {}(25% 20 50) none none none / none)", color_space, color_space), &format!("{}(none none none / none)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a none)", color_space, color_space), &format!("{}(25% 20 none)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a none / alpha)", color_space, color_space), &format!("{}(25% 20 none)", color_space), ); test( &format!("{}(from {}(25% 20 50) l a b / none)", color_space, color_space), &format!("{}(25% 20 50 / none)", color_space), ); test( &format!( "{}(from {}(25% 20 50 / 40%) l a none / alpha)", color_space, color_space ), &format!("{}(25% 20 none / 0.4)", color_space), ); test( &format!("{}(from {}(25% 20 50 / 40%) l a b / none)", color_space, color_space), &format!("{}(25% 20 50 / none)", color_space), ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test( &format!("{}(from {}(none none none) l a b)", color_space, color_space), &format!("{}(0% 0 0)", color_space), ); test( &format!( "{}(from {}(none none none / none) l a b / alpha)", color_space, color_space ), &format!("{}(0% 0 0 / 0)", color_space), ); test( &format!("{}(from {}(25% none 50) l a b)", color_space, color_space), &format!("{}(25% 0 50)", color_space), ); test( &format!("{}(from {}(25% 20 50 / none) l a b / alpha)", color_space, color_space), &format!("{}(25% 20 50 / 0)", color_space), ); } // test_valid_value\(`color`, `\$\{colorSpace\}\(from \$\{colorSpace\}\((.*?)`,\s*`\$\{colorSpace\}(.*?)`\) // test(&format!("{}(from {}($1", color_space, color_space), &format!("{}$2", color_space)) for color_space in &["lch", "oklch"] { // Testing no modifications. test( &format!("{}(from {}(70% 45 30) l c h)", color_space, color_space), &format!("{}(70% 45 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c h / alpha)", color_space, color_space), &format!("{}(70% 45 30)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c h / alpha)", color_space, color_space), &format!("{}(70% 45 30 / 0.4)", color_space), ); test( &format!( "{}(from {}(200% 300 400 / 500%) l c h / alpha)", color_space, color_space ), &format!("{}(200% 300 40)", color_space), ); test( &format!( "{}(from {}(-200% -300 -400 / -500%) l c h / alpha)", color_space, color_space ), &format!("{}(0% 0 320 / 0)", color_space), ); // Test nesting relative colors. test( &format!( "{}(from {}(from {}(70% 45 30) l c h) l c h)", color_space, color_space, color_space ), &format!("{}(70% 45 30)", color_space), ); // Testing non-sRGB origin colors (no gamut mapping will happen since the destination is not a bounded RGB color space). test( &format!("{}(from color(display-p3 0 0 0) l c h / alpha)", color_space), &format!("{}(0% 0 0)", color_space), ); // Testing replacement with 0. test( &format!("{}(from {}(70% 45 30) 0% 0 0)", color_space, color_space), &format!("{}(0% 0 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) 0% 0 0deg)", color_space, color_space), &format!("{}(0% 0 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) 0% 0 0 / 0)", color_space, color_space), &format!("{}(0% 0 0 / 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) 0% 0 0deg / 0)", color_space, color_space), &format!("{}(0% 0 0 / 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) 0% c h / alpha)", color_space, color_space), &format!("{}(0% 45 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) l 0 h / alpha)", color_space, color_space), &format!("{}(70% 0 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c 0 / alpha)", color_space, color_space), &format!("{}(70% 45 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c 0deg / alpha)", color_space, color_space), &format!("{}(70% 45 0)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c h / 0)", color_space, color_space), &format!("{}(70% 45 30 / 0)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) 0% c h / alpha)", color_space, color_space), &format!("{}(0% 45 30 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l 0 h / alpha)", color_space, color_space), &format!("{}(70% 0 30 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c 0 / alpha)", color_space, color_space), &format!("{}(70% 45 0 / 0.4)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) l c 0deg / alpha)", color_space, color_space ), &format!("{}(70% 45 0 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c h / 0)", color_space, color_space), &format!("{}(70% 45 30 / 0)", color_space), ); // Testing replacement with a constant. test( &format!("{}(from {}(70% 45 30) 25% c h / alpha)", color_space, color_space), &format!("{}(25% 45 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) l 25 h / alpha)", color_space, color_space), &format!("{}(70% 25 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c 25 / alpha)", color_space, color_space), &format!("{}(70% 45 25)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c 25deg / alpha)", color_space, color_space), &format!("{}(70% 45 25)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c h / .25)", color_space, color_space), &format!("{}(70% 45 30 / 0.25)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) 25% c h / alpha)", color_space, color_space), &format!("{}(25% 45 30 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l 25 h / alpha)", color_space, color_space), &format!("{}(70% 25 30 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c 25 / alpha)", color_space, color_space), &format!("{}(70% 45 25 / 0.4)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) l c 25deg / alpha)", color_space, color_space ), &format!("{}(70% 45 25 / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c h / .25)", color_space, color_space), &format!("{}(70% 45 30 / 0.25)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) 200% 300 400 / 500)", color_space, color_space ), &format!("{}(200% 300 400)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) -200% -300 -400 / -500)", color_space, color_space ), &format!("{}(0% 0 -400 / 0)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) 50% 120 400deg / 500)", color_space, color_space ), &format!("{}(50% 120 400)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) 50% 120 -400deg / -500)", color_space, color_space ), &format!("{}(50% 120 -400 / 0)", color_space), ); // Testing valid permutation (types match). // NOTE: 'c' is a vaild hue, as hue is |. test( &format!("{}(from {}(70% 45 30) alpha c h / l)", color_space, color_space), &format!("{}(100% 45 30 / 0.7)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c c / alpha)", color_space, color_space), &format!("{}(70% 45 45)", color_space), ); test( &format!("{}(from {}(70% 45 30) alpha c h / alpha)", color_space, color_space), &format!("{}(100% 45 30)", color_space), ); test( &format!("{}(from {}(70% 45 30) alpha c c / alpha)", color_space, color_space), &format!("{}(100% 45 45)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) alpha c h / l)", color_space, color_space), &format!("{}(40% 45 30 / 0.7)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c c / alpha)", color_space, color_space), &format!("{}(70% 45 45 / 0.4)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) alpha c h / alpha)", color_space, color_space ), &format!("{}(40% 45 30 / 0.4)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) alpha c c / alpha)", color_space, color_space ), &format!("{}(40% 45 45 / 0.4)", color_space), ); // Testing with calc(). test( &format!( "{}(from {}(70% 45 30) calc(l) calc(c) calc(h))", color_space, color_space ), &format!("{}(70% 45 30)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))", color_space, color_space ), &format!("{}(70% 45 30 / 0.4)", color_space), ); // Testing with 'none'. test( &format!("{}(from {}(70% 45 30) none none none)", color_space, color_space), &format!("{}(none none none)", color_space), ); test( &format!("{}(from {}(70% 45 30) none none none / none)", color_space, color_space), &format!("{}(none none none / none)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c none)", color_space, color_space), &format!("{}(70% 45 none)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c none / alpha)", color_space, color_space), &format!("{}(70% 45 none)", color_space), ); test( &format!("{}(from {}(70% 45 30) l c h / none)", color_space, color_space), &format!("{}(70% 45 30 / none)", color_space), ); test( &format!( "{}(from {}(70% 45 30 / 40%) l c none / alpha)", color_space, color_space ), &format!("{}(70% 45 none / 0.4)", color_space), ); test( &format!("{}(from {}(70% 45 30 / 40%) l c h / none)", color_space, color_space), &format!("{}(70% 45 30 / none)", color_space), ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test( &format!("{}(from {}(none none none) l c h)", color_space, color_space), &format!("{}(0% 0 0)", color_space), ); test( &format!( "{}(from {}(none none none / none) l c h / alpha)", color_space, color_space ), &format!("{}(0% 0 0 / 0)", color_space), ); test( &format!("{}(from {}(70% none 30) l c h)", color_space, color_space), &format!("{}(70% 0 30)", color_space), ); test( &format!("{}(from {}(70% 45 30 / none) l c h / alpha)", color_space, color_space), &format!("{}(70% 45 30 / 0)", color_space), ); } // test_valid_value\(`color`, `color\(from color\(\$\{colorSpace\}(.*?) \$\{colorSpace\}(.*?)`,\s*`color\(\$\{colorSpace\}(.*?)`\) // test(&format!("color(from color({}$1 {}$2", color_space, color_space), &format!("color({}$3", color_space)) for color_space in &["srgb", "srgb-linear", "a98-rgb", "rec2020", "prophoto-rgb"] { // Testing no modifications. test( &format!("color(from color({} 0.7 0.5 0.3) {} r g b)", color_space, color_space), &format!("color({} 0.7 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.4)", color_space), ); // Test nesting relative colors. test( &format!( "color(from color(from color({} 0.7 0.5 0.3) {} r g b) {} r g b)", color_space, color_space, color_space ), &format!("color({} 0.7 0.5 0.3)", color_space), ); // Testing replacement with 0. test( &format!("color(from color({} 0.7 0.5 0.3) {} 0 0 0)", color_space, color_space), &format!("color({} 0 0 0)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 0 0 0 / 0)", color_space, color_space ), &format!("color({} 0 0 0 / 0)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 0 g b / alpha)", color_space, color_space ), &format!("color({} 0 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r 0 b / alpha)", color_space, color_space ), &format!("color({} 0.7 0 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g 0 / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g b / 0)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} 0 g b / alpha)", color_space, color_space ), &format!("color({} 0 0.5 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r 0 b / alpha)", color_space, color_space ), &format!("color({} 0.7 0 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g 0 / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b / 0)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0)", color_space), ); // Testing replacement with a constant. test( &format!( "color(from color({} 0.7 0.5 0.3) {} 0.2 g b / alpha)", color_space, color_space ), &format!("color({} 0.2 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 20% g b / alpha)", color_space, color_space ), &format!("color({} 0.2 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r 0.2 b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.2 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r 20% b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.2 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g 0.2 / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.2)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g 20% / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.2)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g b / 0.2)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.2)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g b / 20%)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.2)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} 0.2 g b / alpha)", color_space, color_space ), &format!("color({} 0.2 0.5 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} 20% g b / alpha)", color_space, color_space ), &format!("color({} 0.2 0.5 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r 0.2 b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.2 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r 20% b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.2 0.3 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g 0.2 / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.2 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g 20% / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.2 / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b / 0.2)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.2)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b / 20%)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.2)", color_space), ); test( &format!("color(from color({} 0.7 0.5 0.3) {} 2 3 4)", color_space, color_space), &format!("color({} 2 3 4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 2 3 4 / 5)", color_space, color_space ), &format!("color({} 2 3 4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} -2 -3 -4)", color_space, color_space ), &format!("color({} -2 -3 -4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} -2 -3 -4 / -5)", color_space, color_space ), &format!("color({} -2 -3 -4 / 0)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 200% 300% 400%)", color_space, color_space ), &format!("color({} 2 3 4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} 200% 300% 400% / 500%)", color_space, color_space ), &format!("color({} 2 3 4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} -200% -300% -400%)", color_space, color_space ), &format!("color({} -2 -3 -4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} -200% -300% -400% / -500%)", color_space, color_space ), &format!("color({} -2 -3 -4 / 0)", color_space), ); // Testing valid permutation (types match). test( &format!("color(from color({} 0.7 0.5 0.3) {} g b r)", color_space, color_space), &format!("color({} 0.5 0.3 0.7)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} b alpha r / g)", color_space, color_space ), &format!("color({} 0.3 1 0.7 / 0.5)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r r r / r)", color_space, color_space ), &format!("color({} 0.7 0.7 0.7 / 0.7)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} alpha alpha alpha / alpha)", color_space, color_space ), &format!("color({} 1 1 1)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} g b r)", color_space, color_space ), &format!("color({} 0.5 0.3 0.7)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} b alpha r / g)", color_space, color_space ), &format!("color({} 0.3 0.4 0.7 / 0.5)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r r r / r)", color_space, color_space ), &format!("color({} 0.7 0.7 0.7 / 0.7)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} alpha alpha alpha / alpha)", color_space, color_space ), &format!("color({} 0.4 0.4 0.4 / 0.4)", color_space), ); // Testing out of gamut components. test( &format!("color(from color({} 1.7 1.5 1.3) {} r g b)", color_space, color_space), &format!("color({} 1.7 1.5 1.3)", color_space), ); test( &format!( "color(from color({} 1.7 1.5 1.3) {} r g b / alpha)", color_space, color_space ), &format!("color({} 1.7 1.5 1.3)", color_space), ); test( &format!( "color(from color({} 1.7 1.5 1.3 / 140%) {} r g b)", color_space, color_space ), &format!("color({} 1.7 1.5 1.3)", color_space), ); test( &format!( "color(from color({} 1.7 1.5 1.3 / 140%) {} r g b / alpha)", color_space, color_space ), &format!("color({} 1.7 1.5 1.3)", color_space), ); test( &format!( "color(from color({} -0.7 -0.5 -0.3) {} r g b)", color_space, color_space ), &format!("color({} -0.7 -0.5 -0.3)", color_space), ); test( &format!( "color(from color({} -0.7 -0.5 -0.3) {} r g b / alpha)", color_space, color_space ), &format!("color({} -0.7 -0.5 -0.3)", color_space), ); test( &format!( "color(from color({} -0.7 -0.5 -0.3 / -40%) {} r g b)", color_space, color_space ), &format!("color({} -0.7 -0.5 -0.3)", color_space), ); test( &format!( "color(from color({} -0.7 -0.5 -0.3 / -40%) {} r g b / alpha)", color_space, color_space ), &format!("color({} -0.7 -0.5 -0.3 / 0)", color_space), ); // Testing with calc(). test( &format!( "color(from color({} 0.7 0.5 0.3) {} calc(r) calc(g) calc(b))", color_space, color_space ), &format!("color({} 0.7 0.5 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} calc(r) calc(g) calc(b) / calc(alpha))", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0.4)", color_space), ); // Testing with 'none'. test( &format!( "color(from color({} 0.7 0.5 0.3) {} none none none)", color_space, color_space ), &format!("color({} none none none)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} none none none / none)", color_space, color_space ), &format!("color({} none none none / none)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g none)", color_space, color_space ), &format!("color({} 0.7 0.5 none)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g none / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 none)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3) {} r g b / none)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / none)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g none / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 none / 0.4)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / 40%) {} r g b / none)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / none)", color_space), ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test( &format!( "color(from color({} none none none) {} r g b)", color_space, color_space ), &format!("color({} 0 0 0)", color_space), ); test( &format!( "color(from color({} none none none / none) {} r g b / alpha)", color_space, color_space ), &format!("color({} 0 0 0 / 0)", color_space), ); test( &format!("color(from color({} 0.7 none 0.3) {} r g b)", color_space, color_space), &format!("color({} 0.7 0 0.3)", color_space), ); test( &format!( "color(from color({} 0.7 0.5 0.3 / none) {} r g b / alpha)", color_space, color_space ), &format!("color({} 0.7 0.5 0.3 / 0)", color_space), ); } // test_valid_value\(`color`, `color\(from color\(\$\{colorSpace\}(.*?) \$\{colorSpace\}(.*?)`,\s*`color\(\$\{resultColorSpace\}(.*?)`\) // test(&format!("color(from color({}$1 {}$2", color_space, color_space), &format!("color({}$3", result_color_space)) for color_space in &["xyz", "xyz-d50", "xyz-d65"] { let result_color_space = if *color_space == "xyz" { "xyz-d65" } else { color_space }; // Testing no modifications. test( &format!("color(from color({} 7 -20.5 100) {} x y z)", color_space, color_space), &format!("color({} 7 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y z / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y z)", color_space, color_space ), &format!("color({} 7 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y z / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0.4)", result_color_space), ); // Test nesting relative colors. test( &format!( "color(from color(from color({} 7 -20.5 100) {} x y z) {} x y z)", color_space, color_space, color_space ), &format!("color({} 7 -20.5 100)", result_color_space), ); // Testing replacement with 0. test( &format!("color(from color({} 7 -20.5 100) {} 0 0 0)", color_space, color_space), &format!("color({} 0 0 0)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} 0 0 0 / 0)", color_space, color_space ), &format!("color({} 0 0 0 / 0)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} 0 y z / alpha)", color_space, color_space ), &format!("color({} 0 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x 0 z / alpha)", color_space, color_space ), &format!("color({} 7 0 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y 0 / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 0)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y z / 0)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} 0 y z / alpha)", color_space, color_space ), &format!("color({} 0 -20.5 100 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x 0 z / alpha)", color_space, color_space ), &format!("color({} 7 0 100 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y 0 / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 0 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y z / 0)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0)", result_color_space), ); // Testing replacement with a constant. test( &format!( "color(from color({} 7 -20.5 100) {} 0.2 y z / alpha)", color_space, color_space ), &format!("color({} 0.2 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x 0.2 z / alpha)", color_space, color_space ), &format!("color({} 7 0.2 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y 0.2 / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 0.2)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y z / 0.2)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0.2)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y z / 20%)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0.2)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} 0.2 y z / alpha)", color_space, color_space ), &format!("color({} 0.2 -20.5 100 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x 0.2 z / alpha)", color_space, color_space ), &format!("color({} 7 0.2 100 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y 0.2 / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 0.2 / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y z / 0.2)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0.2)", result_color_space), ); // Testing valid permutation (types match). test( &format!("color(from color({} 7 -20.5 100) {} y z x)", color_space, color_space), &format!("color({} -20.5 100 7)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x x x / x)", color_space, color_space ), &format!("color({} 7 7 7)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} y z x)", color_space, color_space ), &format!("color({} -20.5 100 7)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x x x / x)", color_space, color_space ), &format!("color({} 7 7 7)", result_color_space), ); // Testing with calc(). test( &format!( "color(from color({} 7 -20.5 100) {} calc(x) calc(y) calc(z))", color_space, color_space ), &format!("color({} 7 -20.5 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} calc(x) calc(y) calc(z) / calc(alpha))", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0.4)", result_color_space), ); // Testing with 'none'. test( &format!( "color(from color({} 7 -20.5 100) {} none none none)", color_space, color_space ), &format!("color({} none none none)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} none none none / none)", color_space, color_space ), &format!("color({} none none none / none)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y none)", color_space, color_space ), &format!("color({} 7 -20.5 none)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y none / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 none)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100) {} x y z / none)", color_space, color_space ), &format!("color({} 7 -20.5 100 / none)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y none / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 none / 0.4)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / 40%) {} x y z / none)", color_space, color_space ), &format!("color({} 7 -20.5 100 / none)", result_color_space), ); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. test( &format!( "color(from color({} none none none) {} x y z)", color_space, color_space ), &format!("color({} 0 0 0)", result_color_space), ); test( &format!( "color(from color({} none none none / none) {} x y z / alpha)", color_space, color_space ), &format!("color({} 0 0 0 / 0)", result_color_space), ); test( &format!("color(from color({} 7 none 100) {} x y z)", color_space, color_space), &format!("color({} 7 0 100)", result_color_space), ); test( &format!( "color(from color({} 7 -20.5 100 / none) {} x y z / alpha)", color_space, color_space ), &format!("color({} 7 -20.5 100 / 0)", result_color_space), ); // https://github.com/web-platform-tests/wpt/blob/master/css/css-color/parsing/relative-color-invalid.html minify_test( ".foo{color:rgb(from rebeccapurple r 10deg 10)}", ".foo{color:rgb(from rebeccapurple r 10deg 10)}", ); minify_test( ".foo{color:rgb(from rebeccapurple l g b)}", ".foo{color:rgb(from rebeccapurple l g b)}", ); minify_test( ".foo{color:hsl(from rebeccapurple s h l)}", ".foo{color:hsl(from rebeccapurple s h l)}", ); minify_test( ".foo{color:hsl(from rebeccapurple s s s / s)}", ".foo{color:hsl(from rebeccapurple s s s/s)}", ); minify_test( ".foo{color:hsl(from rebeccapurple alpha alpha alpha / alpha)}", ".foo{color:hsl(from rebeccapurple alpha alpha alpha/alpha)}", ); } } #[test] fn test_color_mix() { minify_test( ".foo { color: color-mix(in lab, purple 50%, plum 50%); }", ".foo{color:lab(51.5117% 43.3777 -29.0443)}", ); minify_test( ".foo { color: color-mix(in lch, peru 40%, palegoldenrod); }", ".foo{color:lch(79.7255% 40.4542 84.7634)}", ); minify_test( ".foo { color: color-mix(in lch, teal 65%, olive); }", ".foo{color:lch(49.4431% 40.4806 162.546)}", ); minify_test( ".foo { color: color-mix(in lch, white, black); }", ".foo{color:lch(50% 0 none)}", ); minify_test( ".foo { color: color-mix(in xyz, rgb(82.02% 30.21% 35.02%) 75.23%, rgb(5.64% 55.94% 85.31%)); }", ".foo{color:color(xyz .287458 .208776 .260566)}", ); minify_test( ".foo { color: color-mix(in lch, white, blue); }", ".foo{color:lch(64.7842% 65.6007 301.364)}", ); minify_test( ".foo { color: color-mix(in oklch, white, blue); }", ".foo{color:oklch(72.6007% .156607 264.052)}", ); minify_test( ".foo { color: color-mix(in srgb, white, blue); }", ".foo{color:#8080ff}", ); minify_test( ".foo { color: color-mix(in lch, blue, white); }", ".foo{color:lch(64.7842% 65.6007 301.364)}", ); minify_test( ".foo { color: color-mix(in oklch, blue, white); }", ".foo{color:oklch(72.6007% .156607 264.052)}", ); minify_test( ".foo { color: color-mix(in srgb, blue, white); }", ".foo{color:#8080ff}", ); // minify_test(".foo { color: color-mix(in hsl, color(display-p3 0 1 0) 80%, yellow); }", ".foo{color:hsl(108 100% 49.9184%) }"); minify_test( ".foo { color: color-mix(in hsl, hsl(120 100% 49.898%) 80%, yellow); }", ".foo{color:#33fe00}", ); minify_test( ".foo { color: color-mix(in srgb, rgb(100% 0% 0% / 0.7) 25%, rgb(0% 100% 0% / 0.2)); }", ".foo{color:#89760053}", ); minify_test( ".foo { color: color-mix(in srgb, rgb(100% 0% 0% / 0.7) 20%, rgb(0% 100% 0% / 0.2) 60%); }", ".foo{color:#89760042}", ); minify_test( ".foo { color: color-mix(in lch, color(display-p3 0 1 none), color(display-p3 0 0 1)); }", ".foo{color:lch(58.8143% 141.732 218.684)}", ); minify_test( ".foo { color: color-mix(in srgb, rgb(128 128 none), rgb(none none 128)); }", ".foo{color:gray}", ); minify_test( ".foo { color: color-mix(in srgb, rgb(50% 50% none), rgb(none none 50%)); }", ".foo{color:gray}", ); minify_test( ".foo { color: color-mix(in srgb, rgb(none 50% none), rgb(50% none 50%)); }", ".foo{color:gray}", ); minify_test( ".foo { --color: color-mix(in lch, teal 65%, olive); }", ".foo{--color:lch(49.4431% 40.4806 162.546)}", ); minify_test( ".foo { color: color-mix(in xyz, transparent, green 65%); }", ".foo{color:color(xyz .0771883 .154377 .0257295/.65)}", ); prefix_test( ".foo { color: color-mix(in xyz, transparent, green 65%); }", indoc! { r#" .foo { color: #008000a6; color: color(xyz .0771883 .154377 .0257295 / .65); } "# }, Browsers { chrome: Some(95 << 16), ..Default::default() }, ); // regex for converting web platform tests: // test_computed_value\(.*?, `(.*?)`, `(.*?)`\); // minify_test(".foo { color: $1 }", ".foo{color:$2}"); // https://github.com/web-platform-tests/wpt/blob/f8c76b11cff66a7adc87264a18e39353cb5a60c9/css/css-color/parsing/color-mix-computed.html minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%)) }", ".foo{color:#545c3d}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%)) }", ".foo{color:#706a43}", ); minify_test( ".foo { color: color-mix(in hsl, 25% hsl(120deg 10% 20%), hsl(30deg 30% 40%)) }", ".foo{color:#706a43}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%), 25% hsl(30deg 30% 40%)) }", ".foo{color:#3d4936}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%) 25%) }", ".foo{color:#3d4936}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%) 75%) }", ".foo{color:#706a43}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 30%, hsl(30deg 30% 40%) 90%) }", ".foo{color:#706a43}", ); // Scale down > 100% sum. minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%) }", ".foo{color:#706a4380}", ); // Scale up < 100% sum, causes alpha multiplication. minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%)) }", ".foo{color:#856647}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8)) }", ".foo{color:#5f694199}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40% / .8)) }", ".foo{color:#6c6742d9}", ); minify_test( ".foo { color: color-mix(in hsl, 25% hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8)) }", ".foo{color:#797245b3}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4), 25% hsl(30deg 30% 40% / .8)) }", ".foo{color:#44543b80}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8) 25%) }", ".foo{color:#44543b80}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4) 25%, hsl(30deg 30% 40% / .8) 75%) }", ".foo{color:#797245b3}", ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4) 30%, hsl(30deg 30% 40% / .8) 90%) }", ".foo{color:#797245b3}", ); // Scale down > 100% sum. minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4) 12.5%, hsl(30deg 30% 40% / .8) 37.5%) }", ".foo{color:#79724559}", ); // Scale up < 100% sum, causes alpha multiplication. minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 10% 20% / .4) 0%, hsl(30deg 30% 40% / .8)) }", ".foo{color:#856647cc}", ); fn canonicalize(s: &str) -> String { use crate::traits::{Parse, ToCss}; use crate::values::color::CssColor; use cssparser::{Parser, ParserInput}; let mut input = ParserInput::new(s); let mut parser = Parser::new(&mut input); let v = CssColor::parse(&mut parser).unwrap().to_rgb(); format!(".foo{{color:{}}}", v.to_css_string(PrinterOptions::default()).unwrap()) } // regex for converting web platform tests: // test_computed_value\(.*?, `(.*?)`, canonicalize\(`(.*?)`\)\); // minify_test(".foo { color: $1 }", &canonicalize("$2")); minify_test( ".foo { color: color-mix(in hsl, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl shorter hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl longer hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(230deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl longer hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(230deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl longer hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl longer hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); // minify_test(".foo { color: color-mix(in hsl longer hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); // minify_test(".foo { color: color-mix(in hsl longer hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); minify_test( ".foo { color: color-mix(in hsl increasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl increasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(230deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl increasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl increasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); // minify_test(".foo { color: color-mix(in hsl increasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); // minify_test(".foo { color: color-mix(in hsl increasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)")); minify_test( ".foo { color: color-mix(in hsl decreasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(230deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl decreasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl decreasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(10deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl decreasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl decreasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(350deg 50% 50%)"), ); // minify_test(".foo { color: color-mix(in hsl decreasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); minify_test( ".foo { color: color-mix(in hsl specified hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl specified hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%)) }", &canonicalize("hsl(50deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl specified hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); minify_test( ".foo { color: color-mix(in hsl specified hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%)) }", &canonicalize("hsl(190deg 50% 50%)"), ); // minify_test(".foo { color: color-mix(in hsl specified hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); // minify_test(".foo { color: color-mix(in hsl specified hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%)) }", &canonicalize("hsl(170deg 50% 50%)")); minify_test( ".foo { color: color-mix(in hsl, hsl(none none none), hsl(none none none)) }", &canonicalize("hsl(none none none)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(none none none), hsl(30deg 40% 80%)) }", &canonicalize("hsl(30deg 40% 80%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 20% 40%), hsl(none none none)) }", &canonicalize("hsl(120deg 20% 40%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 20% none), hsl(30deg 40% 60%)) }", &canonicalize("hsl(75deg 30% 60%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(120deg 20% 40%), hsl(30deg 20% none)) }", &canonicalize("hsl(75deg 20% 40%)"), ); minify_test( ".foo { color: color-mix(in hsl, hsl(none 20% 40%), hsl(30deg none 80%)) }", &canonicalize("hsl(30deg 20% 60%)"), ); minify_test( ".foo { color: color-mix(in hsl, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 249, 66)"), ); // Naive clip based mapping would give rgb(0, 255, 0). minify_test( ".foo { color: color-mix(in hsl, lab(100% 104.3 -50.9) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 150, 255). minify_test( ".foo { color: color-mix(in hsl, lab(0% 104.3 -50.9) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(42, 0, 34)"), ); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, minify_test( ".foo { color: color-mix(in hsl, lch(100% 116 334) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 150, 255). minify_test( ".foo { color: color-mix(in hsl, lch(0% 116 334) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(42, 0, 34)"), ); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, minify_test( ".foo { color: color-mix(in hsl, oklab(100% 0.365 -0.16) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 92, 255). minify_test( ".foo { color: color-mix(in hsl, oklab(0% 0.365 -0.16) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 0, 0)"), ); // Naive clip based mapping would give rgb(19, 0, 24). minify_test( ".foo { color: color-mix(in hsl, oklch(100% 0.399 336.3) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 91, 255). minify_test( ".foo { color: color-mix(in hsl, oklch(0% 0.399 336.3) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 0, 0)"), ); // Naive clip based mapping would give rgb(20, 0, 24). minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%)) }", &canonicalize("rgb(147, 179, 52)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%)) }", &canonicalize("rgb(166, 153, 64)"), ); minify_test( ".foo { color: color-mix(in hwb, 25% hwb(120deg 10% 20%), hwb(30deg 30% 40%)) }", &canonicalize("rgb(166, 153, 64)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40%)) }", &canonicalize("rgb(96, 191, 39)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%) 25%) }", &canonicalize("rgb(96, 191, 39)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%) 75%) }", &canonicalize("rgb(166, 153, 64)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%) 30%, hwb(30deg 30% 40%) 90%) }", &canonicalize("rgb(166, 153, 64)"), ); // Scale down > 100% sum. minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%) }", &canonicalize("rgba(166, 153, 64, 0.5)"), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%)) }", &canonicalize("rgb(153, 115, 77)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8)) }", &canonicalize("rgba(143, 170, 60, 0.6)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4) 25%, hwb(30deg 30% 40% / .8)) }", &canonicalize("rgba(160, 149, 70, 0.7)"), ); minify_test( ".foo { color: color-mix(in hwb, 25% hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8)) }", &canonicalize("rgba(160, 149, 70, 0.7)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40% / .8)) }", &canonicalize("rgba(95, 193, 37, 0.95)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8) 25%) }", &canonicalize("rgba(98, 184, 46, 0.5)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4) 25%, hwb(30deg 30% 40% / .8) 75%) }", &canonicalize("rgba(160, 149, 70, 0.7)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4) 30%, hwb(30deg 30% 40% / .8) 90%) }", &canonicalize("rgba(160, 149, 70, 0.7)"), ); // Scale down > 100% sum. minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4) 12.5%, hwb(30deg 30% 40% / .8) 37.5%) }", &canonicalize("rgba(160, 149, 70, 0.35)"), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( ".foo { color: color-mix(in hwb, hwb(120deg 10% 20% / .4) 0%, hwb(30deg 30% 40% / .8)) }", &canonicalize("rgba(153, 115, 77, 0.8)"), ); // minify_test(".foo { color: color-mix(in hwb, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb shorter hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb shorter hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb shorter hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb shorter hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb shorter hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb shorter hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb longer hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(230deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb longer hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(230deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb longer hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb longer hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb longer hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb longer hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb increasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb increasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(230deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb increasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb increasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb increasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb increasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); minify_test( ".foo { color: color-mix(in hwb decreasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(230deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb decreasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb decreasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(10deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb decreasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb decreasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(350deg 30% 40%)"), ); // minify_test(".foo { color: color-mix(in hwb decreasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%)) }", &canonicalize("hwb(50deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%)) }", &canonicalize("hwb(190deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); // minify_test(".foo { color: color-mix(in hwb specified hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%)) }", &canonicalize("hwb(170deg 30% 40%)")); minify_test( ".foo { color: color-mix(in hwb, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 249, 66)"), ); // Naive clip based mapping would give rgb(0, 255, 0). minify_test( ".foo { color: color-mix(in hwb, lab(100% 104.3 -50.9) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 150, 255). minify_test( ".foo { color: color-mix(in hwb, lab(0% 104.3 -50.9) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(42, 0, 34)"), ); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, minify_test( ".foo { color: color-mix(in hwb, lch(100% 116 334) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 150, 255). minify_test( ".foo { color: color-mix(in hwb, lch(0% 116 334) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(42, 0, 34)"), ); // Naive clip based mapping would give rgb(90, 0, 76). NOTE: 0% lightness in Lab/LCH does not automatically correspond with sRGB black, minify_test( ".foo { color: color-mix(in hwb, oklab(100% 0.365 -0.16) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 92, 255). minify_test( ".foo { color: color-mix(in hwb, oklab(0% 0.365 -0.16) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 0, 0)"), ); // Naive clip based mapping would give rgb(19, 0, 24). minify_test( ".foo { color: color-mix(in hwb, oklch(100% 0.399 336.3) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(255, 255, 255)"), ); // Naive clip based mapping would give rgb(255, 91, 255). minify_test( ".foo { color: color-mix(in hwb, oklch(0% 0.399 336.3) 100%, rgb(0, 0, 0) 0%) }", &canonicalize("rgb(0, 0, 0)"), ); // Naive clip based mapping would give rgb(20, 0, 24). for color_space in &["lch", "oklch"] { // regex for converting web platform tests: // test_computed_value\(.*?, `color-mix\(in \$\{colorSpace\}(.*?), (.*?)\$\{colorSpace\}(.*?) \$\{colorSpace\}(.*?)`, `\$\{colorSpace\}(.*?)`\); // minify_test(&format!(".foo {{ color: color-mix(in {0}$1, $2{0}$3 {0}$4 }}", color_space), &format!(".foo{{color:{}$5}}", color_space)); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg), {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg) 25%, {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% {0}(10% 20 30deg), {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg), 25% {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(20% 30 40)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg), {0}(50% 60 70deg) 25%) }}", color_space ), &format!(".foo{{color:{}(20% 30 40)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg) 25%, {0}(50% 60 70deg) 75%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg) 30%, {0}(50% 60 70deg) 90%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg) 12.5%, {0}(50% 60 70deg) 37.5%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60/.5)}}", color_space), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg) 0%, {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4), {0}(50% 60 70deg / .8)) }}", color_space ), &format!(".foo{{color:{}(36.6667% 46.6667 50/.6)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4) 25%, {0}(50% 60 70deg / .8)) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 60/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% {0}(10% 20 30deg / .4), {0}(50% 60 70deg / .8)) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 60/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4), 25% {0}(50% 60 70deg / .8)) }}", color_space ), &format!(".foo{{color:{}(26% 36 40/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4), {0}(50% 60 70deg / .8) 25%) }}", color_space ), &format!(".foo{{color:{}(26% 36 40/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4) 25%, {0}(50% 60 70deg / .8) 75%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 60/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4) 30%, {0}(50% 60 70deg / .8) 90%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 60/.7)}}", color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4) 12.5%, {0}(50% 60 70deg / .8) 37.5%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 60/.35)}}", color_space), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / .4) 0%, {0}(50% 60 70deg / .8)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70/.8)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} shorter hue, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 230)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 230)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} longer hue, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 230)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} increasing hue, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 230)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 10)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 350)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} decreasing hue, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 40deg), {0}(100% 0 60deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 60deg), {0}(100% 0 40deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 50deg), {0}(100% 0 330deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 330deg), {0}(100% 0 50deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 190)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 20deg), {0}(100% 0 320deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0} specified hue, {0}(100% 0 320deg), {0}(100% 0 20deg)) }}", color_space ), &format!(".foo{{color:{}(100% 0 170)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none none none), {0}(none none none)) }}", color_space ), &format!(".foo{{color:{}(none none none)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none none none), {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg), {0}(none none none)) }}", color_space ), &format!(".foo{{color:{}(10% 20 30)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 none), {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(30% 40 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg), {0}(50% 60 none)) }}", color_space ), &format!(".foo{{color:{}(30% 40 30)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none 20 30deg), {0}(50% none 70deg)) }}", color_space ), &format!(".foo{{color:{}(50% 20 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / none), {0}(50% 60 70deg)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / none), {0}(50% 60 70deg / 0.5)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30deg / none), {0}(50% 60 70deg / none)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50/none)}}", color_space), ); } for color_space in ["lab", "oklab"] { minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30), {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30) 25%, {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% {0}(10% 20 30), {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30), 25% {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(20% 30 40)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30), {0}(50% 60 70) 25%) }}", color_space ), &format!(".foo{{color:{}(20% 30 40)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30) 25%, {0}(50% 60 70) 75%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30) 30%, {0}(50% 60 70) 90%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60)}}", color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30) 12.5%, {0}(50% 60 70) 37.5%) }}", color_space ), &format!(".foo{{color:{}(40% 50 60/.5)}}", color_space), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30) 0%, {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4), {0}(50% 60 70 / .8)) }}", color_space ), &format!(".foo{{color:{}(36.6667% 46.6667 56.6667/.6)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4) 25%, {0}(50% 60 70 / .8)) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 64.2857/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% {0}(10% 20 30 / .4), {0}(50% 60 70 / .8)) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 64.2857/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4), 25% {0}(50% 60 70 / .8)) }}", color_space ), &format!(".foo{{color:{}(26% 36 46/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4), {0}(50% 60 70 / .8) 25%) }}", color_space ), &format!(".foo{{color:{}(26% 36 46/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4) 25%, {0}(50% 60 70 / .8) 75%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 64.2857/.7)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4) 30%, {0}(50% 60 70 / .8) 90%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 64.2857/.7)}}", color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4) 12.5%, {0}(50% 60 70 / .8) 37.5%) }}", color_space ), &format!(".foo{{color:{}(44.2857% 54.2857 64.2857/.35)}}", color_space), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / .4) 0%, {0}(50% 60 70 / .8)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70/.8)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none none none), {0}(none none none)) }}", color_space ), &format!(".foo{{color:{}(none none none)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none none none), {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(50% 60 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30), {0}(none none none)) }}", color_space ), &format!(".foo{{color:{}(10% 20 30)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 none), {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(30% 40 70)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30), {0}(50% 60 none)) }}", color_space ), &format!(".foo{{color:{}(30% 40 30)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(none 20 30), {0}(50% none 70)) }}", color_space ), &format!(".foo{{color:{}(50% 20 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / none), {0}(50% 60 70)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / none), {0}(50% 60 70 / 0.5)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50/.5)}}", color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, {0}(10% 20 30 / none), {0}(50% 60 70 / none)) }}", color_space ), &format!(".foo{{color:{}(30% 40 50/none)}}", color_space), ); } for color_space in [/*"srgb", */ "srgb-linear", "xyz", "xyz-d50", "xyz-d65"] { // regex for converting web platform tests: // test_computed_value\(.*?, `color-mix\(in \$\{colorSpace\}(.*?), (.*?)color\(\$\{colorSpace\}(.*?) color\(\$\{colorSpace\}(.*?)`, `color\(\$\{resultColorSpace\}(.*?)`\); // minify_test(&format!(".foo {{ color: color-mix(in {0}$1, $2color({0}$3 color({0}$4 }}", color_space), &format!(".foo{{color:color({}$5}}", result_color_space)); let result_color_space = if color_space == "xyz-d65" { "xyz" } else { color_space }; minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3), color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3) 25%, color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .4 .5 .6)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% color({0} .1 .2 .3), color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .4 .5 .6)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3), color({0} .5 .6 .7) 25%) }}", color_space ), &format!(".foo{{color:color({} .2 .3 .4)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3), 25% color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .2 .3 .4)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3) 25%, color({0} .5 .6 .7) 75%) }}", color_space ), &format!(".foo{{color:color({} .4 .5 .6)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3) 30%, color({0} .5 .6 .7) 90%) }}", color_space ), &format!(".foo{{color:color({} .4 .5 .6)}}", result_color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3) 12.5%, color({0} .5 .6 .7) 37.5%) }}", color_space ), &format!(".foo{{color:color({} .4 .5 .6/.5)}}", result_color_space), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3) 0%, color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .5 .6 .7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .5), color({0} .5 .6 .7 / .8)) }}", color_space ), &format!( ".foo{{color:color({} .346154 .446154 .546154/.65)}}", result_color_space ), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4) 25%, color({0} .5 .6 .7 / .8)) }}", color_space ), &format!(".foo{{color:color({} .442857 .542857 .642857/.7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, 25% color({0} .1 .2 .3 / .4), color({0} .5 .6 .7 / .8)) }}", color_space ), &format!(".foo{{color:color({} .442857 .542857 .642857/.7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4), color({0} .5 .6 .7 / .8) 25%) }}", color_space ), &format!(".foo{{color:color({} .26 .36 .46/.5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4), 25% color({0} .5 .6 .7 / .8)) }}", color_space ), &format!(".foo{{color:color({} .26 .36 .46/.5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4) 25%, color({0} .5 .6 .7 / .8) 75%) }}", color_space ), &format!(".foo{{color:color({} .442857 .542857 .642857/.7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4) 30%, color({0} .5 .6 .7 / .8) 90%) }}", color_space ), &format!(".foo{{color:color({} .442857 .542857 .642857/.7)}}", result_color_space), ); // Scale down > 100% sum. minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4) 12.5%, color({0} .5 .6 .7 / .8) 37.5%) }}", color_space ), &format!( ".foo{{color:color({} .442857 .542857 .642857/.35)}}", result_color_space ), ); // Scale up < 100% sum, causes alpha multiplication. minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / .4) 0%, color({0} .5 .6 .7 / .8)) }}", color_space ), &format!(".foo{{color:color({} .5 .6 .7/.8)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} 2 3 4 / 5), color({0} 4 6 8 / 10)) }}", color_space ), &format!(".foo{{color:color({} 3 4.5 6)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} -2 -3 -4), color({0} -4 -6 -8)) }}", color_space ), &format!(".foo{{color:color({} -3 -4.5 -6)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} -2 -3 -4 / -5), color({0} -4 -6 -8 / -10)) }}", color_space ), &format!(".foo{{color:color({}/0)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} none none none), color({0} none none none)) }}", color_space ), &format!(".foo{{color:color({} none none none)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} none none none), color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .5 .6 .7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3), color({0} none none none)) }}", color_space ), &format!(".foo{{color:color({} .1 .2 .3)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 none), color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .7)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3), color({0} .5 .6 none)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .3)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} none .2 .3), color({0} .5 none .7)) }}", color_space ), &format!(".foo{{color:color({} .5 .2 .5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / none), color({0} .5 .6 .7)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / none), color({0} .5 .6 .7 / 0.5)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .5/.5)}}", result_color_space), ); minify_test( &format!( ".foo {{ color: color-mix(in {0}, color({0} .1 .2 .3 / none), color({0} .5 .6 .7 / none)) }}", color_space ), &format!(".foo{{color:color({} .3 .4 .5/none)}}", result_color_space), ); } } #[cfg(feature = "grid")] #[test] fn test_grid() { minify_test( ".foo { grid-template-columns: [first nav-start] 150px [main-start] 1fr [last]; }", ".foo{grid-template-columns:[first nav-start]150px[main-start]1fr[last]}", ); minify_test( ".foo { grid-template-columns: 150px 1fr; }", ".foo{grid-template-columns:150px 1fr}", ); minify_test( ".foo { grid-template-columns: repeat(4, 1fr); }", ".foo{grid-template-columns:repeat(4,1fr)}", ); minify_test( ".foo { grid-template-columns: repeat(2, [e] 40px); }", ".foo{grid-template-columns:repeat(2,[e]40px)}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] 250px [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]250px[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] 60% [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]60%[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] 1fr [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]1fr[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] min-content [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]min-content[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] max-content [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]max-content[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] auto [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]auto[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] minmax(100px, 1fr) [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]minmax(100px,1fr)[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, [col-start] fit-content(200px) [col-end]); }", ".foo{grid-template-columns:repeat(4,[col-start]fit-content(200px)[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(4, 10px [col-start] 30% [col-middle] auto [col-end]); }", ".foo{grid-template-columns:repeat(4,10px[col-start]30%[col-middle]auto[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(5, auto); }", ".foo{grid-template-columns:repeat(5,auto)}", ); minify_test( ".foo { grid-template-columns: repeat(auto-fill, 250px); }", ".foo{grid-template-columns:repeat(auto-fill,250px)}", ); minify_test( ".foo { grid-template-columns: repeat(auto-fit, 250px); }", ".foo{grid-template-columns:repeat(auto-fit,250px)}", ); minify_test( ".foo { grid-template-columns: repeat(auto-fill, [col-start] 250px [col-end]); }", ".foo{grid-template-columns:repeat(auto-fill,[col-start]250px[col-end])}", ); minify_test( ".foo { grid-template-columns: repeat(auto-fill, [col-start] minmax(100px, 1fr) [col-end]); }", ".foo{grid-template-columns:repeat(auto-fill,[col-start]minmax(100px,1fr)[col-end])}", ); minify_test( ".foo { grid-template-columns: minmax(min-content, 1fr); }", ".foo{grid-template-columns:minmax(min-content,1fr)}", ); minify_test( ".foo { grid-template-columns: 200px repeat(auto-fill, 100px) 300px; }", ".foo{grid-template-columns:200px repeat(auto-fill,100px) 300px}", ); minify_test(".foo { grid-template-columns: [linename1 linename2] 100px repeat(auto-fit, [linename1] 300px) [linename3]; }", ".foo{grid-template-columns:[linename1 linename2]100px repeat(auto-fit,[linename1]300px)[linename3]}"); minify_test( ".foo { grid-template-rows: [linename1 linename2] 100px repeat(auto-fit, [linename1] 300px) [linename3]; }", ".foo{grid-template-rows:[linename1 linename2]100px repeat(auto-fit,[linename1]300px)[linename3]}", ); minify_test(".foo { grid-auto-rows: auto; }", ".foo{grid-auto-rows:auto}"); minify_test(".foo { grid-auto-rows: 1fr; }", ".foo{grid-auto-rows:1fr}"); minify_test(".foo { grid-auto-rows: 100px; }", ".foo{grid-auto-rows:100px}"); minify_test( ".foo { grid-auto-rows: min-content; }", ".foo{grid-auto-rows:min-content}", ); minify_test( ".foo { grid-auto-rows: max-content; }", ".foo{grid-auto-rows:max-content}", ); minify_test( ".foo { grid-auto-rows: minmax(100px,auto); }", ".foo{grid-auto-rows:minmax(100px,auto)}", ); minify_test( ".foo { grid-auto-rows: fit-content(20%); }", ".foo{grid-auto-rows:fit-content(20%)}", ); minify_test( ".foo { grid-auto-rows: 100px minmax(100px, auto) 10% 0.5fr fit-content(400px); }", ".foo{grid-auto-rows:100px minmax(100px,auto) 10% .5fr fit-content(400px)}", ); minify_test( ".foo { grid-auto-columns: 100px minmax(100px, auto) 10% 0.5fr fit-content(400px); }", ".foo{grid-auto-columns:100px minmax(100px,auto) 10% .5fr fit-content(400px)}", ); minify_test( r#" .foo { grid-template-areas: "head head" "nav main" "foot ...."; } "#, ".foo{grid-template-areas:\"head head\"\"nav main\"\"foot.\"}", ); minify_test( r#" .foo { grid-template-areas: "head head" "nav main" ".... foot"; } "#, ".foo{grid-template-areas:\"head head\"\"nav main\"\".foot\"}", ); minify_test( r#" .foo { grid-template-areas: "head head" "nav main" ".... ...."; } "#, ".foo{grid-template-areas:\"head head\"\"nav main\"\". .\"}", ); test( r#" .foo { grid-template-areas: "head head" "nav main" "foot ...."; } "#, indoc! { r#" .foo { grid-template-areas: "head head" "nav main" "foot ."; } "#}, ); minify_test( r#" .foo { grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom]; } "#, ".foo{grid-template:[header-top]\"a a a\"[header-bottom main-top]\"b b b\"1fr[main-bottom]}", ); minify_test( r#" .foo { grid-template: "head head" "nav main" 1fr "foot ...."; } "#, ".foo{grid-template:\"head head\"\"nav main\"1fr\"foot.\"}", ); minify_test( r#" .foo { grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; } "#, ".foo{grid-template:[header-top]\"a a a\"[header-bottom main-top]\"b b b\"1fr[main-bottom]/auto 1fr auto}", ); minify_test( ".foo { grid-template: auto 1fr / auto 1fr auto; }", ".foo{grid-template:auto 1fr/auto 1fr auto}", ); minify_test( ".foo { grid-template: [linename1 linename2] 100px repeat(auto-fit, [linename1] 300px) [linename3] / [linename1 linename2] 100px repeat(auto-fit, [linename1] 300px) [linename3]; }", ".foo{grid-template:[linename1 linename2]100px repeat(auto-fit,[linename1]300px)[linename3]/[linename1 linename2]100px repeat(auto-fit,[linename1]300px)[linename3]}" ); test( ".foo{grid-template:[header-top]\"a a a\"[header-bottom main-top]\"b b b\"1fr[main-bottom]/auto 1fr auto}", indoc! {r#" .foo { grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; } "#}, ); test( ".foo{grid-template:[header-top]\"a a a\"[main-top]\"b b b\"1fr/auto 1fr auto}", indoc! {r#" .foo { grid-template: [header-top] "a a a" [main-top] "b b b" 1fr / auto 1fr auto; } "#}, ); minify_test(".foo { grid-auto-flow: row }", ".foo{grid-auto-flow:row}"); minify_test(".foo { grid-auto-flow: column }", ".foo{grid-auto-flow:column}"); minify_test(".foo { grid-auto-flow: row dense }", ".foo{grid-auto-flow:dense}"); minify_test(".foo { grid-auto-flow: dense row }", ".foo{grid-auto-flow:dense}"); minify_test( ".foo { grid-auto-flow: column dense }", ".foo{grid-auto-flow:column dense}", ); minify_test( ".foo { grid-auto-flow: dense column }", ".foo{grid-auto-flow:column dense}", ); minify_test(".foo { grid: none }", ".foo{grid:none}"); minify_test(".foo { grid: \"a\" 100px \"b\" 1fr }", ".foo{grid:\"a\"100px\"b\"1fr}"); minify_test( ".foo { grid: [linename1] \"a\" 100px [linename2] }", ".foo{grid:[linename1]\"a\"100px[linename2]}", ); minify_test( ".foo { grid: \"a\" 200px \"b\" min-content }", ".foo{grid:\"a\"200px\"b\"min-content}", ); minify_test( ".foo { grid: \"a\" minmax(100px, max-content) \"b\" 20% }", ".foo{grid:\"a\"minmax(100px,max-content)\"b\"20%}", ); minify_test(".foo { grid: 100px / 200px }", ".foo{grid:100px/200px}"); minify_test( ".foo { grid: minmax(400px, min-content) / repeat(auto-fill, 50px) }", ".foo{grid:minmax(400px,min-content)/repeat(auto-fill,50px)}", ); minify_test(".foo { grid: 200px / auto-flow }", ".foo{grid:200px/auto-flow}"); minify_test(".foo { grid: 30% / auto-flow dense }", ".foo{grid:30%/auto-flow dense}"); minify_test(".foo { grid: 30% / dense auto-flow }", ".foo{grid:30%/auto-flow dense}"); minify_test( ".foo { grid: repeat(3, [line1 line2 line3] 200px) / auto-flow 300px }", ".foo{grid:repeat(3,[line1 line2 line3]200px)/auto-flow 300px}", ); minify_test( ".foo { grid: [line1] minmax(20em, max-content) / auto-flow dense 40% }", ".foo{grid:[line1]minmax(20em,max-content)/auto-flow dense 40%}", ); minify_test(".foo { grid: none / auto-flow 1fr }", ".foo{grid:none/auto-flow 1fr}"); minify_test(".foo { grid: auto-flow / 200px }", ".foo{grid:none/200px}"); minify_test(".foo { grid: auto-flow dense / 30% }", ".foo{grid:auto-flow dense/30%}"); minify_test(".foo { grid: dense auto-flow / 30% }", ".foo{grid:auto-flow dense/30%}"); minify_test( ".foo { grid: auto-flow 300px / repeat(3, [line1 line2 line3] 200px) }", ".foo{grid:auto-flow 300px/repeat(3,[line1 line2 line3]200px)}", ); minify_test( ".foo { grid: auto-flow dense 40% / [line1] minmax(20em, max-content) }", ".foo{grid:auto-flow dense 40%/[line1]minmax(20em,max-content)}", ); minify_test(".foo { grid-row-start: auto }", ".foo{grid-row-start:auto}"); minify_test(".foo { grid-row-start: some-area }", ".foo{grid-row-start:some-area}"); minify_test(".foo { grid-row-start: 2 }", ".foo{grid-row-start:2}"); minify_test( ".foo { grid-row-start: 2 some-line }", ".foo{grid-row-start:2 some-line}", ); minify_test( ".foo { grid-row-start: some-line 2 }", ".foo{grid-row-start:2 some-line}", ); minify_test(".foo { grid-row-start: span 3 }", ".foo{grid-row-start:span 3}"); minify_test( ".foo { grid-row-start: span some-line }", ".foo{grid-row-start:span some-line}", ); minify_test( ".foo { grid-row-start: span some-line 1 }", ".foo{grid-row-start:span some-line}", ); minify_test( ".foo { grid-row-start: span 1 some-line }", ".foo{grid-row-start:span some-line}", ); minify_test( ".foo { grid-row-start: span 5 some-line }", ".foo{grid-row-start:span 5 some-line}", ); minify_test( ".foo { grid-row-start: span some-line 5 }", ".foo{grid-row-start:span 5 some-line}", ); minify_test( ".foo { grid-row-end: span 1 some-line }", ".foo{grid-row-end:span some-line}", ); minify_test( ".foo { grid-column-start: span 1 some-line }", ".foo{grid-column-start:span some-line}", ); minify_test( ".foo { grid-column-end: span 1 some-line }", ".foo{grid-column-end:span some-line}", ); minify_test(".foo { grid-row: 1 }", ".foo{grid-row:1}"); minify_test(".foo { grid-row: 1 / auto }", ".foo{grid-row:1}"); minify_test(".foo { grid-row: 1 / 1 }", ".foo{grid-row:1/1}"); minify_test(".foo { grid-row: 1 / 3 }", ".foo{grid-row:1/3}"); minify_test(".foo { grid-row: 1 / span 2 }", ".foo{grid-row:1/span 2}"); minify_test(".foo { grid-row: main-start }", ".foo{grid-row:main-start}"); minify_test( ".foo { grid-row: main-start / main-end }", ".foo{grid-row:main-start/main-end}", ); minify_test( ".foo { grid-row: main-start / main-start }", ".foo{grid-row:main-start}", ); minify_test(".foo { grid-column: 1 / auto }", ".foo{grid-column:1}"); minify_test(".foo { grid-area: a }", ".foo{grid-area:a}"); minify_test(".foo { grid-area: a / a / a / a }", ".foo{grid-area:a}"); minify_test(".foo { grid-area: a / b / a / b }", ".foo{grid-area:a/b}"); minify_test(".foo { grid-area: a / b / c / b }", ".foo{grid-area:a/b/c}"); minify_test(".foo { grid-area: a / b / c / d }", ".foo{grid-area:a/b/c/d}"); minify_test(".foo { grid-area: auto / auto / auto / auto }", ".foo{grid-area:auto}"); minify_test(".foo { grid-area: 1 / auto }", ".foo{grid-area:1}"); minify_test(".foo { grid-area: 1 / 2 / 3 / 4 }", ".foo{grid-area:1/2/3/4}"); minify_test(".foo { grid-area: 1 / 1 / 1 / 1 }", ".foo{grid-area:1/1/1/1}"); test( r#" .foo{ grid-template-rows: auto 1fr; grid-template-columns: auto 1fr auto; grid-template-areas: none; } "#, indoc! {r#" .foo { grid-template: auto 1fr / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-areas: "a a a" "b b b"; grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom]; grid-template-columns: auto 1fr auto; } "#, indoc! {r#" .foo { grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-areas: "a a a" "b b b"; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto 1fr; } "#, indoc! {r#" .foo { grid-template-rows: auto 1fr; grid-template-columns: repeat(3, 1fr); grid-template-areas: "a a a" "b b b"; } "#}, ); test( r#" .foo{ grid-template-areas: "a a a" "b b b"; grid-template-columns: auto 1fr auto; grid-template-rows: repeat(2, 1fr); } "#, indoc! {r#" .foo { grid-template-rows: repeat(2, 1fr); grid-template-columns: auto 1fr auto; grid-template-areas: "a a a" "b b b"; } "#}, ); test( r#" .foo{ grid-template-areas: ". a a ." ". b b ."; grid-template-rows: auto 1fr; grid-template-columns: 10px 1fr 1fr 10px; } "#, indoc! {r#" .foo { grid-template: ". a a ." ". b b ." 1fr / 10px 1fr 1fr 10px; } "#}, ); test( r#" .foo{ grid-template-areas: none; grid-template-columns: auto 1fr auto; grid-template-rows: repeat(2, 1fr); } "#, indoc! {r#" .foo { grid-template: repeat(2, 1fr) / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; } "#, indoc! {r#" .foo { grid-template: none; } "#}, ); test( r#" .foo{ grid-template-areas: "a a a" "b b b"; grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom]; grid-template-columns: auto 1fr auto; grid-auto-flow: row; grid-auto-rows: auto; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-areas: none; grid-template-columns: auto 1fr auto; grid-template-rows: repeat(2, 1fr); grid-auto-flow: row; grid-auto-rows: auto; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: repeat(2, 1fr) / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-areas: none; grid-template-columns: none; grid-template-rows: none; grid-auto-flow: row; grid-auto-rows: auto; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: none; } "#}, ); test( r#" .foo{ grid-template-areas: "a a a" "b b b"; grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom]; grid-template-columns: auto 1fr auto; grid-auto-flow: column; grid-auto-rows: 1fr; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; grid-auto-rows: 1fr; grid-auto-columns: 1fr; grid-auto-flow: column; } "#}, ); test( r#" .foo{ grid-template-rows: auto 1fr; grid-template-columns: auto 1fr auto; grid-template-areas: none; grid-auto-flow: row; grid-auto-rows: auto; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: auto 1fr / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-rows: auto 1fr; grid-template-columns: auto 1fr auto; grid-template-areas: none; grid-auto-flow: column; grid-auto-rows: 1fr; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid-template: auto 1fr / auto 1fr auto; grid-auto-rows: 1fr; grid-auto-columns: 1fr; grid-auto-flow: column; } "#}, ); test( r#" .foo{ grid-template-rows: none; grid-template-columns: auto 1fr auto; grid-template-areas: none; grid-auto-flow: column; grid-auto-rows: 1fr; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid-template: none / auto 1fr auto; grid-auto-rows: 1fr; grid-auto-columns: 1fr; grid-auto-flow: column; } "#}, ); test( r#" .foo{ grid-template-rows: none; grid-template-columns: auto 1fr auto; grid-template-areas: none; grid-auto-flow: row; grid-auto-rows: 1fr; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: auto-flow 1fr / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-rows: none; grid-template-columns: auto 1fr auto; grid-template-areas: none; grid-auto-flow: row dense; grid-auto-rows: 1fr; grid-auto-columns: auto; } "#, indoc! {r#" .foo { grid: auto-flow dense 1fr / auto 1fr auto; } "#}, ); test( r#" .foo{ grid-template-rows: auto 1fr auto; grid-template-columns: none; grid-template-areas: none; grid-auto-flow: column; grid-auto-rows: auto; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid: auto 1fr auto / auto-flow 1fr; } "#}, ); test( r#" .foo{ grid-template-rows: auto 1fr auto; grid-template-columns: none; grid-template-areas: none; grid-auto-flow: column dense; grid-auto-rows: auto; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid: auto 1fr auto / auto-flow dense 1fr; } "#}, ); test( r#" .foo{ grid-template-rows: auto 1fr auto; grid-template-columns: none; grid-template-areas: none; grid-auto-flow: var(--auto-flow); grid-auto-rows: auto; grid-auto-columns: 1fr; } "#, indoc! {r#" .foo { grid-template: auto 1fr auto / none; grid-auto-flow: var(--auto-flow); grid-auto-rows: auto; grid-auto-columns: 1fr; } "#}, ); test( r#" .foo{ grid: auto 1fr auto / auto-flow dense 1fr; grid-template-rows: 1fr 1fr 1fr; } "#, indoc! {r#" .foo { grid: 1fr 1fr 1fr / auto-flow dense 1fr; } "#}, ); test( r#" .foo{ grid-row-start: a; grid-row-end: a; grid-column-start: a; grid-column-end: a; } "#, indoc! {r#" .foo { grid-area: a; } "#}, ); test( r#" .foo{ grid-row-start: 1; grid-row-end: 2; grid-column-start: 3; grid-column-end: 4; } "#, indoc! {r#" .foo { grid-area: 1 / 3 / 2 / 4; } "#}, ); test( r#" .foo{ grid-row-start: a; grid-row-end: a; } "#, indoc! {r#" .foo { grid-row: a; } "#}, ); test( r#" .foo{ grid-column-start: a; grid-column-end: a; } "#, indoc! {r#" .foo { grid-column: a; } "#}, ); } #[test] fn test_moz_document() { minify_test( r#" @-moz-document url-prefix() { h1 { color: yellow; } } "#, "@-moz-document url-prefix(){h1{color:#ff0}}", ); minify_test( r#" @-moz-document url-prefix("") { h1 { color: yellow; } } "#, "@-moz-document url-prefix(){h1{color:#ff0}}", ); error_test( "@-moz-document url-prefix(foo) {}", ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("foo".into())), ); error_test( "@-moz-document url-prefix(\"foo\") {}", ParserError::UnexpectedToken(crate::properties::custom::Token::String("foo".into())), ); } #[test] fn test_custom_properties() { minify_test(".foo { --test: ; }", ".foo{--test: }"); minify_test(".foo { --test: ; }", ".foo{--test: }"); minify_test(".foo { --test: foo; }", ".foo{--test:foo}"); minify_test(".foo { --test: foo; }", ".foo{--test:foo}"); minify_test(".foo { --test: foo ; }", ".foo{--test:foo}"); minify_test(".foo { --test: foo ; }", ".foo{--test:foo}"); minify_test(".foo { --test:foo; }", ".foo{--test:foo}"); minify_test(".foo { --test:foo ; }", ".foo{--test:foo}"); minify_test(".foo { --test: var(--foo, 20px); }", ".foo{--test:var(--foo,20px)}"); minify_test( ".foo { transition: var(--foo, 20px),\nvar(--bar, 40px); }", ".foo{transition:var(--foo,20px),var(--bar,40px)}", ); minify_test( ".foo { background: var(--color) var(--image); }", ".foo{background:var(--color)var(--image)}", ); minify_test( ".foo { height: calc(var(--spectrum-global-dimension-size-300) / 2);", ".foo{height:calc(var(--spectrum-global-dimension-size-300)/2)}", ); minify_test( ".foo { color: var(--color, rgb(255, 255, 0)); }", ".foo{color:var(--color,#ff0)}", ); minify_test( ".foo { color: var(--color, #ffff00); }", ".foo{color:var(--color,#ff0)}", ); minify_test( ".foo { color: var(--color, rgb(var(--red), var(--green), 0)); }", ".foo{color:var(--color,rgb(var(--red),var(--green),0))}", ); minify_test(".foo { --test: .5s; }", ".foo{--test:.5s}"); minify_test(".foo { --theme-sizes-1\\/12: 2 }", ".foo{--theme-sizes-1\\/12:2}"); minify_test(".foo { --test: 0px; }", ".foo{--test:0px}"); prefix_test( r#" .foo { --custom: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --custom: #b32323; } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: lab(40% 56.6 39) !important; } "#, indoc! {r#" .foo { --custom: #b32323 !important; } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39) !important; } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --custom: #b32323; } @supports (color: color(display-p3 0 0 0)) { .foo { --custom: color(display-p3 .643308 .192455 .167712); } } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --custom: color(display-p3 .643308 .192455 .167712); } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39); } } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --custom: lab(40% 56.6 39); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: oklab(59.686% 0.1009 0.1192); } "#, indoc! {r#" .foo { --custom: lab(52.2319% 40.1449 59.9171); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: oklab(59.686% 0.1009 0.1192); } "#, indoc! {r#" .foo { --custom: color(display-p3 .724144 .386777 .148795); } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(52.2319% 40.1449 59.9171); } } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: oklab(59.686% 0.1009 0.1192); } "#, indoc! {r#" .foo { --custom: #c65d07; } @supports (color: color(display-p3 0 0 0)) { .foo { --custom: color(display-p3 .724144 .386777 .148795); } } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(52.2319% 40.1449 59.9171); } } "#}, Browsers { safari: Some(14 << 16), chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --foo: oklab(59.686% 0.1009 0.1192); --bar: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --foo: #c65d07; --bar: #b32323; } @supports (color: color(display-p3 0 0 0)) { .foo { --foo: color(display-p3 .724144 .386777 .148795); --bar: color(display-p3 .643308 .192455 .167712); } } @supports (color: lab(0% 0 0)) { .foo { --foo: lab(52.2319% 40.1449 59.9171); --bar: lab(40% 56.6 39); } } "#}, Browsers { safari: Some(14 << 16), chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --foo: color(display-p3 0 1 0); } "#, indoc! {r#" .foo { --foo: #00f942; } @supports (color: color(display-p3 0 0 0)) { .foo { --foo: color(display-p3 0 1 0); } } "#}, Browsers { safari: Some(14 << 16), chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --foo: color(display-p3 0 1 0); } "#, indoc! {r#" .foo { --foo: color(display-p3 0 1 0); } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --foo: color(display-p3 0 1 0); } "#, indoc! {r#" .foo { --foo: #00f942; } @supports (color: color(display-p3 0 0 0)) { .foo { --foo: color(display-p3 0 1 0); } } "#}, Browsers { safari: Some(15 << 16), chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --foo: color(display-p3 0 1 0); } "#, indoc! {r#" .foo { --foo: #00f942; } @supports (color: color(display-p3 0 0 0)) { .foo { --foo: color(display-p3 0 1 0); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { text-decoration: underline; } .foo { --custom: lab(40% 56.6 39); } "#, indoc! {r#" .foo { --custom: #b32323; text-decoration: underline; } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { --custom: lab(40% 56.6 39); } .foo { text-decoration: underline; } "#, indoc! {r#" .foo { --custom: #b32323; } @supports (color: lab(0% 0 0)) { .foo { --custom: lab(40% 56.6 39); } } .foo { text-decoration: underline; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" @keyframes foo { from { --custom: lab(40% 56.6 39); } to { --custom: lch(50.998% 135.363 338); } } "#, indoc! {r#" @keyframes foo { from { --custom: #b32323; } to { --custom: #ee00be; } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { --custom: lab(40% 56.6 39); } to { --custom: lab(50.998% 125.506 -50.7078); } } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" @keyframes foo { from { --custom: lab(40% 56.6 39); } to { --custom: lch(50.998% 135.363 338); } } "#, indoc! {r#" @keyframes foo { from { --custom: #b32323; } to { --custom: #ee00be; } } @supports (color: color(display-p3 0 0 0)) { @keyframes foo { from { --custom: color(display-p3 .643308 .192455 .167712); } to { --custom: color(display-p3 .972962 -.362078 .804206); } } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { --custom: lab(40% 56.6 39); } to { --custom: lab(50.998% 125.506 -50.7078); } } } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( r#" @keyframes foo { from { --custom: #ff0; opacity: 0; } to { --custom: lch(50.998% 135.363 338); opacity: 1; } } "#, indoc! {r#" @keyframes foo { from { --custom: #ff0; opacity: 0; } to { --custom: #ee00be; opacity: 1; } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { --custom: #ff0; opacity: 0; } to { --custom: lab(50.998% 125.506 -50.7078); opacity: 1; } } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" @keyframes foo { from { text-decoration: var(--foo) lab(29.2345% 39.3825 20.0664); } } "#, indoc! {r#" @keyframes foo { from { text-decoration: var(--foo) #7d2329; } } @supports (color: lab(0% 0 0)) { @keyframes foo { from { text-decoration: var(--foo) lab(29.2345% 39.3825 20.0664); } } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] fn test_charset() { test( r#" @charset "UTF-8"; .foo { color: red; } @charset "UTF-8"; .bar { color: yellow; } "#, indoc! { r#" .foo { color: red; } .bar { color: #ff0; } "#}, ) } #[test] fn test_style_attr() { attr_test("color: yellow; flex: 1 1 auto", "color: #ff0; flex: auto", false, None); attr_test("color: yellow; flex: 1 1 auto", "color:#ff0;flex:auto", true, None); attr_test( "border-inline-start: 2px solid red", "border-inline-start: 2px solid red", false, Some(Browsers { safari: Some(12 << 16), ..Browsers::default() }), ); attr_test( "color: lab(40% 56.6 39);", "color:#b32323;color:lab(40% 56.6 39)", true, Some(Browsers { safari: Some(8 << 16), ..Browsers::default() }), ); attr_test( "--foo: lab(40% 56.6 39);", "--foo:#b32323", true, Some(Browsers { safari: Some(8 << 16), ..Browsers::default() }), ); attr_test( "text-decoration: var(--foo) lab(40% 56.6 39);", "text-decoration:var(--foo)#b32323", true, Some(Browsers { chrome: Some(90 << 16), ..Browsers::default() }), ); } #[test] fn test_nesting() { nesting_test( r#" .foo { color: blue; & > .bar { color: red; } } "#, indoc! {r#" .foo { color: #00f; } .foo > .bar { color: red; } "#}, ); nesting_test( r#" .foo { color: blue; &.bar { color: red; } } "#, indoc! {r#" .foo { color: #00f; } .foo.bar { color: red; } "#}, ); nesting_test( r#" .foo, .bar { color: blue; & + .baz, &.qux { color: red; } } "#, indoc! {r#" .foo, .bar { color: #00f; } :is(.foo, .bar) + .baz, :is(.foo, .bar).qux { color: red; } "#}, ); nesting_test( r#" .foo { color: blue; & .bar & .baz & .qux { color: red; } } "#, indoc! {r#" .foo { color: #00f; } .foo .bar .foo .baz .foo .qux { color: red; } "#}, ); nesting_test( r#" .foo { color: blue; & { padding: 2ch; } } "#, indoc! {r#" .foo { color: #00f; } .foo { padding: 2ch; } "#}, ); nesting_test( r#" .foo { color: blue; && { padding: 2ch; } } "#, indoc! {r#" .foo { color: #00f; } .foo.foo { padding: 2ch; } "#}, ); nesting_test( r#" .error, .invalid { &:hover > .baz { color: red; } } "#, indoc! {r#" :is(.error, .invalid):hover > .baz { color: red; } "#}, ); nesting_test( r#" .foo { &:is(.bar, &.baz) { color: red; } } "#, indoc! {r#" .foo:is(.bar, .foo.baz) { color: red; } "#}, ); nesting_test( r#" figure { margin: 0; & > figcaption { background: hsl(0 0% 0% / 50%); & > p { font-size: .9rem; } } } "#, indoc! {r#" figure { margin: 0; } figure > figcaption { background: #00000080; } figure > figcaption > p { font-size: .9rem; } "#}, ); nesting_test( r#" .foo { display: grid; @media (orientation: landscape) { grid-auto-flow: column; } } "#, indoc! {r#" .foo { display: grid; } @media (orientation: landscape) { .foo { grid-auto-flow: column; } } "#}, ); nesting_test( r#" .foo { display: grid; @media (orientation: landscape) { grid-auto-flow: column; @media (width > 1024px) { max-inline-size: 1024px; } } } "#, indoc! {r#" .foo { display: grid; } @media (orientation: landscape) { .foo { grid-auto-flow: column; } @media (min-width: 1024px) { .foo { max-inline-size: 1024px; } } } "#}, ); nesting_test( r#" .foo { @media (min-width: 640px) { color: red !important; } } "#, indoc! {r#" @media (min-width: 640px) { .foo { color: red !important; } } "#}, ); nesting_test( r#" .foo { display: grid; @supports (foo: bar) { grid-auto-flow: column; } } "#, indoc! {r#" .foo { display: grid; } @supports (foo: bar) { .foo { grid-auto-flow: column; } } "#}, ); nesting_test( r#" .foo { display: grid; @container (min-width: 100px) { grid-auto-flow: column; } } "#, indoc! {r#" .foo { display: grid; } @container (width >= 100px) { .foo { grid-auto-flow: column; } } "#}, ); nesting_test( r#" .foo { display: grid; @layer test { grid-auto-flow: column; } } "#, indoc! {r#" .foo { display: grid; } @layer test { .foo { grid-auto-flow: column; } } "#}, ); nesting_test( r#" .foo { display: grid; @layer { grid-auto-flow: column; } } "#, indoc! {r#" .foo { display: grid; } @layer { .foo { grid-auto-flow: column; } } "#}, ); nesting_test( r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; .foo { &div { color: red; } &* { color: green; } &|x { color: red; } &*|x { color: green; } &toto|x { color: red; } } "#, indoc! {r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; div.foo { color: red; } *.foo { color: green; } |x.foo { color: red; } *|x.foo { color: green; } toto|x.foo { color: red; } "#}, ); nesting_test( r#" .foo { &article > figure { color: red; } } "#, indoc! {r#" article.foo > figure { color: red; } "#}, ); nesting_test( r#" div { &.bar { background: green; } } "#, indoc! {r#" div.bar { background: green; } "#}, ); nesting_test( r#" div > .foo { &span { background: green; } } "#, indoc! {r#" span:is(div > .foo) { background: green; } "#}, ); nesting_test( r#" .foo { & h1 { background: green; } } "#, indoc! {r#" .foo h1 { background: green; } "#}, ); nesting_test( r#" .foo .bar { &h1 { background: green; } } "#, indoc! {r#" h1:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo.bar { &h1 { background: green; } } "#, indoc! {r#" h1.foo.bar { background: green; } "#}, ); nesting_test( r#" .foo .bar { &h1 .baz { background: green; } } "#, indoc! {r#" h1:is(.foo .bar) .baz { background: green; } "#}, ); nesting_test( r#" .foo .bar { &.baz { background: green; } } "#, indoc! {r#" .foo .bar.baz { background: green; } "#}, ); nesting_test( r#" .foo { color: red; @nest .parent & { color: blue; } } "#, indoc! {r#" .foo { color: red; } .parent .foo { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; @nest :not(&) { color: blue; } } "#, indoc! {r#" .foo { color: red; } :not(.foo) { color: #00f; } "#}, ); nesting_test( r#" .foo { color: blue; @nest .bar & { color: red; &.baz { color: green; } } } "#, indoc! {r#" .foo { color: #00f; } .bar .foo { color: red; } .bar .foo.baz { color: green; } "#}, ); nesting_test( r#" .foo { @nest :not(&) { color: red; } & h1 { background: green; } } "#, indoc! {r#" :not(.foo) { color: red; } .foo h1 { background: green; } "#}, ); nesting_test( r#" .foo { & h1 { background: green; } @nest :not(&) { color: red; } } "#, indoc! {r#" .foo h1 { background: green; } :not(.foo) { color: red; } "#}, ); nesting_test( r#" .foo .bar { @nest h1& { background: green; } } "#, indoc! {r#" h1:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; div { @nest .foo& { color: red; } } * { @nest .foo& { color: red; } } |x { @nest .foo& { color: red; } } *|x { @nest .foo& { color: red; } } toto|x { @nest .foo& { color: red; } } "#, indoc! {r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; .foo:is(div) { color: red; } .foo:is(*) { color: red; } .foo:is(|x) { color: red; } .foo:is(*|x) { color: red; } .foo:is(toto|x) { color: red; } "#}, ); nesting_test( r#" .foo .bar { @nest h1 .baz& { background: green; } } "#, indoc! {r#" h1 .baz:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo .bar { @nest .baz& { background: green; } } "#, indoc! {r#" .baz:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo .bar { @nest .baz & { background: green; } } "#, indoc! {r#" .baz :is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo { color: red; @nest & > .bar { color: blue; } } "#, indoc! {r#" .foo { color: red; } .foo > .bar { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; .bar { color: blue; } } "#, indoc! {r#" .foo { color: red; } .foo .bar { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; .bar & { color: blue; } } "#, indoc! {r#" .foo { color: red; } .bar .foo { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; + .bar + & { color: blue; } } "#, indoc! {r#" .foo { color: red; } .foo + .bar + .foo { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; .bar & { color: blue; } } "#, indoc! {r#" .foo { color: red; } .bar .foo { color: #00f; } "#}, ); nesting_error_test( r#" .foo { color: blue; div { color: red; } } "#, ParserError::UnexpectedToken(crate::properties::custom::Token::CurlyBracketBlock), ); nesting_test( r#" .foo { color: red; .parent & { color: blue; } } "#, indoc! {r#" .foo { color: red; } .parent .foo { color: #00f; } "#}, ); nesting_test( r#" .foo { color: red; :not(&) { color: blue; } } "#, indoc! {r#" .foo { color: red; } :not(.foo) { color: #00f; } "#}, ); nesting_test( r#" .foo { color: blue; .bar & { color: red; &.baz { color: green; } } } "#, indoc! {r#" .foo { color: #00f; } .bar .foo { color: red; } .bar .foo.baz { color: green; } "#}, ); nesting_test( r#" .foo { :not(&) { color: red; } & h1 { background: green; } } "#, indoc! {r#" :not(.foo) { color: red; } .foo h1 { background: green; } "#}, ); nesting_test( r#" .foo { & h1 { background: green; } :not(&) { color: red; } } "#, indoc! {r#" .foo h1 { background: green; } :not(.foo) { color: red; } "#}, ); nesting_test( r#" .foo .bar { :is(h1)& { background: green; } } "#, indoc! {r#" :is(h1):is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; div { .foo& { color: red; } } * { .foo& { color: red; } } |x { .foo& { color: red; } } *|x { .foo& { color: red; } } toto|x { .foo& { color: red; } } "#, indoc! {r#" @namespace "http://example.com/foo"; @namespace toto "http://toto.example.org"; .foo:is(div) { color: red; } .foo:is(*) { color: red; } .foo:is(|x) { color: red; } .foo:is(*|x) { color: red; } .foo:is(toto|x) { color: red; } "#}, ); nesting_test( r#" .foo .bar { :is(h1) .baz& { background: green; } } "#, indoc! {r#" :is(h1) .baz:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo .bar { .baz& { background: green; } } "#, indoc! {r#" .baz:is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo .bar { .baz & { background: green; } } "#, indoc! {r#" .baz :is(.foo .bar) { background: green; } "#}, ); nesting_test( r#" .foo { .bar { color: blue; } color: red; } "#, indoc! {r#" .foo { color: red; } .foo .bar { color: #00f; } "#}, ); nesting_test( r#" article { color: green; & { color: blue; } color: red; } "#, indoc! {r#" article { color: green; color: red; } article { color: #00f; } "#}, ); nesting_test( r#" & .foo { color: red; } "#, indoc! {r#" :scope .foo { color: red; } "#}, ); nesting_test( r#" &.foo { color: red; } "#, indoc! {r#" :scope.foo { color: red; } "#}, ); nesting_test( r#" .foo& { color: red; } "#, indoc! {r#" .foo:scope { color: red; } "#}, ); nesting_test( r#" &html { color: red; } "#, indoc! {r#" html:scope { color: red; } "#}, ); nesting_test_no_targets( r#" .foo { color: blue; @nest .bar & { color: red; &.baz { color: green; } } } "#, indoc! {r#" .foo { color: #00f; @nest .bar & { color: red; &.baz { color: green; } } } "#}, ); nesting_test_no_targets( r#" .foo { color: blue; &div { color: red; } &span { color: purple; } } "#, indoc! {r#" .foo { color: #00f; &div { color: red; } &span { color: purple; } } "#}, ); nesting_test_no_targets( r#" .error, .invalid { &:hover > .baz { color: red; } } "#, indoc! {r#" .error, .invalid { &:hover > .baz { color: red; } } "#}, ); } #[test] fn test_css_modules() { css_modules_test( r#" .foo { color: red; } #id { animation: 2s test; } @keyframes test { from { color: red } to { color: yellow } } @counter-style circles { symbols: Ⓐ Ⓑ Ⓒ; } ul { list-style: circles; } ol { list-style-type: none; } li { list-style-type: disc; } @keyframes fade { from { opacity: 0 } to { opacity: 1 } } "#, indoc! {r#" .EgL3uq_foo { color: red; } #EgL3uq_id { animation: 2s EgL3uq_test; } @keyframes EgL3uq_test { from { color: red; } to { color: #ff0; } } @counter-style EgL3uq_circles { symbols: Ⓐ Ⓑ Ⓒ; } ul { list-style: EgL3uq_circles; } ol { list-style-type: none; } li { list-style-type: disc; } @keyframes EgL3uq_fade { from { opacity: 0; } to { opacity: 1; } } "#}, map! { "foo" => "EgL3uq_foo", "id" => "EgL3uq_id", "test" => "EgL3uq_test" referenced: true, "circles" => "EgL3uq_circles" referenced: true, "fade" => "EgL3uq_fade" }, HashMap::new(), Default::default(), ); #[cfg(feature = "grid")] css_modules_test( r#" body { grid: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; } header { grid-area: a; } main { grid-row: main-top / main-bottom; } "#, indoc! {r#" body { grid: [EgL3uq_header-top] "EgL3uq_a EgL3uq_a EgL3uq_a" [EgL3uq_header-bottom] [EgL3uq_main-top] "EgL3uq_b EgL3uq_b EgL3uq_b" 1fr [EgL3uq_main-bottom] / auto 1fr auto; } header { grid-area: EgL3uq_a; } main { grid-row: EgL3uq_main-top / EgL3uq_main-bottom; } "#}, map! { "header-top" => "EgL3uq_header-top", "header-bottom" => "EgL3uq_header-bottom", "main-top" => "EgL3uq_main-top", "main-bottom" => "EgL3uq_main-bottom", "a" => "EgL3uq_a", "b" => "EgL3uq_b" }, HashMap::new(), Default::default(), ); #[cfg(feature = "grid")] css_modules_test( r#" .grid { grid-template-areas: "foo"; } .foo { grid-area: foo; } .bar { grid-column-start: foo-start; } "#, indoc! {r#" .EgL3uq_grid { grid-template-areas: "EgL3uq_foo"; } .EgL3uq_foo { grid-area: EgL3uq_foo; } .EgL3uq_bar { grid-column-start: EgL3uq_foo-start; } "#}, map! { "foo" => "EgL3uq_foo", "foo-start" => "EgL3uq_foo-start", "grid" => "EgL3uq_grid", "bar" => "EgL3uq_bar" }, HashMap::new(), Default::default(), ); css_modules_test( r#" test { transition-property: opacity; } "#, indoc! {r#" test { transition-property: opacity; } "#}, map! {}, HashMap::new(), Default::default(), ); css_modules_test( r#" :global(.foo) { color: red; } :local(.bar) { color: yellow; } .bar :global(.baz) { color: purple; } "#, indoc! {r#" .foo { color: red; } .EgL3uq_bar { color: #ff0; } .EgL3uq_bar .baz { color: purple; } "#}, map! { "bar" => "EgL3uq_bar" }, HashMap::new(), Default::default(), ); // :global(:local(.hi)) { // color: green; // } css_modules_test( r#" .test { composes: foo; background: white; } .foo { color: red; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } .EgL3uq_foo { color: red; } "#}, map! { "test" => "EgL3uq_test" "EgL3uq_foo", "foo" => "EgL3uq_foo" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .a, .b { composes: foo; background: white; } .foo { color: red; } "#, indoc! {r#" .EgL3uq_a, .EgL3uq_b { background: #fff; } .EgL3uq_foo { color: red; } "#}, map! { "a" => "EgL3uq_a" "EgL3uq_foo", "b" => "EgL3uq_b" "EgL3uq_foo", "foo" => "EgL3uq_foo" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo bar; background: white; } .foo { color: red; } .bar { color: yellow; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } .EgL3uq_foo { color: red; } .EgL3uq_bar { color: #ff0; } "#}, map! { "test" => "EgL3uq_test" "EgL3uq_foo" "EgL3uq_bar", "foo" => "EgL3uq_foo", "bar" => "EgL3uq_bar" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo from global; background: white; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } "#}, map! { "test" => "EgL3uq_test" "foo" global: true }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo bar from global; background: white; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } "#}, map! { "test" => "EgL3uq_test" "foo" global: true "bar" global: true }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo from "foo.css"; background: white; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } "#}, map! { "test" => "EgL3uq_test" "foo" from "foo.css" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo bar from "foo.css"; background: white; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } "#}, map! { "test" => "EgL3uq_test" "foo" from "foo.css" "bar" from "foo.css" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .test { composes: foo; composes: foo from "foo.css"; composes: bar from "bar.css"; background: white; } .foo { color: red; } "#, indoc! {r#" .EgL3uq_test { background: #fff; } .EgL3uq_foo { color: red; } "#}, map! { "test" => "EgL3uq_test" "EgL3uq_foo" "foo" from "foo.css" "bar" from "bar.css", "foo" => "EgL3uq_foo" }, HashMap::new(), Default::default(), ); css_modules_test( r#" .foo { color: red; } "#, indoc! {r#" .test-EgL3uq-foo { color: red; } "#}, map! { "foo" => "test-EgL3uq-foo" }, HashMap::new(), crate::css_modules::Config { pattern: crate::css_modules::Pattern::parse("test-[hash]-[local]").unwrap(), ..Default::default() }, ); let stylesheet = StyleSheet::parse( r#" .grid { grid-template-areas: "foo"; } .foo { grid-area: foo; } .bar { grid-column-start: foo-start; } "#, ParserOptions { css_modules: Some(crate::css_modules::Config { pattern: crate::css_modules::Pattern::parse("test-[local]-[hash]").unwrap(), ..Default::default() }), ..ParserOptions::default() }, ) .unwrap(); if let Err(err) = stylesheet.to_css(PrinterOptions::default()) { assert_eq!(err.kind, PrinterErrorKind::InvalidCssModulesPatternInGrid); } else { unreachable!() } css_modules_test( r#" @property --foo { syntax: ''; inherits: false; initial-value: yellow; } .foo { --foo: red; color: var(--foo); } "#, indoc! {r#" @property --foo { syntax: ""; inherits: false; initial-value: #ff0; } .EgL3uq_foo { --foo: red; color: var(--foo); } "#}, map! { "foo" => "EgL3uq_foo" }, HashMap::new(), Default::default(), ); css_modules_test( r#" @property --foo { syntax: ''; inherits: false; initial-value: yellow; } @font-palette-values --Cooler { font-family: Bixa; base-palette: 1; override-colors: 1 #7EB7E4; } .foo { --foo: red; --bar: green; color: var(--foo); font-palette: --Cooler; } .bar { color: var(--color from "./b.css"); } "#, indoc! {r#" @property --EgL3uq_foo { syntax: ""; inherits: false; initial-value: #ff0; } @font-palette-values --EgL3uq_Cooler { font-family: Bixa; base-palette: 1; override-colors: 1 #7eb7e4; } .EgL3uq_foo { --EgL3uq_foo: red; --EgL3uq_bar: green; color: var(--EgL3uq_foo); font-palette: --EgL3uq_Cooler; } .EgL3uq_bar { color: var(--ma1CsG); } "#}, map! { "foo" => "EgL3uq_foo", "--foo" => "--EgL3uq_foo" referenced: true, "--bar" => "--EgL3uq_bar", "bar" => "EgL3uq_bar", "--Cooler" => "--EgL3uq_Cooler" referenced: true }, HashMap::from([( "--ma1CsG".into(), CssModuleReference::Dependency { name: "--color".into(), specifier: "./b.css".into(), }, )]), crate::css_modules::Config { dashed_idents: true, ..Default::default() }, ); // Stable hashes between project roots. fn test_project_root(project_root: &str, filename: &str, hash: &str) { let stylesheet = StyleSheet::parse( r#" .foo { background: red; } "#, ParserOptions { filename: filename.into(), css_modules: Some(Default::default()), ..ParserOptions::default() }, ) .unwrap(); let res = stylesheet .to_css(PrinterOptions { project_root: Some(project_root), ..PrinterOptions::default() }) .unwrap(); assert_eq!( res.code, format!( indoc! {r#" .{}_foo {{ background: red; }} "#}, hash ) ); } test_project_root("/foo/bar", "/foo/bar/test.css", "EgL3uq"); test_project_root("/foo", "/foo/test.css", "EgL3uq"); test_project_root("/foo/bar", "/foo/bar/baz/test.css", "xLEkNW"); test_project_root("/foo", "/foo/baz/test.css", "xLEkNW"); } #[test] fn test_pseudo_replacement() { let source = r#" .foo:hover { color: red; } .foo:active { color: yellow; } .foo:focus-visible { color: purple; } "#; let expected = indoc! { r#" .foo.is-hovered { color: red; } .foo.is-active { color: #ff0; } .foo.focus-visible { color: purple; } "#}; let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); let res = stylesheet .to_css(PrinterOptions { pseudo_classes: Some(PseudoClasses { hover: Some("is-hovered"), active: Some("is-active"), focus_visible: Some("focus-visible"), ..PseudoClasses::default() }), ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); let source = r#" .foo:hover { color: red; } "#; let expected = indoc! { r#" .EgL3uq_foo.EgL3uq_is-hovered { color: red; } "#}; let stylesheet = StyleSheet::parse( &source, ParserOptions { filename: "test.css".into(), css_modules: Some(Default::default()), ..ParserOptions::default() }, ) .unwrap(); let res = stylesheet .to_css(PrinterOptions { pseudo_classes: Some(PseudoClasses { hover: Some("is-hovered"), ..PseudoClasses::default() }), ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); } #[test] fn test_unused_symbols() { let source = r#" .foo { color: red; } .bar { color: green; } .bar:hover { color: purple; } .bar .baz { background: red; } .baz:is(.bar) { background: green; } #id { animation: 2s test; } #other_id { color: red; } @keyframes test { from { color: red } to { color: yellow } } @counter-style circles { symbols: Ⓐ Ⓑ Ⓒ; } @keyframes fade { from { opacity: 0 } to { opacity: 1 } } "#; let expected = indoc! {r#" .foo { color: red; } #id { animation: 2s test; } @keyframes test { from { color: red; } to { color: #ff0; } } "#}; let mut stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); stylesheet .minify(MinifyOptions { unused_symbols: vec!["bar", "other_id", "fade", "circles"] .iter() .map(|s| String::from(*s)) .collect(), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); let source = r#" .foo { color: red; &.bar { color: green; } } "#; let expected = indoc! {r#" .foo { color: red; } "#}; let mut stylesheet = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet .minify(MinifyOptions { unused_symbols: vec!["bar"].iter().map(|s| String::from(*s)).collect(), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); let source = r#" .foo { color: red; &.bar { color: purple; } @nest &.bar { color: orange; } @nest :not(&) { color: green; } @media (orientation: portrait) { color: brown; } } .x { color: purple; &.y { color: green; } } "#; let expected = indoc! {r#" :not(.foo) { color: green; } "#}; let mut stylesheet = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet .minify(MinifyOptions { unused_symbols: vec!["foo", "x"].iter().map(|s| String::from(*s)).collect(), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet .to_css(PrinterOptions { targets: Some(Browsers { chrome: Some(95 << 16), ..Browsers::default() }), ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); let source = r#" @property --EgL3uq_foo { syntax: ""; inherits: false; initial-value: #ff0; } @font-palette-values --EgL3uq_Cooler { font-family: Bixa; base-palette: 1; override-colors: 1 #7EB7E4; } .EgL3uq_foo { --EgL3uq_foo: red; } .EgL3uq_bar { color: green; } "#; let expected = indoc! {r#" .EgL3uq_bar { color: green; } "#}; let mut stylesheet = StyleSheet::parse( &source, ParserOptions { nesting: true, ..ParserOptions::default() }, ) .unwrap(); stylesheet .minify(MinifyOptions { unused_symbols: vec!["--EgL3uq_foo", "--EgL3uq_Cooler"] .iter() .map(|s| String::from(*s)) .collect(), ..MinifyOptions::default() }) .unwrap(); let res = stylesheet.to_css(PrinterOptions::default()).unwrap(); assert_eq!(res.code, expected); } #[test] fn test_svg() { minify_test(".foo { fill: yellow; }", ".foo{fill:#ff0}"); minify_test(".foo { fill: url(#foo); }", ".foo{fill:url(#foo)}"); minify_test(".foo { fill: url(#foo) none; }", ".foo{fill:url(#foo) none}"); minify_test(".foo { fill: url(#foo) yellow; }", ".foo{fill:url(#foo) #ff0}"); minify_test(".foo { fill: none; }", ".foo{fill:none}"); minify_test(".foo { fill: context-fill; }", ".foo{fill:context-fill}"); minify_test(".foo { fill: context-stroke; }", ".foo{fill:context-stroke}"); minify_test(".foo { stroke: yellow; }", ".foo{stroke:#ff0}"); minify_test(".foo { stroke: url(#foo); }", ".foo{stroke:url(#foo)}"); minify_test(".foo { stroke: url(#foo) none; }", ".foo{stroke:url(#foo) none}"); minify_test(".foo { stroke: url(#foo) yellow; }", ".foo{stroke:url(#foo) #ff0}"); minify_test(".foo { stroke: none; }", ".foo{stroke:none}"); minify_test(".foo { stroke: context-fill; }", ".foo{stroke:context-fill}"); minify_test(".foo { stroke: context-stroke; }", ".foo{stroke:context-stroke}"); minify_test(".foo { marker-start: url(#foo); }", ".foo{marker-start:url(#foo)}"); minify_test(".foo { stroke-dasharray: 4 1 2; }", ".foo{stroke-dasharray:4 1 2}"); minify_test(".foo { stroke-dasharray: 4,1,2; }", ".foo{stroke-dasharray:4 1 2}"); minify_test(".foo { stroke-dasharray: 4, 1, 2; }", ".foo{stroke-dasharray:4 1 2}"); minify_test( ".foo { stroke-dasharray: 4px, 1px, 2px; }", ".foo{stroke-dasharray:4 1 2}", ); minify_test(".foo { mask: url('foo.svg'); }", ".foo{mask:url(foo.svg)}"); minify_test( ".foo { mask: url(masks.svg#star) luminance }", ".foo{mask:url(masks.svg#star) luminance}", ); minify_test( ".foo { mask: url(masks.svg#star) 40px 20px }", ".foo{mask:url(masks.svg#star) 40px 20px}", ); minify_test( ".foo { mask: url(masks.svg#star) 0 0 / 50px 50px }", ".foo{mask:url(masks.svg#star) 0 0/50px 50px}", ); minify_test( ".foo { mask: url(masks.svg#star) repeat-x }", ".foo{mask:url(masks.svg#star) repeat-x}", ); minify_test( ".foo { mask: url(masks.svg#star) stroke-box }", ".foo{mask:url(masks.svg#star) stroke-box}", ); minify_test( ".foo { mask: url(masks.svg#star) stroke-box stroke-box }", ".foo{mask:url(masks.svg#star) stroke-box}", ); minify_test( ".foo { mask: url(masks.svg#star) border-box }", ".foo{mask:url(masks.svg#star)}", ); minify_test( ".foo { mask: url(masks.svg#star) left / 16px repeat-y, url(masks.svg#circle) right / 16px repeat-y }", ".foo{mask:url(masks.svg#star) 0/16px repeat-y,url(masks.svg#circle) 100%/16px repeat-y}", ); minify_test( ".foo { mask-border: url('border-mask.png') 25; }", ".foo{mask-border:url(border-mask.png) 25}", ); minify_test( ".foo { mask-border: url('border-mask.png') 25 / 35px / 12px space alpha; }", ".foo{mask-border:url(border-mask.png) 25/35px/12px space}", ); minify_test( ".foo { mask-border: url('border-mask.png') 25 / 35px / 12px space luminance; }", ".foo{mask-border:url(border-mask.png) 25/35px/12px space luminance}", ); minify_test( ".foo { mask-border: url('border-mask.png') luminance 25 / 35px / 12px space; }", ".foo{mask-border:url(border-mask.png) 25/35px/12px space luminance}", ); minify_test( ".foo { clip-path: url('clip.svg#star'); }", ".foo{clip-path:url(clip.svg#star)}", ); minify_test(".foo { clip-path: margin-box; }", ".foo{clip-path:margin-box}"); minify_test( ".foo { clip-path: inset(100px 50px); }", ".foo{clip-path:inset(100px 50px)}", ); minify_test( ".foo { clip-path: inset(100px 50px round 5px); }", ".foo{clip-path:inset(100px 50px round 5px)}", ); minify_test( ".foo { clip-path: inset(100px 50px round 5px 5px 5px 5px); }", ".foo{clip-path:inset(100px 50px round 5px)}", ); minify_test(".foo { clip-path: circle(50px); }", ".foo{clip-path:circle(50px)}"); minify_test( ".foo { clip-path: circle(50px at center center); }", ".foo{clip-path:circle(50px)}", ); minify_test( ".foo { clip-path: circle(50px at 50% 50%); }", ".foo{clip-path:circle(50px)}", ); minify_test( ".foo { clip-path: circle(50px at 0 100px); }", ".foo{clip-path:circle(50px at 0 100px)}", ); minify_test( ".foo { clip-path: circle(closest-side at 0 100px); }", ".foo{clip-path:circle(at 0 100px)}", ); minify_test( ".foo { clip-path: circle(farthest-side at 0 100px); }", ".foo{clip-path:circle(farthest-side at 0 100px)}", ); minify_test( ".foo { clip-path: circle(closest-side at 50% 50%); }", ".foo{clip-path:circle()}", ); minify_test( ".foo { clip-path: ellipse(50px 60px at 0 10% 20%); }", ".foo{clip-path:ellipse(50px 60px at 0 10% 20%)}", ); minify_test( ".foo { clip-path: ellipse(50px 60px at center center); }", ".foo{clip-path:ellipse(50px 60px)}", ); minify_test( ".foo { clip-path: ellipse(closest-side closest-side at 50% 50%); }", ".foo{clip-path:ellipse()}", ); minify_test( ".foo { clip-path: ellipse(closest-side closest-side at 10% 20%); }", ".foo{clip-path:ellipse(at 10% 20%)}", ); minify_test( ".foo { clip-path: ellipse(farthest-side closest-side at 10% 20%); }", ".foo{clip-path:ellipse(farthest-side closest-side at 10% 20%)}", ); minify_test( ".foo { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }", ".foo{clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%)}", ); minify_test( ".foo { clip-path: polygon(nonzero, 50% 0%, 100% 50%, 50% 100%, 0% 50%); }", ".foo{clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%)}", ); minify_test( ".foo { clip-path: polygon(evenodd, 50% 0%, 100% 50%, 50% 100%, 0% 50%); }", ".foo{clip-path:polygon(evenodd,50% 0%,100% 50%,50% 100%,0% 50%)}", ); minify_test( ".foo { clip-path: padding-box circle(50px at 0 100px); }", ".foo{clip-path:circle(50px at 0 100px) padding-box}", ); minify_test( ".foo { clip-path: circle(50px at 0 100px) padding-box; }", ".foo{clip-path:circle(50px at 0 100px) padding-box}", ); minify_test( ".foo { clip-path: circle(50px at 0 100px) border-box; }", ".foo{clip-path:circle(50px at 0 100px)}", ); prefix_test( ".foo { clip-path: circle(50px); }", indoc! { r#" .foo { -webkit-clip-path: circle(50px); clip-path: circle(50px); } "#}, Browsers { chrome: Some(30 << 16), ..Browsers::default() }, ); prefix_test( ".foo { clip-path: circle(50px); }", indoc! { r#" .foo { clip-path: circle(50px); } "#}, Browsers { chrome: Some(80 << 16), ..Browsers::default() }, ); prefix_test( ".foo { clip-path: circle(50px); }", indoc! { r#" .foo { -webkit-clip-path: circle(50px); clip-path: circle(50px); } "#}, Browsers { safari: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { clip-path: circle(50px); }", indoc! { r#" .foo { clip-path: circle(50px); } "#}, Browsers { safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { fill: lch(50.998% 135.363 338) }", indoc! { r#" .foo { fill: #ee00be; fill: color(display-p3 .972962 -.362078 .804206); fill: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { stroke: lch(50.998% 135.363 338) }", indoc! { r#" .foo { stroke: #ee00be; stroke: color(display-p3 .972962 -.362078 .804206); stroke: lch(50.998% 135.363 338); } "#}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { fill: url(#foo) lch(50.998% 135.363 338) }", indoc! { r##" .foo { fill: url("#foo") #ee00be; fill: url("#foo") color(display-p3 .972962 -.362078 .804206); fill: url("#foo") lch(50.998% 135.363 338); } "##}, Browsers { chrome: Some(90 << 16), safari: Some(14 << 16), ..Browsers::default() }, ); prefix_test( ".foo { fill: var(--url) lch(50.998% 135.363 338) }", indoc! { r#" .foo { fill: var(--url) #ee00be; } @supports (color: lab(0% 0 0)) { .foo { fill: var(--url) lab(50.998% 125.506 -50.7078); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { -webkit-mask-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)); -webkit-mask-image: -webkit-linear-gradient(#ff0f0e, #7773ff); -webkit-mask-image: linear-gradient(#ff0f0e, #7773ff); mask-image: linear-gradient(#ff0f0e, #7773ff); -webkit-mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) }", indoc! { r#" .foo { -webkit-mask-image: linear-gradient(#ff0f0e, #7773ff); mask-image: linear-gradient(#ff0f0e, #7773ff); -webkit-mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask-image: linear-gradient(red, green) }", indoc! { r#" .foo { -webkit-mask-image: linear-gradient(red, green); mask-image: linear-gradient(red, green); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo { -webkit-mask-image: url(x.svg); mask-image: url(x.svg); }", indoc! { r#" .foo { -webkit-mask-image: url("x.svg"); mask-image: url("x.svg"); } "#}, Browsers { chrome: Some(95 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 40px 20px }", indoc! { r#" .foo { -webkit-mask: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)) 40px 20px; -webkit-mask: -webkit-linear-gradient(#ff0f0e, #7773ff) 40px 20px; -webkit-mask: linear-gradient(#ff0f0e, #7773ff) 40px 20px; mask: linear-gradient(#ff0f0e, #7773ff) 40px 20px; -webkit-mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 40px 20px; mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 40px 20px; } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask: -webkit-linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 40px 20px }", indoc! { r#" .foo { -webkit-mask: -webkit-gradient(linear, 0 0, 0 100%, from(#ff0f0e), to(#7773ff)) 40px 20px; -webkit-mask: -webkit-linear-gradient(#ff0f0e, #7773ff) 40px 20px; } "#}, Browsers { chrome: Some(8 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 40px var(--foo) }", indoc! { r#" .foo { -webkit-mask: linear-gradient(#ff0f0e, #7773ff) 40px var(--foo); mask: linear-gradient(#ff0f0e, #7773ff) 40px var(--foo); } @supports (color: lab(0% 0 0)) { .foo { -webkit-mask: linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)) 40px var(--foo); mask: linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)) 40px var(--foo); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask: url(masks.svg#star) luminance }", indoc! { r#" .foo { -webkit-mask: url("masks.svg#star"); -webkit-mask-source-type: luminance; mask: url("masks.svg#star") luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( ".foo { mask-image: url(masks.svg#star) }", indoc! { r#" .foo { -webkit-mask-image: url("masks.svg#star"); mask-image: url("masks.svg#star"); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-image: url(masks.svg#star); mask-position: 25% 75%; mask-size: cover; mask-repeat: no-repeat; mask-clip: padding-box; mask-origin: content-box; mask-composite: subtract; mask-mode: luminance; } "#, indoc! { r#" .foo { -webkit-mask: url("masks.svg#star") 25% 75% / cover no-repeat content-box padding-box; -webkit-mask-composite: source-out; -webkit-mask-source-type: luminance; mask: url("masks.svg#star") 25% 75% / cover no-repeat content-box padding-box subtract luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); mask-position: 25% 75%; mask-size: cover; mask-repeat: no-repeat; mask-clip: padding-box; mask-origin: content-box; mask-composite: subtract; mask-mode: luminance; } "#, indoc! { r#" .foo { -webkit-mask: linear-gradient(#ff0f0e, #7773ff) 25% 75% / cover no-repeat content-box padding-box; -webkit-mask-composite: source-out; -webkit-mask-source-type: luminance; mask: linear-gradient(#ff0f0e, #7773ff) 25% 75% / cover no-repeat content-box padding-box subtract luminance; -webkit-mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 25% 75% / cover no-repeat content-box padding-box; -webkit-mask-composite: source-out; -webkit-mask-source-type: luminance; mask: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 25% 75% / cover no-repeat content-box padding-box subtract luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-composite: subtract; } "#, indoc! { r#" .foo { -webkit-mask-composite: source-out; mask-composite: subtract; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-mode: luminance; } "#, indoc! { r#" .foo { -webkit-mask-source-type: luminance; mask-mode: luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border: url('border-mask.png') 25 / 35px / 12px space luminance; } "#, indoc! { r#" .foo { -webkit-mask-box-image: url("border-mask.png") 25 / 35px / 12px space; mask-border: url("border-mask.png") 25 / 35px / 12px space luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 25 / 35px / 12px space luminance; } "#, indoc! { r#" .foo { -webkit-mask-box-image: linear-gradient(#ff0f0e, #7773ff) 25 / 35px / 12px space; mask-border: linear-gradient(#ff0f0e, #7773ff) 25 / 35px / 12px space luminance; -webkit-mask-box-image: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 25 / 35px / 12px space; mask-border: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) 25 / 35px / 12px space luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border-source: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#, indoc! { r#" .foo { -webkit-mask-box-image-source: linear-gradient(#ff0f0e, #7773ff); mask-border-source: linear-gradient(#ff0f0e, #7773ff); -webkit-mask-box-image-source: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); mask-border-source: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border-source: url(foo.png); mask-border-slice: 10 40 10 40; mask-border-width: 10px; mask-border-outset: 0; mask-border-repeat: round round; mask-border-mode: luminance; } "#, indoc! { r#" .foo { -webkit-mask-box-image: url("foo.png") 10 40 / 10px round; mask-border: url("foo.png") 10 40 / 10px round luminance; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { -webkit-mask-box-image-source: url(foo.png); -webkit-mask-box-image-slice: 10 40 10 40; -webkit-mask-box-image-width: 10px; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: round round; } "#, indoc! { r#" .foo { -webkit-mask-box-image: url("foo.png") 10 40 / 10px round; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border-slice: 10 40 10 40; } "#, indoc! { r#" .foo { -webkit-mask-box-image-slice: 10 40; mask-border-slice: 10 40; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border-slice: var(--foo); } "#, indoc! { r#" .foo { -webkit-mask-box-image-slice: var(--foo); mask-border-slice: var(--foo); } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { mask-border: linear-gradient(lch(56.208% 136.76 46.312), lch(51% 135.366 301.364)) var(--foo); } "#, indoc! { r#" .foo { -webkit-mask-box-image: linear-gradient(#ff0f0e, #7773ff) var(--foo); mask-border: linear-gradient(#ff0f0e, #7773ff) var(--foo); } @supports (color: lab(0% 0 0)) { .foo { -webkit-mask-box-image: linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)) var(--foo); mask-border: linear-gradient(lab(56.208% 94.4644 98.8928), lab(51% 70.4544 -115.586)) var(--foo); } } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: mask 200ms; } "#, indoc! { r#" .foo { transition: -webkit-mask .2s, mask .2s; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition: mask-border 200ms; } "#, indoc! { r#" .foo { transition: -webkit-mask-box-image .2s, mask-border .2s; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: mask; } "#, indoc! { r#" .foo { transition-property: -webkit-mask, mask; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: mask-border; } "#, indoc! { r#" .foo { transition-property: -webkit-mask-box-image, mask-border; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); prefix_test( r#" .foo { transition-property: mask-composite, mask-mode; } "#, indoc! { r#" .foo { transition-property: -webkit-mask-composite, mask-composite, -webkit-mask-source-type, mask-mode; } "#}, Browsers { chrome: Some(90 << 16), ..Browsers::default() }, ); } #[test] fn test_filter() { minify_test( ".foo { filter: url('filters.svg#filter-id'); }", ".foo{filter:url(filters.svg#filter-id)}", ); minify_test(".foo { filter: blur(5px); }", ".foo{filter:blur(5px)}"); minify_test(".foo { filter: blur(0px); }", ".foo{filter:blur()}"); minify_test(".foo { filter: brightness(10%); }", ".foo{filter:brightness(10%)}"); minify_test(".foo { filter: brightness(100%); }", ".foo{filter:brightness()}"); minify_test( ".foo { filter: drop-shadow(16px 16px 20px yellow); }", ".foo{filter:drop-shadow(16px 16px 20px #ff0)}", ); minify_test( ".foo { filter: contrast(175%) brightness(3%); }", ".foo{filter:contrast(175%)brightness(3%)}", ); minify_test(".foo { filter: hue-rotate(0) }", ".foo{filter:hue-rotate()}"); prefix_test( ".foo { filter: blur(5px) }", indoc! { r#" .foo { -webkit-filter: blur(5px); filter: blur(5px); } "#}, Browsers { chrome: Some(20 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: blur(5px) }", indoc! { r#" .foo { filter: blur(5px); } "#}, Browsers { chrome: Some(80 << 16), ..Browsers::default() }, ); prefix_test( ".foo { backdrop-filter: blur(5px) }", indoc! { r#" .foo { backdrop-filter: blur(5px); } "#}, Browsers { chrome: Some(80 << 16), ..Browsers::default() }, ); prefix_test( ".foo { backdrop-filter: blur(5px) }", indoc! { r#" .foo { -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px); } "#}, Browsers { safari: Some(15 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: var(--foo) }", indoc! { r#" .foo { -webkit-filter: var(--foo); filter: var(--foo); } "#}, Browsers { chrome: Some(20 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: drop-shadow(16px 16px 20px lab(40% 56.6 39)) }", indoc! { r#" .foo { -webkit-filter: drop-shadow(16px 16px 20px #b32323); filter: drop-shadow(16px 16px 20px #b32323); filter: drop-shadow(16px 16px 20px lab(40% 56.6 39)); } "#}, Browsers { chrome: Some(20 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: contrast(175%) drop-shadow(16px 16px 20px lab(40% 56.6 39)) }", indoc! { r#" .foo { filter: contrast(175%) drop-shadow(16px 16px 20px #b32323); filter: contrast(175%) drop-shadow(16px 16px 20px lab(40% 56.6 39)); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: drop-shadow(16px 16px 20px lab(40% 56.6 39)) drop-shadow(16px 16px 20px yellow) }", indoc! { r#" .foo { filter: drop-shadow(16px 16px 20px #b32323) drop-shadow(16px 16px 20px #ff0); filter: drop-shadow(16px 16px 20px lab(40% 56.6 39)) drop-shadow(16px 16px 20px #ff0); } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); prefix_test( ".foo { filter: var(--foo) drop-shadow(16px 16px 20px lab(40% 56.6 39)) }", indoc! { r#" .foo { filter: var(--foo) drop-shadow(16px 16px 20px #b32323); } @supports (color: lab(0% 0 0)) { .foo { filter: var(--foo) drop-shadow(16px 16px 20px lab(40% 56.6 39)); } } "#}, Browsers { chrome: Some(4 << 16), ..Browsers::default() }, ); } #[test] fn test_viewport() { minify_test( r#" @viewport { width: 100vw; }"#, "@viewport{width:100vw}", ); minify_test( r#" @-ms-viewport { width: device-width; }"#, "@-ms-viewport{width:device-width}", ); } #[test] fn test_custom_media() { custom_media_test( r#" @custom-media --modern (color), (hover); @media (--modern) and (width > 1024px) { .a { color: green; } } "#, indoc! {r#" @media ((color) or (hover)) and (width > 1024px) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @media (--color) and (width > 1024px) { .a { color: green; } } "#, indoc! {r#" @media (color) and (width > 1024px) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --a (color); @custom-media --b (--a); @media (--b) and (width > 1024px) { .a { color: green; } } "#, indoc! {r#" @media (color) and (width > 1024px) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --not-color not (color); @media not (--not-color) { .a { color: green; } } "#, indoc! {r#" @media (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color-print print and (color); @media (--color-print) { .a { color: green; } } "#, indoc! {r#" @media print and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color-print print and (color); @media print and (--color-print) { .a { color: green; } } "#, indoc! {r#" @media print and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --not-color-print not print and (color); @media not print and (--not-color-print) { .a { color: green; } } "#, indoc! {r#" @media not print and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --print print; @media (--print) { .a { color: green; } } "#, indoc! {r#" @media print { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --print print; @media not (--print) { .a { color: green; } } "#, indoc! {r#" @media not print { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --print not print; @media not (--print) { .a { color: green; } } "#, indoc! {r#" @media print { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --print print; @media ((--print)) { .a { color: green; } } "#, indoc! {r#" @media print { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @custom-media --print print; @media (--print) and (--color) { .a { color: green; } } "#, indoc! {r#" @media print and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @custom-media --not-print not print; @media (--not-print) and (--color) { .a { color: green; } } "#, indoc! {r#" @media not print and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @custom-media --screen screen; @custom-media --print print; @media (--print) and (--color), (--screen) and (--color) { .a { color: green; } } "#, indoc! {r#" @media print and (color), screen and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color print and (color), print and (script); @media (--color) { .a { color: green; } } "#, indoc! {r#" @media print and ((color) or (script)) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @custom-media --not-color not all and (--color); @media (--not-color) { .a { color: green; } } "#, indoc! {r#" @media not all and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --color (color); @media not all and (--color) { .a { color: green; } } "#, indoc! {r#" @media not all and (color) { .a { color: green; } } "#}, ); custom_media_test( r#" @media (--print) { .a { color: green; } } @custom-media --print print; "#, indoc! {r#" @media print { .a { color: green; } } "#}, ); custom_media_test( r#" @custom-media --not-width not (min-width: 300px); @media screen and ((prefers-color-scheme: dark) or (--not-width)) { .foo { order: 6; } } "#, indoc! {r#" @media screen and ((prefers-color-scheme: dark) or (not (width >= 300px))) { .foo { order: 6; } } "#}, ); fn custom_media_error_test(source: &str, err: Error) { let mut stylesheet = StyleSheet::parse( &source, ParserOptions { filename: "test.css".into(), custom_media: true, ..ParserOptions::default() }, ) .unwrap(); let res = stylesheet.minify(MinifyOptions { targets: Some(Browsers { chrome: Some(95 << 16), ..Browsers::default() }), ..MinifyOptions::default() }); assert_eq!(res, Err(err)) } custom_media_error_test( r#" @custom-media --color-print print and (color); @media screen and (--color-print) { .a { color: green; } } "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 1, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 3, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --color-print print and (color); @media not print and (--color-print) { .a { color: green; } } "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 1, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 3, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --color-print print and (color); @custom-media --color-screen screen and (color); @media (--color-print) or (--color-screen) {} "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 2, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 4, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --color-print print and (color); @custom-media --color-screen screen and (color); @media (--color-print) and (--color-screen) {} "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 2, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 4, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --screen screen; @custom-media --print print; @media (--print) and (--screen) {} "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 1, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 4, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --not-print not print and (color); @custom-media --not-screen not screen and (color); @media ((script) or ((--not-print) and (--not-screen))) { .a { color: green; } } "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 2, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 4, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --color screen and (color), print and (color); @media (--color) { .a { color: green; } } "#, Error { kind: MinifyErrorKind::UnsupportedCustomMediaBooleanLogic { custom_media_loc: Location { source_index: 0, line: 1, column: 7, }, }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 3, column: 7, }), }, ); custom_media_error_test( r#" @media (--not-defined) { .a { color: green; } } "#, Error { kind: MinifyErrorKind::CustomMediaNotDefined { name: "--not-defined".into(), }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 1, column: 7, }), }, ); custom_media_error_test( r#" @custom-media --circular-mq-a (--circular-mq-b); @custom-media --circular-mq-b (--circular-mq-a); @media (--circular-mq-a) { body { order: 3; } } "#, Error { kind: MinifyErrorKind::CircularCustomMedia { name: "--circular-mq-a".into(), }, loc: Some(ErrorLocation { filename: "test.css".into(), line: 4, column: 7, }), }, ); } #[test] fn test_dependencies() { fn dep_test(source: &str, expected: &str, deps: Vec<(&str, &str)>) { let mut stylesheet = StyleSheet::parse( &source, ParserOptions { filename: "test.css".into(), ..ParserOptions::default() }, ) .unwrap(); stylesheet.minify(MinifyOptions::default()).unwrap(); let res = stylesheet .to_css(PrinterOptions { analyze_dependencies: Some(Default::default()), minify: true, ..PrinterOptions::default() }) .unwrap(); assert_eq!(res.code, expected); let dependencies = res.dependencies.unwrap(); assert_eq!(dependencies.len(), deps.len()); for (i, (url, placeholder)) in deps.into_iter().enumerate() { match &dependencies[i] { Dependency::Url(dep) => { assert_eq!(dep.url, url); assert_eq!(dep.placeholder, placeholder); } Dependency::Import(dep) => { assert_eq!(dep.url, url); assert_eq!(dep.placeholder, placeholder); } } } } fn dep_error_test(source: &str, error: PrinterErrorKind) { let stylesheet = StyleSheet::parse(&source, ParserOptions::default()).unwrap(); let res = stylesheet.to_css(PrinterOptions { analyze_dependencies: Some(Default::default()), ..PrinterOptions::default() }); match res { Err(e) => assert_eq!(e.kind, error), _ => unreachable!(), } } dep_test( ".foo { background: image-set('./img12x.png', './img21x.png' 2x)}", ".foo{background:image-set(\"hXFI8W\" 1x,\"5TkpBa\" 2x)}", vec![("./img12x.png", "hXFI8W"), ("./img21x.png", "5TkpBa")], ); dep_test( ".foo { background: image-set(url(./img12x.png), url('./img21x.png') 2x)}", ".foo{background:image-set(\"hXFI8W\" 1x,\"5TkpBa\" 2x)}", vec![("./img12x.png", "hXFI8W"), ("./img21x.png", "5TkpBa")], ); dep_test( ".foo { --test: url(/foo.png) }", ".foo{--test:url(\"lDnnrG\")}", vec![("/foo.png", "lDnnrG")], ); dep_test( ".foo { --test: url(\"/foo.png\") }", ".foo{--test:url(\"lDnnrG\")}", vec![("/foo.png", "lDnnrG")], ); dep_test( ".foo { --test: url(\"http://example.com/foo.png\") }", ".foo{--test:url(\"3X1zSW\")}", vec![("http://example.com/foo.png", "3X1zSW")], ); dep_test( ".foo { --test: url(\"data:image/svg+xml;utf8,\") }", ".foo{--test:url(\"-vl-rG\")}", vec![("data:image/svg+xml;utf8,", "-vl-rG")], ); dep_test( ".foo { background: url(\"foo.png\") var(--test) }", ".foo{background:url(\"Vwkwkq\") var(--test)}", vec![("foo.png", "Vwkwkq")], ); dep_error_test( ".foo { --test: url(\"foo.png\") }", PrinterErrorKind::AmbiguousUrlInCustomProperty { url: "foo.png".into() }, ); dep_error_test( ".foo { --test: url(foo.png) }", PrinterErrorKind::AmbiguousUrlInCustomProperty { url: "foo.png".into() }, ); dep_error_test( ".foo { --test: url(./foo.png) }", PrinterErrorKind::AmbiguousUrlInCustomProperty { url: "./foo.png".into(), }, ); dep_test( ".foo { behavior: url(#foo) }", ".foo{behavior:url(\"Zn9-2q\")}", vec![("#foo", "Zn9-2q")], ); dep_test( ".foo { --foo: url(#foo) }", ".foo{--foo:url(\"Zn9-2q\")}", vec![("#foo", "Zn9-2q")], ); dep_test( "@import \"test.css\"; .foo { color: red }", "@import \"hHsogW\";.foo{color:red}", vec![("test.css", "hHsogW")], ); } #[test] fn test_api() { let stylesheet = StyleSheet::parse(".foo:hover { color: red }", ParserOptions::default()).unwrap(); match &stylesheet.rules.0[0] { CssRule::Style(s) => { assert_eq!(&s.selectors.to_string(), ".foo:hover"); } _ => unreachable!(), } let color = CssColor::parse_string("#f0f").unwrap(); assert_eq!(color.to_css_string(PrinterOptions::default()).unwrap(), "#f0f"); let rule = CssRule::parse_string(".foo { color: red }", ParserOptions::default()).unwrap(); assert_eq!( rule.to_css_string(PrinterOptions::default()).unwrap(), indoc! {r#" .foo { color: red; }"#} ); let property = Property::parse_string("color".into(), "#f0f", ParserOptions::default()).unwrap(); assert_eq!( property.to_css_string(false, PrinterOptions::default()).unwrap(), "color: #f0f" ); assert_eq!( property.to_css_string(true, PrinterOptions::default()).unwrap(), "color: #f0f !important" ); let code = indoc! { r#" .foo { color: green; } .bar { color: red; background: pink; } @media print { .baz { color: green; } } "#}; let stylesheet = StyleSheet::parse(code, ParserOptions::default()).unwrap(); if let CssRule::Style(style) = &stylesheet.rules.0[1] { let (key, val) = style.property_location(code, 0).unwrap(); assert_eq!( key, SourceLocation { line: 5, column: 3 }..SourceLocation { line: 5, column: 8 } ); assert_eq!( val, SourceLocation { line: 5, column: 10 }..SourceLocation { line: 5, column: 13 } ); } if let CssRule::Style(style) = &stylesheet.rules.0[1] { let (key, val) = style.property_location(code, 1).unwrap(); assert_eq!( key, SourceLocation { line: 6, column: 3 }..SourceLocation { line: 6, column: 13 } ); assert_eq!( val, SourceLocation { line: 6, column: 15 }..SourceLocation { line: 6, column: 19 } ); } if let CssRule::Media(media) = &stylesheet.rules.0[2] { if let CssRule::Style(style) = &media.rules.0[0] { let (key, val) = style.property_location(code, 0).unwrap(); assert_eq!( key, SourceLocation { line: 11, column: 5 }..SourceLocation { line: 11, column: 10 } ); assert_eq!( val, SourceLocation { line: 11, column: 12 }..SourceLocation { line: 11, column: 17 } ); } } let mut property = Property::Transform(Default::default(), VendorPrefix::WebKit); property.set_prefix(VendorPrefix::None); assert_eq!(property, Property::Transform(Default::default(), VendorPrefix::None)); property.set_prefix(VendorPrefix::Moz); assert_eq!(property, Property::Transform(Default::default(), VendorPrefix::Moz)); property.set_prefix(VendorPrefix::WebKit | VendorPrefix::Moz); assert_eq!( property, Property::Transform(Default::default(), VendorPrefix::WebKit | VendorPrefix::Moz) ); let mut property = Property::TextDecorationLine(Default::default(), VendorPrefix::None); property.set_prefix(VendorPrefix::Ms); assert_eq!( property, Property::TextDecorationLine(Default::default(), VendorPrefix::None) ); property.set_prefix(VendorPrefix::WebKit | VendorPrefix::Moz | VendorPrefix::Ms); assert_eq!( property, Property::TextDecorationLine(Default::default(), VendorPrefix::WebKit | VendorPrefix::Moz) ); let mut property = Property::AccentColor(Default::default()); property.set_prefix(VendorPrefix::WebKit); assert_eq!(property, Property::AccentColor(Default::default())); } #[cfg(feature = "substitute_variables")] #[test] fn test_substitute_vars() { use crate::properties::custom::TokenList; use crate::traits::ParseWithOptions; fn test(property: Property, vars: HashMap<&str, &str>, expected: &str) { if let Property::Unparsed(unparsed) = property { let vars = vars .into_iter() .map(|(k, v)| { ( k, TokenList::parse_string_with_options(v, ParserOptions::default()).unwrap(), ) }) .collect(); let substituted = unparsed.substitute_variables(&vars).unwrap(); assert_eq!( substituted.to_css_string(false, PrinterOptions::default()).unwrap(), expected ); } else { panic!("Not an unparsed property"); } } let property = Property::parse_string("color".into(), "var(--test)", ParserOptions::default()).unwrap(); test(property, HashMap::from([("--test", "yellow")]), "color: #ff0"); let property = Property::parse_string("color".into(), "var(--test, var(--foo))", ParserOptions::default()).unwrap(); test(property, HashMap::from([("--foo", "yellow")]), "color: #ff0"); let property = Property::parse_string( "color".into(), "var(--test, var(--foo, yellow))", ParserOptions::default(), ) .unwrap(); test(property, HashMap::new(), "color: #ff0"); let property = Property::parse_string("width".into(), "calc(var(--a) + var(--b))", ParserOptions::default()).unwrap(); test(property, HashMap::from([("--a", "2px"), ("--b", "4px")]), "width: 6px"); let property = Property::parse_string("color".into(), "var(--a)", ParserOptions::default()).unwrap(); test( property, HashMap::from([("--a", "var(--b)"), ("--b", "yellow")]), "color: #ff0", ); let property = Property::parse_string("color".into(), "var(--a)", ParserOptions::default()).unwrap(); test( property, HashMap::from([("--a", "var(--b)"), ("--b", "var(--c)"), ("--c", "var(--a)")]), "color: var(--a)", ); } #[test] fn test_layer() { minify_test("@layer foo;", "@layer foo;"); minify_test("@layer foo, bar;", "@layer foo,bar;"); minify_test("@layer foo.bar;", "@layer foo.bar;"); minify_test("@layer foo.bar, baz;", "@layer foo.bar,baz;"); minify_test( r#" @layer foo { .bar { color: red; } } "#, "@layer foo{.bar{color:red}}", ); minify_test( r#" @layer foo.bar { .bar { color: red; } } "#, "@layer foo.bar{.bar{color:red}}", ); minify_test(r#" @layer base { p { max-width: 70ch; } } @layer framework { @layer base { p { margin-block: 0.75em; } } @layer theme { p { color: #222; } } } "#, "@layer base{p{max-width:70ch}}@layer framework{@layer base{p{margin-block:.75em}}@layer theme{p{color:#222}}}"); minify_test( r#" @layer { .bar { color: red; } } "#, "@layer{.bar{color:red}}", ); minify_test( r#" @layer foo\20 bar, baz; "#, "@layer foo\\ bar,baz;", ); minify_test( r#" @layer one.two\20 three\#four\.five { .bar { color: red; } } "#, "@layer one.two\\ three\\#four\\.five{.bar{color:red}}", ); error_test("@layer;", ParserError::UnexpectedToken(Token::Semicolon)); error_test("@layer foo, bar {};", ParserError::AtRuleBodyInvalid); minify_test("@import 'test.css' layer;", "@import \"test.css\" layer;"); minify_test("@import 'test.css' layer(foo);", "@import \"test.css\" layer(foo);"); minify_test( "@import 'test.css' layer(foo.bar);", "@import \"test.css\" layer(foo.bar);", ); minify_test( "@import 'test.css' layer(foo\\20 bar);", "@import \"test.css\" layer(foo\\ bar);", ); error_test( "@import 'test.css' layer(foo, bar) {};", ParserError::UnexpectedToken(Token::Comma), ); minify_test( r#" @layer one { body { background: red; } } body { background: red; } @layer two { body { background: green; } } @layer one { body { background: yellow; } } "#, "@layer one{body{background:#ff0}}body{background:red}@layer two{body{background:green}}", ); } #[test] fn test_property() { minify_test( r#" @property --property-name { syntax: ''; inherits: false; initial-value: yellow; } "#, "@property --property-name{syntax:\"\";inherits:false;initial-value:#ff0}", ); minify_test( r#" @property --property-name { syntax: ''; inherits: true; initial-value: 25px; } "#, "@property --property-name{syntax:\"\";inherits:true;initial-value:25px}", ); error_test( r#" @property --property-name { syntax: ''; inherits: false; initial-value: 25px; } "#, ParserError::UnexpectedToken(crate::properties::custom::Token::Dimension { has_sign: false, value: 25.0, int_value: Some(25), unit: "px".into(), }), ); error_test( r#" @property --property-name { syntax: ''; inherits: false; initial-value: var(--some-value); } "#, ParserError::UnexpectedToken(crate::properties::custom::Token::Function("var".into())), ); error_test( r#" @property --property-name { syntax: ''; inherits: false; } "#, ParserError::AtRuleBodyInvalid, ); minify_test( r#" @property --property-name { syntax: '*'; inherits: false; } "#, "@property --property-name{syntax:\"*\";inherits:false}", ); error_test( r#" @property --property-name { syntax: '*'; } "#, ParserError::AtRuleBodyInvalid, ); error_test( r#" @property --property-name { inherits: false; } "#, ParserError::AtRuleBodyInvalid, ); error_test( r#" @property property-name { syntax: '*'; inherits: false; } "#, ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("property-name".into())), ); minify_test( r#" @property --property-name { syntax: 'custom | '; inherits: false; initial-value: yellow; } "#, "@property --property-name{syntax:\"custom|\";inherits:false;initial-value:#ff0}", ); minify_test(r#" @property --property-name { syntax: ''; inherits: false; initial-value: translate(200px,300px) translate(100px,200px) scale(2); } "#, "@property --property-name{syntax:\"\";inherits:false;initial-value:matrix(2,0,0,2,300,500)}"); minify_test( r#" @property --property-name { syntax: '