From 3ad9dd4945227d4aaa521ecb3747ca42c244740c Mon Sep 17 00:00:00 2001 From: Jason Finch Date: Fri, 27 Mar 2026 14:19:21 +1000 Subject: [PATCH] Fix 0% losing its percent unit during CSS serialization CssLengthValue.CssText unconditionally stripped all units when the numeric value was zero. Per CSS spec, 0 is valid without a unit for values, but values must always include the % sign since 0% and 0 have different semantic meanings (e.g. flex-basis). --- .../Declarations/CssFlexProperty.cs | 30 +++++++++++++++++++ .../Declarations/CssObjectSizing.cs | 2 +- .../Declarations/CssTransformProperty.cs | 8 ++--- .../Values/Primitives/CssLengthValue.cs | 4 ++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs index 624113ed..16268a4f 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs @@ -95,6 +95,36 @@ public void CssFlexShorthandGrowAndShrinkLegal() Assert.AreEqual("1 2", property.Value); } + [Test] + public void CssFlexShorthandWithZeroPercentBasisLegal() + { + var snippet = "flex: 1 1 0%"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("1 1 0%", property.Value); + } + + [Test] + public void CssFlexBasisZeroPercentLegal() + { + var snippet = "flex-basis: 0%"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex-basis", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("0%", property.Value); + } + + [Test] + public void CssFlexBasisZeroPxSerializesWithoutUnit() + { + var snippet = "flex-basis: 0px"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex-basis", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("0", property.Value); + } + [Test] public void CssFlexWrapLegal() { diff --git a/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs b/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs index 300312dd..d46c868f 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs @@ -131,7 +131,7 @@ public void CssObjectPositionLeft30Legal() Assert.IsTrue(property.IsAnimatable); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 30px", property.Value); + Assert.AreEqual("0% 30px", property.Value); } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs index c86216d5..2d22a627 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs @@ -230,7 +230,7 @@ public void CssTransformOriginYOffsetXKeywordLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px", property.Value); + Assert.AreEqual("0% 2px", property.Value); } [Test] @@ -242,7 +242,7 @@ public void CssTransformOriginXKeywordYOffsetLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px", property.Value); + Assert.AreEqual("0% 2px", property.Value); } [Test] @@ -290,7 +290,7 @@ public void CssTransformOriginYXKeywordZLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px 10px", property.Value); + Assert.AreEqual("0% 2px 10px", property.Value); } [Test] @@ -302,7 +302,7 @@ public void CssTransformOriginXKeywordYZLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 5px -3px", property.Value); + Assert.AreEqual("0% 5px -3px", property.Value); } [Test] diff --git a/src/AngleSharp.Css/Values/Primitives/CssLengthValue.cs b/src/AngleSharp.Css/Values/Primitives/CssLengthValue.cs index cffee8d3..f63e9ce6 100644 --- a/src/AngleSharp.Css/Values/Primitives/CssLengthValue.cs +++ b/src/AngleSharp.Css/Values/Primitives/CssLengthValue.cs @@ -107,7 +107,9 @@ public String CssText } else { - var unit = _value == 0.0 ? String.Empty : UnitString; + // Per CSS spec, 0 is valid without a unit for lengths, + // but 0% is a percentage (relative to context) and must keep its unit. + var unit = _value == 0.0 && _unit != Unit.Percent ? String.Empty : UnitString; var val = _value.CssStringify(); return String.Concat(val, unit); }