From f2c967034adddaea532091d89d9847267d3afdcd Mon Sep 17 00:00:00 2001 From: Justin Haygood Date: Wed, 22 Jul 2026 18:31:07 -0400 Subject: [PATCH] Register the ::marker pseudo-element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ::marker is a standard pseudo-element (CSS Lists 3 6.1), but it was absent from PseudoElementSelectorFactory's registered set, so a rule that used it - "li::marker { … }" - failed to parse and was dropped whole rather than producing a PseudoElementSelector. Add it to the factory (and PseudoElementNames). Only the two-colon form is registered; unlike ::before/::after, ::marker has no one-colon legacy spelling. --- src/ExCSS.Tests/SelectorsTests.cs | 25 +++++++++++++++++++ src/ExCSS/Enumerations/PseudoElementNames.cs | 1 + .../Factories/PseudoElementSelectorFactory.cs | 1 + 3 files changed, 27 insertions(+) diff --git a/src/ExCSS.Tests/SelectorsTests.cs b/src/ExCSS.Tests/SelectorsTests.cs index 71013a56..43400909 100644 --- a/src/ExCSS.Tests/SelectorsTests.cs +++ b/src/ExCSS.Tests/SelectorsTests.cs @@ -146,6 +146,31 @@ public void NthChildRejectsMalformedSpacedOffset(string selector) Assert.Equal(0, sheet.Rules.Length); } + [Theory] + // ::marker is a standard pseudo-element (CSS Lists 3 6.1). It was unregistered, so a rule using it + // was rejected wholesale rather than parsed. Only the two-colon form exists - unlike ::before/::after, + // ::marker has no one-colon legacy spelling. + [InlineData("li::marker", "li::marker")] + [InlineData("::marker", "::marker")] + public void MarkerPseudoElementIsParsed(string selector, string expectedText) + { + var sheet = new StylesheetParser().Parse(selector + " { color: red }"); + + Assert.Equal(1, sheet.Rules.Length); + Assert.Equal(expectedText, ((StyleRule)sheet.Rules[0]).SelectorText); + } + + [Fact] + public void MarkerPseudoElementProducesPseudoElementSelector() + { + var sheet = new StylesheetParser().Parse("li::marker { color: red }"); + var selector = ((StyleRule)sheet.Rules[0]).Selector; + var subject = selector is CompoundSelector compound ? compound.Last() : selector; + + var pseudo = Assert.IsType(subject); + Assert.Equal("marker", pseudo.Name); + } + private static bool HasStandardPseudoElementSelector(ISelector selector, bool negate = false) { if (selector is PseudoElementSelector pes) diff --git a/src/ExCSS/Enumerations/PseudoElementNames.cs b/src/ExCSS/Enumerations/PseudoElementNames.cs index d22a62f8..bd8216fb 100644 --- a/src/ExCSS/Enumerations/PseudoElementNames.cs +++ b/src/ExCSS/Enumerations/PseudoElementNames.cs @@ -4,6 +4,7 @@ public static class PseudoElementNames { public static readonly string Before = "before"; public static readonly string After = "after"; + public static readonly string Marker = "marker"; public static readonly string Selection = "selection"; public static readonly string FirstLine = "first-line"; public static readonly string FirstLetter = "first-letter"; diff --git a/src/ExCSS/Factories/PseudoElementSelectorFactory.cs b/src/ExCSS/Factories/PseudoElementSelectorFactory.cs index b119758d..b4a53603 100644 --- a/src/ExCSS/Factories/PseudoElementSelectorFactory.cs +++ b/src/ExCSS/Factories/PseudoElementSelectorFactory.cs @@ -20,6 +20,7 @@ public sealed class PseudoElementSelectorFactory // some implementations are dubious (first-line, first-letter, ...) PseudoElementNames.Before, PseudoElementNames.After, + PseudoElementNames.Marker, PseudoElementNames.Selection, PseudoElementNames.FirstLine, PseudoElementNames.FirstLetter,