diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 6a8c8a1d..a8483d33 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -2,8 +2,8 @@
"version": 1,
"isRoot": true,
"tools": {
- "fallout.cli": {
- "version": "11.0.18",
+ "fallout.globaltool": {
+ "version": "10.4.0",
"commands": [
"fallout"
]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed966e3e..465f5936 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 1.0.2
+
+Released on Friday, August 21 2026.
+
+- Fixed `oklab`/`oklch` usage with plain number as first argument (#225) @scasteran-jw
+
# 1.0.1
Released on Friday, July 31 2026.
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index f62e6cee..6c9b8b2d 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -25,6 +25,7 @@ AngleSharp.Css contains code written by (in order of first pull request / commit
* [Sebastian Stehle](https://github.com/SebastianStehle)
* [MaceWindu](https://github.com/MaceWindu)
* [Serhan Apaydın](https://github.com/monoblaine)
+* [scasteran](https://github.com/scasteran-jw)
Without these awesome people AngleSharp.Css could not exist. Thanks to everyone for your contributions! :beers:
diff --git a/README.md b/README.md
index 4ecccc76..dcc5e464 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,6 @@
[](https://github.com/AngleSharp/AngleSharp.Css/releases)
[](https://www.nuget.org/packages/AngleSharp.Css/)
[](https://github.com/AngleSharp/AngleSharp.Css/issues)
-[](https://gitter.im/AngleSharp/AngleSharp)
-[](https://stackoverflow.com/tags/anglesharp)
[](https://cla-assistant.io/AngleSharp/AngleSharp.Css)
AngleSharp.Css extends the core AngleSharp library with some more powerful CSS capabilities. This repository is the home of the source for the AngleSharp.Css NuGet package.
diff --git a/build/_build.csproj b/build/_build.csproj
index 647ac264..11c89b02 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -11,9 +11,7 @@
-
-
-
+
diff --git a/src/AngleSharp.Css.Docs/package.json b/src/AngleSharp.Css.Docs/package.json
index 2ba7b5bf..df838e5a 100644
--- a/src/AngleSharp.Css.Docs/package.json
+++ b/src/AngleSharp.Css.Docs/package.json
@@ -1,6 +1,6 @@
{
"name": "@anglesharp/css",
- "version": "1.0.0",
+ "version": "1.0.2",
"preview": true,
"description": "The doclet for the AngleSharp.Css documentation.",
"keywords": [
diff --git a/src/AngleSharp.Css.Tests/Declarations/CssFontDescriptorProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssFontDescriptorProperty.cs
new file mode 100644
index 00000000..7f945917
--- /dev/null
+++ b/src/AngleSharp.Css.Tests/Declarations/CssFontDescriptorProperty.cs
@@ -0,0 +1,91 @@
+namespace AngleSharp.Css.Tests.Declarations
+{
+ using NUnit.Framework;
+ using static CssConstructionFunctions;
+
+ [TestFixture]
+ public class CssFontDescriptorPropertyTests
+ {
+ [TestCase("size-adjust: 100%", "100%")]
+ [TestCase("size-adjust: 90.5%", "90.5%")]
+ [TestCase("size-adjust: 0%", "0%")]
+ public void SizeAdjustLegalValues(string snippet, string expected)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.AreEqual("size-adjust", property.Name);
+ Assert.IsTrue(property.HasValue);
+ Assert.AreEqual(expected, property.Value);
+ }
+
+ [TestCase("size-adjust: 10px")]
+ [TestCase("size-adjust: 100")]
+ [TestCase("size-adjust: auto")]
+ public void SizeAdjustIllegalValues(string snippet)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.IsFalse(property.HasValue);
+ }
+
+ [TestCase("ascent-override: 90%", "90%")]
+ [TestCase("ascent-override: normal", "normal")]
+ [TestCase("descent-override: 20%", "20%")]
+ [TestCase("line-gap-override: 0%", "0%")]
+ public void MetricOverrideLegalValues(string snippet, string expected)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.IsTrue(property.HasValue);
+ Assert.AreEqual(expected, property.Value);
+ }
+
+ [TestCase("ascent-override: 10px")]
+ [TestCase("descent-override: auto")]
+ [TestCase("line-gap-override: none")]
+ public void MetricOverrideIllegalValues(string snippet)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.IsFalse(property.HasValue);
+ }
+
+ [TestCase("font-feature-settings: normal", "normal")]
+ [TestCase("font-feature-settings: \"liga\"", "\"liga\"")]
+ [TestCase("font-feature-settings: \"liga\" 1", "\"liga\" 1")]
+ [TestCase("font-feature-settings: \"kern\" on", "\"kern\" on")]
+ [TestCase("font-feature-settings: \"kern\" off", "\"kern\" off")]
+ [TestCase("font-feature-settings: \"liga\" 1, \"kern\" off", "\"liga\" 1, \"kern\" off")]
+ public void FontFeatureSettingsLegalValues(string snippet, string expected)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.AreEqual("font-feature-settings", property.Name);
+ Assert.IsTrue(property.HasValue);
+ Assert.AreEqual(expected, property.Value);
+ }
+
+ [TestCase("font-feature-settings: 12")]
+ [TestCase("font-feature-settings: liga")]
+ [TestCase("font-feature-settings: \"liga\" bogus")]
+ public void FontFeatureSettingsIllegalValues(string snippet)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.IsFalse(property.HasValue);
+ }
+
+ [TestCase("font-variation-settings: normal", "normal")]
+ [TestCase("font-variation-settings: \"wght\" 400", "\"wght\" 400")]
+ [TestCase("font-variation-settings: \"wght\" 400, \"slnt\" -10", "\"wght\" 400, \"slnt\" -10")]
+ public void FontVariationSettingsLegalValues(string snippet, string expected)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.AreEqual("font-variation-settings", property.Name);
+ Assert.IsTrue(property.HasValue);
+ Assert.AreEqual(expected, property.Value);
+ }
+
+ [TestCase("font-variation-settings: wght 400")]
+ [TestCase("font-variation-settings: 400")]
+ public void FontVariationSettingsIllegalValues(string snippet)
+ {
+ var property = ParseDeclaration(snippet);
+ Assert.IsFalse(property.HasValue);
+ }
+ }
+}
diff --git a/src/AngleSharp.Css.Tests/Declarations/SetPropertyOverwriteTests.cs b/src/AngleSharp.Css.Tests/Declarations/SetPropertyOverwriteTests.cs
new file mode 100644
index 00000000..9846940e
--- /dev/null
+++ b/src/AngleSharp.Css.Tests/Declarations/SetPropertyOverwriteTests.cs
@@ -0,0 +1,81 @@
+#nullable disable
+namespace AngleSharp.Css.Tests.Declarations
+{
+ using AngleSharp.Css.Dom;
+ using NUnit.Framework;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using static CssConstructionFunctions;
+
+ ///
+ /// Setting a declaration builds a fresh property and assigns its value, which always
+ /// replaces the raw value it starts with. These cases pin down that re-declaring a
+ /// property - longhand over shorthand, shorthand over longhand, important over
+ /// normal, or an invalid value over a valid one - does not depend on the new property
+ /// being seeded from the old one first.
+ ///
+ /// The expectations are a captured baseline of the behaviour, not a statement about
+ /// what the spec requires.
+ ///
+ [TestFixture]
+ public class SetPropertyOverwriteTests
+ {
+ private static readonly (String Input, String Expected)[] Cases =
+ {
+ ("color:red;color:blue", "color: rgba(0, 0, 255, 1)"),
+ ("color:blue;color:red", "color: rgba(255, 0, 0, 1)"),
+ ("color:red;color:notacolor", "color: rgba(255, 0, 0, 1)"),
+ ("color:red !important;color:blue", "color: rgba(255, 0, 0, 1) !important"),
+ ("color:red;color:blue !important", "color: rgba(0, 0, 255, 1) !important"),
+ ("color:red !important;color:blue !important", "color: rgba(0, 0, 255, 1) !important"),
+ ("--x:1;--x:2", "--x: 2"),
+ ("--x:1;color:red", "--x: 1; color: rgba(255, 0, 0, 1)"),
+ ("margin:1px;margin-top:2px", "margin-bottom: 1px; margin-left: 1px; margin-right: 1px; margin-top: 2px"),
+ ("margin-top:2px;margin:1px", "margin-bottom: 1px; margin-left: 1px; margin-right: 1px; margin-top: 1px"),
+ ("margin:1px;margin:notalength", "margin-bottom: 1px; margin-left: 1px; margin-right: 1px; margin-top: 1px"),
+ ("margin:1px !important;margin-top:2px", "margin-bottom: 1px !important; margin-left: 1px !important; margin-right: 1px !important; margin-top: 1px !important"),
+ ("padding:1px 2px;padding-left:9px;padding:3px", "padding-bottom: 3px; padding-left: 3px; padding-right: 3px; padding-top: 3px"),
+ ("border-radius:4px;border-top-left-radius:9px", "border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border-top-left-radius: 9px; border-top-right-radius: 4px"),
+ ("flex:1 1 auto;flex-grow:5", "flex-basis: auto; flex-grow: 5; flex-shrink: 1"),
+ ("overflow:hidden;overflow-x:scroll", "overflow-x: scroll; overflow: hidden"),
+ ("grid-area:a;grid-row:2", "grid-column-end: a; grid-column-start: a; grid-row-end: auto; grid-row-start: 2"),
+ ("grid-row:2;grid-area:a", "grid-column-end: a; grid-column-start: a; grid-row-end: a; grid-row-start: a"),
+ ("transition:all .3s;transition-duration:1s", "transition-delay: initial; transition-duration: 1s; transition-property: all; transition-timing-function: initial"),
+ ("font:bold 14px/1.2 Arial;font-size:20px", "font-family: Arial; font-size: 20px; font-stretch: ; font-style: ; font-variant: ; font-weight: bold; line-height: 1.2"),
+ ("font-size:20px;font:bold 14px/1.2 Arial", "font-family: Arial; font-size: 14px; font-stretch: ; font-style: ; font-variant: ; font-weight: bold; line-height: 1.2"),
+ ("border:1px solid red;border-width:3px", "border-bottom-color: rgba(255, 0, 0, 1); border-bottom-style: solid; border-bottom-width: 3px; border-left-color: rgba(255, 0, 0, 1); border-left-style: solid; border-left-width: 3px; border-right-color: rgba(255, 0, 0, 1); border-right-style: solid; border-right-width: 3px; border-top-color: rgba(255, 0, 0, 1); border-top-style: solid; border-top-width: 3px"),
+ ("border-width:3px;border:1px solid red", "border-bottom-color: rgba(255, 0, 0, 1); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgba(255, 0, 0, 1); border-left-style: solid; border-left-width: 1px; border-right-color: rgba(255, 0, 0, 1); border-right-style: solid; border-right-width: 1px; border-top-color: rgba(255, 0, 0, 1); border-top-style: solid; border-top-width: 1px"),
+ ("background:red;background:blue", "background-attachment: initial; background-clip: initial; background-color: rgba(0, 0, 255, 1); background-image: initial; background-origin: initial; background-position-x: initial; background-position-y: initial; background-repeat-x: initial; background-repeat-y: initial; background-size: initial"),
+ ("background:red;background-color:blue", "background-attachment: initial; background-clip: initial; background-color: rgba(0, 0, 255, 1); background-image: initial; background-origin: initial; background-position-x: initial; background-position-y: initial; background-repeat-x: initial; background-repeat-y: initial; background-size: initial"),
+ ("background-color:blue;background:red", "background-attachment: initial; background-clip: initial; background-color: rgba(255, 0, 0, 1); background-image: initial; background-origin: initial; background-position-x: initial; background-position-y: initial; background-repeat-x: initial; background-repeat-y: initial; background-size: initial"),
+ ("background:red;background:", "background-attachment: initial; background-clip: initial; background-color: rgba(255, 0, 0, 1); background-image: initial; background-origin: initial; background-position-x: initial; background-position-y: initial; background-repeat-x: initial; background-repeat-y: initial; background-size: initial"),
+ ("background:linear-gradient(red,blue);background:none", "background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position-x: initial; background-position-y: initial; background-repeat-x: initial; background-repeat-y: initial; background-size: initial"),
+ };
+
+ [Test]
+ public void RedeclaringAPropertyProducesTheExpectedBlock()
+ {
+ var failures = new List();
+
+ foreach (var (input, expected) in Cases)
+ {
+ var actual = Describe(ParseDeclarations(input));
+
+ if (!String.Equals(actual, expected, StringComparison.Ordinal))
+ {
+ failures.Add($"{input}{Environment.NewLine} expected: {expected}{Environment.NewLine} actual: {actual}");
+ }
+ }
+
+ Assert.That(failures, Is.Empty, String.Join(Environment.NewLine, failures));
+ }
+
+ private static String Describe(ICssStyleDeclaration style)
+ {
+ var parts = style.Select(p => $"{p.Name}: {p.Value}{(p.IsImportant ? " !important" : String.Empty)}").ToList();
+ parts.Sort(StringComparer.Ordinal);
+ return String.Join("; ", parts);
+ }
+ }
+}
diff --git a/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs b/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs
index cf36e11b..e74d060d 100644
--- a/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs
+++ b/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs
@@ -75,6 +75,28 @@ public void ParseOklabToRgb_Second()
Assert.AreEqual("rgba(198, 93, 7, 1)", color);
}
+ [Test]
+ public void ParseOklabNumberToRgb_First()
+ {
+ var html = @"
Text
";
+ var dom = ParseDocument(html);
+ var p = dom.QuerySelector("p");
+ var s = p.GetStyle();
+ var color = s.GetColor();
+ Assert.AreEqual("rgba(125, 35, 40, 1)", color);
+ }
+
+ [Test]
+ public void ParseOklabNumberToRgb_Second()
+ {
+ var html = @"
Text
";
+ var dom = ParseDocument(html);
+ var p = dom.QuerySelector("p");
+ var s = p.GetStyle();
+ var color = s.GetColor();
+ Assert.AreEqual("rgba(198, 93, 7, 1)", color);
+ }
+
[Test]
public void ParseOklabToRgb_Alpha()
{
@@ -108,6 +130,28 @@ public void ParseOklchToRgb_Second()
Assert.AreEqual("rgba(198, 93, 7, 1)", color);
}
+ [Test]
+ public void ParseOklchNumberToRgb_First()
+ {
+ var html = @"
Text
";
+ var dom = ParseDocument(html);
+ var p = dom.QuerySelector("p");
+ var s = p.GetStyle();
+ var color = s.GetColor();
+ Assert.AreEqual("rgba(125, 35, 40, 1)", color);
+ }
+
+ [Test]
+ public void ParseOklchNumberToRgb_Second()
+ {
+ var html = @"
Text
";
+ var dom = ParseDocument(html);
+ var p = dom.QuerySelector("p");
+ var s = p.GetStyle();
+ var color = s.GetColor();
+ Assert.AreEqual("rgba(198, 93, 7, 1)", color);
+ }
+
[Test]
public void ParseOklchToRgb_Alpha()
{
diff --git a/src/AngleSharp.Css.Tests/Rules/FontFaceDescriptors.cs b/src/AngleSharp.Css.Tests/Rules/FontFaceDescriptors.cs
new file mode 100644
index 00000000..f324c1e9
--- /dev/null
+++ b/src/AngleSharp.Css.Tests/Rules/FontFaceDescriptors.cs
@@ -0,0 +1,162 @@
+namespace AngleSharp.Css.Tests.Rules
+{
+ using AngleSharp.Css.Dom;
+ using AngleSharp.Css.Parser;
+ using NUnit.Framework;
+ using System.Linq;
+ using static CssConstructionFunctions;
+
+ ///
+ /// Descriptors inside @font-face used to be limited to a hardcoded set of seven,
+ /// with everything else dropped silently and without regard to the parser options.
+ /// These cases pin down that the standard CSS Fonts Level 4 descriptors survive by
+ /// default and that anything else survives when unknown declarations are included.
+ ///
+ [TestFixture]
+ public class FontFaceDescriptorTests
+ {
+ private static readonly CssParserOptions IncludingUnknown = new() { IsIncludingUnknownDeclarations = true };
+
+ [Test]
+ public void FontFaceKeepsFontDisplay()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-display: swap }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("swap", fontface.GetPropertyValue(PropertyNames.FontDisplay));
+ }
+
+ [Test]
+ public void FontFaceKeepsSizeAdjust()
+ {
+ var sheet = ParseStyleSheet("@font-face { size-adjust: 90% }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("90%", fontface.GetPropertyValue(PropertyNames.SizeAdjust));
+ }
+
+ [TestCase("ascent-override", "90%")]
+ [TestCase("descent-override", "20%")]
+ [TestCase("line-gap-override", "0%")]
+ [TestCase("ascent-override", "normal")]
+ public void FontFaceKeepsMetricOverrides(string name, string value)
+ {
+ var sheet = ParseStyleSheet($"@font-face {{ {name}: {value} }}");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual(value, fontface.GetPropertyValue(name));
+ }
+
+ [Test]
+ public void FontFaceKeepsFontFeatureSettings()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-feature-settings: \"liga\" 1, \"kern\" on, \"smcp\" }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("\"liga\" 1, \"kern\" on, \"smcp\"", fontface.GetPropertyValue(PropertyNames.FontFeatureSettings));
+ }
+
+ [Test]
+ public void FontFaceKeepsFontVariationSettings()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-variation-settings: \"wght\" 400, \"slnt\" -10 }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("\"wght\" 400, \"slnt\" -10", fontface.GetPropertyValue(PropertyNames.FontVariationSettings));
+ }
+
+ [Test]
+ public void FontFaceFeaturesMapToFontFeatureSettings()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-feature-settings: \"liga\" 1 }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("\"liga\" 1", fontface.Features);
+
+ fontface.Features = "\"kern\" off";
+ Assert.AreEqual("\"kern\" off", fontface.GetPropertyValue(PropertyNames.FontFeatureSettings));
+ }
+
+ [Test]
+ public void FontFaceStandardDescriptorsRoundtripViaToCss()
+ {
+ var src = "@font-face { font-family: \"FontName\"; src: url(\"https://example.com/font.woff\") format(\"woff\"); font-display: swap; size-adjust: 100% }";
+ var sheet = ParseStyleSheet(src);
+ var css = sheet.ToCss();
+ Assert.That(css, Does.Contain("font-display: swap"));
+ Assert.That(css, Does.Contain("size-adjust: 100%"));
+ }
+
+ [Test]
+ public void FontFaceDropsVendorDescriptorByDefault()
+ {
+ var sheet = ParseStyleSheet("@font-face { mso-generic-font-family: auto }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual(0, fontface.Length);
+ }
+
+ [Test]
+ public void FontFaceKeepsVendorDescriptorWhenIncludingUnknownDeclarations()
+ {
+ var sheet = ParseStyleSheet("@font-face { mso-generic-font-family: auto }", IncludingUnknown);
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("auto", fontface.GetPropertyValue("mso-generic-font-family"));
+ Assert.That(sheet.ToCss(), Does.Contain("mso-generic-font-family: auto"));
+ }
+
+ [Test]
+ public void FontFaceKeepsCustomPropertyWhenIncludingUnknownDeclarations()
+ {
+ var sheet = ParseStyleSheet("@font-face { --custom-thing: 12px }", IncludingUnknown);
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("12px", fontface.GetPropertyValue("--custom-thing"));
+ }
+
+ [Test]
+ public void FontFaceIgnoresInvalidDescriptorValue()
+ {
+ var sheet = ParseStyleSheet("@font-face { size-adjust: 10px; ascent-override: bogus }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual(0, fontface.Length);
+ Assert.That(sheet.ToCss(), Does.Not.Contain("size-adjust"));
+ }
+
+ [Test]
+ public void FontFaceInvalidValueDoesNotOverwriteValidOne()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-weight: 400; font-weight: bogus }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("400", fontface.Weight);
+ }
+
+ [Test]
+ public void FontFaceValidValueOverwritesEarlierOne()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-weight: 400; font-weight: 700 }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ Assert.AreEqual("700", fontface.Weight);
+ Assert.AreEqual(1, fontface.Length);
+ }
+
+ [Test]
+ public void CounterStyleKeepsDescriptorsWhenIncludingUnknownDeclarations()
+ {
+ var sheet = ParseStyleSheet("@counter-style thumbs { system: cyclic; symbols: \"X\" }", IncludingUnknown);
+ var rule = (ICssProperties)sheet.Rules[0];
+ Assert.AreEqual("cyclic", rule.GetPropertyValue("system"));
+ Assert.AreEqual("\"X\"", rule.GetPropertyValue("symbols"));
+ }
+
+ [Test]
+ public void ViewportKeepsUnknownDescriptorWhenIncludingUnknownDeclarations()
+ {
+ var sheet = ParseStyleSheet("@viewport { width: 100px; foo: bar }", IncludingUnknown);
+ var rule = (ICssProperties)sheet.Rules[0];
+ Assert.AreEqual("bar", rule.GetPropertyValue("foo"));
+ Assert.AreEqual(2, rule.Length);
+ }
+
+ [Test]
+ public void FontFaceDescriptorsAreEnumerable()
+ {
+ var sheet = ParseStyleSheet("@font-face { font-family: \"X\"; font-display: swap; size-adjust: 50% }");
+ var fontface = (ICssFontFaceRule)sheet.Rules[0];
+ var names = fontface.Select(m => m.Name).ToArray();
+ CollectionAssert.AreEquivalent(new[] { "font-family", "font-display", "size-adjust" }, names);
+ }
+ }
+}
diff --git a/src/AngleSharp.Css/BrowsingContextExtensions.cs b/src/AngleSharp.Css/BrowsingContextExtensions.cs
index 9b6249c3..5cecbb1c 100644
--- a/src/AngleSharp.Css/BrowsingContextExtensions.cs
+++ b/src/AngleSharp.Css/BrowsingContextExtensions.cs
@@ -95,7 +95,7 @@ internal static CssProperty CreateProperty(this IBrowsingContext context, String
private static Boolean AllowsDeclaration(this IBrowsingContext context, DeclarationInfo info) =>
info.Flags != PropertyFlags.Unknown || context.IsAllowingUnknownDeclarations();
- private static Boolean IsAllowingUnknownDeclarations(this IBrowsingContext context)
+ internal static Boolean IsAllowingUnknownDeclarations(this IBrowsingContext context)
{
var parser = context.GetProvider();
return parser?.Options.IsIncludingUnknownDeclarations ?? true;
diff --git a/src/AngleSharp.Css/Constants/CssKeywords.cs b/src/AngleSharp.Css/Constants/CssKeywords.cs
index bacdb6aa..4d36e61b 100644
--- a/src/AngleSharp.Css/Constants/CssKeywords.cs
+++ b/src/AngleSharp.Css/Constants/CssKeywords.cs
@@ -307,6 +307,16 @@ public static class CssKeywords
///
public static readonly String Optional = "optional";
+ ///
+ /// The on keyword (font-feature-settings).
+ ///
+ public static readonly String On = "on";
+
+ ///
+ /// The off keyword (font-feature-settings).
+ ///
+ public static readonly String Off = "off";
+
///
/// The avoid keyword.
///
diff --git a/src/AngleSharp.Css/Constants/InitialValues.cs b/src/AngleSharp.Css/Constants/InitialValues.cs
index 166fb563..eb808595 100644
--- a/src/AngleSharp.Css/Constants/InitialValues.cs
+++ b/src/AngleSharp.Css/Constants/InitialValues.cs
@@ -47,6 +47,11 @@ static class InitialValues
public static readonly ICssValue FontSynthesisStyleDecl = new CssConstantValue