From 4b702930eb0bf03f69fdbe565f8d255753e0e742 Mon Sep 17 00:00:00 2001 From: Justin Haygood Date: Wed, 22 Jul 2026 17:35:32 -0400 Subject: [PATCH] Keep layers comma-separated when exporting a multi-layer shorthand EndListValueConverter.ListValue.ExtractFor joined the per-layer values with whitespace while its own CssText, and ListValueConverter's equivalent ExtractFor, both join with a comma. Exporting a multi-layer shorthand to its longhands therefore ran the layers together into one invalid value instead of a comma-separated list: background: url(a.png) no-repeat, url(b.png) repeat background-repeat -> "no-repeat repeat" (one invalid layer) background-image -> "initial" (conversion failed outright) Use Token.Comma, matching the sibling converter. --- .../PropertyTests/BackgroundProperty.cs | 21 +++++++++++++++++++ .../ValueConverters/EndListValueConverter.cs | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ExCSS.Tests/PropertyTests/BackgroundProperty.cs b/src/ExCSS.Tests/PropertyTests/BackgroundProperty.cs index 4b0ea2c1..44d1bfe9 100644 --- a/src/ExCSS.Tests/PropertyTests/BackgroundProperty.cs +++ b/src/ExCSS.Tests/PropertyTests/BackgroundProperty.cs @@ -736,5 +736,26 @@ public void BackgroundImageDataUrlLegal() Assert.True(concrete.HasValue); Assert.Equal("url(\"" + url + "\")", concrete.Value); } + + [Fact] + public void BackgroundMultipleLayersExportCommaSeparatedLonghands() + { + // Each comma-separated layer must stay its own layer when the shorthand is exported to the + // longhands; joining them with whitespace produces one invalid layer instead of two valid ones. + var style = ParseDeclarations("background: url(a.png) no-repeat, url(b.png) repeat"); + + Assert.Equal("url(\"a.png\"), url(\"b.png\")", style.BackgroundImage); + Assert.Equal("no-repeat, repeat", style.BackgroundRepeat); + } + + [Fact] + public void BackgroundThreeLayersExportCommaSeparatedLonghands() + { + var style = ParseDeclarations( + "background: url(a.png) no-repeat, url(b.png) repeat-x, url(c.png) repeat"); + + Assert.Equal("url(\"a.png\"), url(\"b.png\"), url(\"c.png\")", style.BackgroundImage); + Assert.Equal("no-repeat, repeat-x, repeat", style.BackgroundRepeat); + } } } diff --git a/src/ExCSS/ValueConverters/EndListValueConverter.cs b/src/ExCSS/ValueConverters/EndListValueConverter.cs index 93415cef..5a6fd650 100644 --- a/src/ExCSS/ValueConverters/EndListValueConverter.cs +++ b/src/ExCSS/ValueConverters/EndListValueConverter.cs @@ -92,7 +92,11 @@ public TokenValue ExtractFor(string name) if (extracted == null) continue; - if (tokens.Count > 0) tokens.Add(Token.Whitespace); + // Layers stay comma-separated when exported to a longhand, matching CssText above and + // ListValueConverter's own ExtractFor. Joining with whitespace instead ran the layers + // together: "background: url(a) no-repeat, url(b) repeat" gave a background-repeat of + // "no-repeat repeat", which is a single invalid layer rather than two valid ones. + if (tokens.Count > 0) tokens.Add(Token.Comma); tokens.AddRange(extracted); }