Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 229 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7685,6 +7685,233 @@ mod tests {
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)}");

// Test <integer> is out of the `i32` range
minify_test(".z-index { z-index: 99999988888 }", ".z-index{z-index:calc(1/0)}");
minify_test(".z-index { z-index: -99999988888 }", ".z-index{z-index:calc(-1/0)}");

// Test <integer> in calc()
minify_test(".z-index1 { z-index: calc(10 / 5) }", ".z-index1{z-index:2}");
minify_test(".z-index2 { z-index: calc(-10 / 5) }", ".z-index2{z-index:-2}");
minify_test(".z-index3 { z-index: calc(10 / 3) }", ".z-index3{z-index:3}");
minify_test(".z-index4 { z-index: calc(11 / 3) }", ".z-index4{z-index:4}");
minify_test(".z-index5 { z-index: calc(-11 / 3) }", ".z-index5{z-index:-4}");
minify_test(".order1 { order: calc(5 * 2 - 1) }", ".order1{order:9}");
minify_test(".order2 { order: calc( calc(8 - 2) / -2 ) }", ".order2{order:-3}");
minify_test(".order3 { order: calc(-6 + calc(2 * 3)) }", ".order3{order:0}");
minify_test(".order4 { order: calc(0) }", ".order4{order:0}");
minify_test(".order5 { order: calc(-0) }", ".order5{order:0}");
minify_test(".order6 { order: calc(0.5) }", ".order6{order:1}");
Copy link
Copy Markdown
Contributor Author

@yisibl yisibl Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: What happens when Chrome uses calc(decimal) before implementing this feature?
https://chromestatus.com/feature/5656451751084032

minify_test(".order7 { order: calc(-5 * 0) }", ".order7{order:0}");

// Test Infinity <integer>
// i32: calc(1/0) = 2147483647 = 2^31 - 1
// i32: calc(-1/0) = -2147483648 = -2^31
// Spec: https://drafts.csswg.org/css-values-4/#calc-ieee
minify_test(".infinity1 { z-index: calc(9/0) }", ".infinity1{z-index:calc(1/0)}");
minify_test(
".infinity2 { z-index: calc(0.0002/0) }",
".infinity2{z-index:calc(1/0)}",
);
minify_test(".infinity3 { z-index: calc(-1/0) }", ".infinity3{z-index:calc(-1/0)}");
minify_test(
".infinity3-1 { z-index: calc(1/-0) }",
".infinity3-1{z-index:calc(-1/0)}",
);
minify_test(".infinity3-2 { z-index: calc(0/-0) }", ".infinity3-2{z-index:0}");
minify_test(".infinity3-3 { z-index: calc(-0/-0) }", ".infinity3-3{z-index:0}");
minify_test(
".infinity3-4 { z-index: calc(-0/0) }", // NaN
".infinity3-4{z-index:0}",
);
minify_test(
".infinity3-5 { z-index: calc(-1/-0) }",
".infinity3-5{z-index:calc(1/0)}",
);
minify_test(
".infinity4 { z-index: calc(1 * infinity) }",
".infinity4{z-index:calc(1/0)}",
);
minify_test(
".infinity5 { z-index: calc(-1 * infinity) }",
".infinity5{z-index:calc(-1/0)}",
);
minify_test(".infinity6 { z-index: calc(1 / -infinity) }", ".infinity6{z-index:0}");
minify_test(
".infinity7 { z-index: calc(1 - infinity) }",
".infinity7{z-index:calc(-1/0)}",
);
minify_test(
".infinity8 { z-index: calc(infinity - infinity) }",
".infinity8{z-index:0}",
);
minify_test(
".infinity9 { z-index: calc(1 / calc(-5 * 0)) }",
".infinity9{z-index:calc(-1/0)}",
);
minify_test(
".infinity10 { z-index: calc(infinity + infinity) }",
".infinity10{z-index:calc(1/0)}",
);

