diff --git a/.gitignore b/.gitignore index 4f44a6d5..e82b6873 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store *.node node_modules/ target/ @@ -6,4 +7,4 @@ dist/ .parcel-cache node/*.flow artifacts -npm +npm \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 016a709c..17365b1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19572,6 +19572,61 @@ mod tests { "@container my-layout (inline-size>45em){.foo{color:red}}", ); + minify_test( + r#" + @container my-layout ( not (width > 500px) ) { + .foo { + color: red; + } + } + "#, + "@container my-layout (not (width>500px)){.foo{color:red}}", + ); + + minify_test( + r#" + @container my-layout not (width > 500px) { + .foo { + color: red; + } + } + "#, + "@container my-layout not (width>500px){.foo{color:red}}", + ); + + minify_test( + r#" + @container not (width > 500px) { + .foo { + color: red; + } + } + "#, + "@container not (width>500px){.foo{color:red}}", + ); + + minify_test( + r#" + @container my-layout ((width: 100px) and (not (height: 100px))) { + .foo { + color: red; + } + } + "#, + "@container my-layout ((width:100px) and (not (height:100px))){.foo{color:red}}", + ); + + minify_test( + r#" + @container my-layout (width = max(10px, 10em)) { + .foo { + color: red; + } + } + "#, + "@container my-layout (width=max(10px,10em)){.foo{color:red}}", + ); + // without name minify_test( r#" @@ -19595,6 +19650,29 @@ mod tests { "@container (inline-size>45em) and (inline-size<100em){.foo{color:red}}", ); + // calc() + minify_test( + r#" + @container (width > calc(100vw - 50px)) { + .foo { + color: red; + } + } + "#, + "@container (width>calc(100vw - 50px)){.foo{color:red}}", + ); + + minify_test( + r#" + @container (calc(100vh - 50px) <= height ) { + .foo { + color: red; + } + } + "#, + "@container (height>=calc(100vh - 50px)){.foo{color:red}}", + ); + // merge adjacent minify_test( r#" @@ -19652,5 +19730,80 @@ mod tests { minify_test(".foo { width: calc(1cqb + 2cqb) }", ".foo{width:3cqb}"); minify_test(".foo { width: calc(1cqmin + 2cqmin) }", ".foo{width:3cqmin}"); minify_test(".foo { width: calc(1cqmax + 2cqmax) }", ".foo{width:3cqmax}"); + + // Unlike in @media, there is no need to convert the range syntax in @container, + // because browsers all support this syntax. + prefix_test( + r#" + @container (width > 100px) { + .foo { padding: 5px; } + } + "#, + indoc! { r#" + @container (width > 100px) { + .foo { + padding: 5px; + } + } + "#}, + Browsers { + chrome: Some(105 << 16), + ..Browsers::default() + }, + ); + prefix_test( + r#" + @container (min-width: 100px) { + .foo { padding: 5px; } + } + "#, + indoc! { r#" + @container (min-width: 100px) { + .foo { + padding: 5px; + } + } + "#}, + Browsers { + chrome: Some(105 << 16), + ..Browsers::default() + }, + ); + + // Disallow 'none', 'not', 'and', 'or' as a `` + // https://github.com/w3c/csswg-drafts/issues/7203#issuecomment-1144257312 + // https://chromium-review.googlesource.com/c/chromium/src/+/3698402 + error_test( + "@container none (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("none".into())), + ); + + error_test( + "@container and (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("and".into())), + ); + + error_test( + "@container or (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("or".into())), + ); + + // Disallow CSS wide keywords as a `` + error_test( + "@container revert-layer (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("revert-layer".into())), + ); + + error_test( + "@container initial (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("initial".into())), + ); + + // contains spaces + // https://github.com/web-platform-tests/wpt/blob/39f0da08fbbe33d0582a35749b6dbf8bd067a52d/css/css-contain/container-queries/at-container-parsing.html#L160-L178 + error_test( + "@container foo bar (width < 100vw) {}", + ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("bar".into())), + ); } } diff --git a/src/rules/container.rs b/src/rules/container.rs index ad6eadf4..c3a71fc1 100644 --- a/src/rules/container.rs +++ b/src/rules/container.rs @@ -1,4 +1,4 @@ -//! The `@media` rule. +//! The `@container` rule. use cssparser::*; @@ -76,7 +76,13 @@ impl<'a, 'i> ToCssWithContext<'a, 'i> for ContainerRule<'i> { name.to_css(dest)?; dest.write_char(' ')?; } + + // Don't downlevel range syntax in container queries. + let mut targets = None; + std::mem::swap(&mut targets, &mut dest.targets); self.condition.to_css(dest)?; + std::mem::swap(&mut targets, &mut dest.targets); + dest.whitespace()?; dest.write_char('{')?; dest.indent();