minify_test(
".infinity11-0 { z-index: calc(2147483 - 1) }",
".infinity11-0{z-index:2147482}",
);
// TODO: Using the double type (f64) in Chrome can prevent precision loss.
// We currently align with Firefox, 2147483647 - 65 = 2147483520
minify_test(
".infinity11-1 { z-index: calc(2147483647 - 65) }",
".infinity11-1{z-index:2147483520}", // Chrome: 2147483582, Firefox: 2147483520
);
minify_test(
".infinity11-2 { z-index: calc(-2147483647 + 1) }",
".infinity11-2{z-index:calc(-1/0)}", // Chrome: -2147483646, Firefox: -2147483648
);
minify_test(
".infinity11-3 { z-index: calc(2147483647 + 1) }",
".infinity11-3{z-index:calc(1/0)}",
);
minify_test(
".infinity11-4 { z-index: calc(-2147483647 - 1) }",
".infinity11-4{z-index:calc(-1/0)}",
);
minify_test(
".infinity11-5 { z-index: calc(2147483646 + infinity) }",
".infinity11-5{z-index:calc(1/0)}",
);
minify_test(
".infinity11-6 { z-index: calc(2147483647 + 2 - 1) }",
".infinity11-6{z-index:calc(1/0)}",
);
minify_test(
".infinity11-7 { z-index: calc(1 - 2147483649) }",
".infinity11-7{z-index:calc(-1/0)}", // Negative overflow: 1 + (-2147483649) = -2147483648
);
minify_test(
".infinity11-8 { z-index: 2147483647 }",
".infinity11-8{z-index:calc(1/0)}",
);
minify_test(
".infinity12-1 { z-index: calc(calc(1/0) + infinity) }",
".infinity12-1{z-index:calc(1/0)}",
);
minify_test(
".infinity12-2 { z-index: calc(calc(1/0) - infinity) }",
".infinity12-2{z-index:0}",
);

minify_test(
".infinity-order-1 { order: calc(infinity / infinity) }",
".infinity-order-1{order:0}",
);
minify_test(
".infinity-order-2 { order: calc(infinity / 0) }",
".infinity-order-2{order:calc(1/0)}",
);
minify_test(
".infinity-order-3 { order: calc(infinity + 888) }",
".infinity-order-3{order:calc(1/0)}",
);
minify_test(
".infinity-order-4 { order: calc(infinity - 888) }",
".infinity-order-4{order:calc(1/0)}",
);
minify_test(
".infinity-steps-1 { transition-timing-function: steps(calc(infinity), jump-start) }",
".infinity-steps-1{transition-timing-function:steps(calc(1/0),start)}",
);
minify_test(
".infinity-steps-2 { transition: steps(calc(infinity), jump-start) }",
".infinity-steps-2{transition:all steps(calc(1/0),start)}",
);

// Test Infinity <length> - division by zero should preserve sign
minify_test(
".infinity-dim1 { width: calc(100px / 0) }",
".infinity-dim1{width:calc(100px/0)}",
);
minify_test(
".infinity-dim2 { width: calc(100px / -0) }",
".infinity-dim2{width:calc(100px/-0)}",
);
minify_test(
".infinity-dim3 { width: calc(-100px / 0) }",
".infinity-dim3{width:calc(-100px/0)}",
);
minify_test(
".infinity-dim4 { width: calc(-100px / -0) }",
".infinity-dim4{width:calc(-100px/-0)}",
);
minify_test(
".infinity-dim5 { width: calc(-0px); height: calc(-0); }",
".infinity-dim5{width:0;height:0}",
);

// Test Infinity <number>
minify_test(".number1 { line-height: calc(9/0) }", ".number1{line-height:calc(1/0)}");
minify_test(
".number2 { line-height: calc(0.0002/0) }",
".number2{line-height:calc(1/0)}",
);
minify_test(
".number3 { line-height: calc(-1/0) }",
".number3{line-height:calc(-1/0)}",
);
minify_test(
".number3-1 { line-height: calc(1/-0) }",
".number3-1{line-height:calc(-1/0)}",
);
minify_test(".number3-1 { flex: calc(0/-0) }", ".number3-1{flex:0}");
minify_test(".number4 { flex: calc(1 * infinity) }", ".number4{flex:calc(1/0)}");
minify_test(".number5 { flex: calc(-1 * infinity) }", ".number5{flex:calc(-1/0)}");

// TODO Support orphans prop
// Test orphans = <integer [1,∞]>
// minify_test(".orphans1 { orphans: calc(5 + 0.3) }", ".orphans1{orphans:5}");
// minify_test(".orphans2 { orphans: calc(10 / 3) }", ".orphans2{orphans:3}");
// minify_test(".orphans3 { orphans: calc(0.3) }", ".orphans3{orphans:1}");
// minify_test(
// ".orphans1 { orphans: calc(-0.5) }",
// ".orphans1{orphans:1}",
// );
// minify_test(
// ".orphans2 { orphans: calc(-.5 * 2) }",
// ".orphans2{orphans:-1}",
// );
// minify_test(
// ".orphans4 { orphans: calc(5 - 10) }",
// ".orphans4{orphans:-5}",
// );

// Test infinity repeat() = <integer [1,∞]>
minify_test(
".grid1 { grid-template-rows: repeat( calc(0.5), 1fr ); }",
".grid1{grid-template-rows:repeat(1,1fr)}",
);
minify_test(
".grid2 { grid-template-rows: repeat( calc(10 / 3), 1fr ); }",
".grid2{grid-template-rows:repeat(3,1fr)}",
);
minify_test(
".grid3 { grid-template-rows: repeat( calc(0.5 * 3), 200px ); }",
".grid3{grid-template-rows:repeat(2,200px)}",
);
minify_test(
".grid-mix1 { grid-template-rows: repeat( calc(0.5 * 5 - 1), minmax(calc(100px * 2), 1fr) ); }",
".grid-mix1{grid-template-rows:repeat(2,minmax(200px,1fr))}",
);
// end infinity

minify_test(".mix1 { z-index: calc(10 + 5 * 2 - 3) }", ".mix1{z-index:17}");
minify_test(".mix2 { z-index: calc(10 * 2 + 5) }", ".mix2{z-index:25}");
minify_test(".mix3 { z-index: calc(10 * 2 - 5) }", ".mix3{z-index:15}");
minify_test(".mix4 { z-index: calc((10 + 5) * 2) }", ".mix4{z-index:30}");
minify_test(".mix5 { order: calc(100 - 50 + 25 - 10) }", ".mix5{order:65}");
minify_test(".mix6 { z-index: calc(2 * 3 * 4) }", ".mix6{z-index:24}");
minify_test(".mix7 { z-index: calc(2 * 3 + 4 * 5) }", ".mix7{z-index:26}");
minify_test(".mix8 { z-index: calc(2 * (3 + 4)) }", ".mix8{z-index:14}");

minify_test(
".foo { width: calc(100% - 30px + 20px) }",
".foo{width:calc(100% - 10px)}",
Expand Down Expand Up @@ -7772,7 +7999,8 @@ mod tests {
".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 { margin: calc(500px/0) }", ".foo{margin:calc(500px/0)}");
minify_test(".foo { margin: calc(-500px/0) }", ".foo{margin: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}");
Expand Down
3 changes: 3 additions & 0 deletions src/properties/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ impl FromStandard<CSSInteger> for BoxOrdinalGroup {
}
}

/// A value for the legacy (prefixed) [box-flex-group](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#box-flex-group) property.
pub type BoxFlexGroup = CSSInteger;

// Old flex (2012): https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/

enum_property! {
Expand Down
2 changes: 1 addition & 1 deletion src/properties/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,7 @@ impl<'i> Parse<'i> for GridLine<'i> {
let ident = input.try_parse(CustomIdent::parse).ok();
(line_number, ident)
} else if let Ok(ident) = input.try_parse(CustomIdent::parse) {
let line_number = input.try_parse(CSSInteger::parse).unwrap_or(1);
let line_number = input.try_parse(CSSInteger::parse).unwrap_or(CSSInteger(1));
(line_number, Some(ident))
} else {
return Err(input.new_custom_error(ParserError::InvalidDeclaration));
Expand Down
28 changes: 26 additions & 2 deletions src/properties/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl ToCss for Position {
}

/// A value for the [z-index](https://drafts.csswg.org/css2/#z-index) property.
#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
Expand All @@ -85,10 +85,34 @@ impl ToCss for Position {
pub enum ZIndex {
/// The `auto` keyword.
Auto,
/// An integer value.
/// An integer value (supports infinity via calc()).
Integer(CSSInteger),
}

impl<'i> Parse<'i> for ZIndex {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() {
return Ok(ZIndex::Auto);
}

// Use CSSInteger::parse which handles calc() and infinity
let integer = CSSInteger::parse(input)?;
Ok(ZIndex::Integer(integer))
}
}

impl ToCss for ZIndex {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
ZIndex::Auto => dest.write_str("auto"),
ZIndex::Integer(v) => v.to_css(dest),
}
}
}

#[derive(Default)]
pub(crate) struct PositionHandler {
position: Option<Position>,
Expand Down
8 changes: 4 additions & 4 deletions src/rules/font_palette_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl<'i> Parse<'i> for BasePalette {
if i.is_negative() {
return Err(input.new_custom_error(ParserError::InvalidValue));
}
return Ok(BasePalette::Integer(i as u16));
return Ok(BasePalette::Integer(*i as u16));
}

let location = input.current_source_location();
Expand All @@ -208,7 +208,7 @@ impl ToCss for BasePalette {
match self {
BasePalette::Light => dest.write_str("light"),
BasePalette::Dark => dest.write_str("dark"),
BasePalette::Integer(i) => (*i as CSSInteger).to_css(dest),
BasePalette::Integer(i) => CSSInteger(i32::from(*i)).to_css(dest),
}
}
}
Expand All @@ -226,7 +226,7 @@ impl<'i> Parse<'i> for OverrideColors {
}

Ok(OverrideColors {
index: index as u16,
index: (*index) as u16,
color,
})
}
Expand All @@ -237,7 +237,7 @@ impl ToCss for OverrideColors {
where
W: std::fmt::Write,
{
(self.index as CSSInteger).to_css(dest)?;
CSSInteger(self.index as i32).to_css(dest)?;
dest.write_char(' ')?;
self.color.to_css(dest)
}
Expand Down
Loading
Loading