Skip to content
Merged
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
32 changes: 28 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
# 1.1.4

Released on Tuesday, October 20 2026.

- Improved default stylesheet with HTML5 elements, non-rendered elements, and form control styles
- Updated computed zero lengths to serialize with their unit (e.g., `0px`)
- Updated cascade to always order author rules after default stylesheet rules
- Updated `font-variant` to be a shorthand for `font-variant-caps`
- Fixed rules matching the host being applied to its `::before` / `::after` pseudo-elements
- Fixed computed `font-weight` to resolve `bolder` / `lighter` against the parent's weight
- Fixed computed `border-*-radius` to keep percentages instead of resolving them to pixels
- Fixed computed `background-position` and `mask-position` to keep percentages instead of resolving them against the viewport
- Fixed computed `background-position` and `background-repeat` being empty
- Fixed computed `border-image-slice` serializing missing sides as `NaNpx`
- Fixed computing a unitless `border-image-width` or `border-image-outset` throwing
- Fixed `border-image-outset` dropping unitless numbers
- Fixed `text-align-last` not being inherited
- Added support for `display: flow-root`
- Added support for double-position color stops in gradients (e.g., `red 0 50%`)
- Added support for filter functions in `backdrop-filter`
- Added support for multiple layers in `mask-size`, `mask-repeat`, and `mask-position`
- Added support for percentages in `font-stretch`
- Added `font-variant-caps` property

# 1.1.3

Released on Thursday, October 15 2026
Released on Thursday, October 15 2026.

- Improved pseudo-class matching to skip a per-element `ConditionalWeakTable` probe when no state has ever been forced via `SetPseudoClass`
- Fixed box-shadow serialization dropping zero blur before nonzero spread (#249) @PrzemyslawKlys

# 1.1.2

Released on Friday, September 11 2026
Released on Friday, September 11 2026.

- Fixed issue with calculation of CSS grid style
- Fixed default value of CSS gradients
Expand All @@ -28,7 +52,7 @@ Released on Friday, September 11 2026

# 1.1.1

Released on Wednesday, September 9 2026
Released on Wednesday, September 9 2026.

- Fixed unresolved converter re-entry (#243) @sebastienros
- Fixed issue with computation of `transform` functions
Expand All @@ -41,7 +65,7 @@ Released on Wednesday, September 9 2026

# 1.1.0

Released on Saturday, September 5 2026
Released on Saturday, September 5 2026.

- Improved evaluation of comma-separated media queries (#230)
- Fixed wrong media feature used for scripting (#233)
Expand Down
2 changes: 1 addition & 1 deletion src/AngleSharp.Css.Docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@anglesharp/css",
"version": "1.1.3",
"version": "1.1.4",
"preview": true,
"description": "The doclet for the AngleSharp.Css documentation.",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion src/AngleSharp.Css.Tests/Declarations/CssTextProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ public void CssTextAlignLastNoneIllegal()
var snippet = "text-align-last: none";
var property = ParseDeclaration(snippet);
Assert.AreEqual("text-align-last", property.Name);
Assert.IsFalse(property.IsInherited);
Assert.IsTrue(property.IsInherited);
Assert.IsFalse(property.IsImportant);
Assert.IsFalse(property.HasValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ private static readonly (String Input, String Expected)[] Cases =
("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"),
("font:bold 14px/1.2 Arial;font-size:20px", "font-family: Arial; font-size: 20px; font-stretch: ; font-style: ; font-variant-caps: ; 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-caps: ; 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"),
Expand Down
2 changes: 1 addition & 1 deletion src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public void GetComputedStyleComplexScenario()
Assert.IsNotNull(style);
Assert.AreEqual(8, style.Length);

Assert.AreEqual("0", style.GetMargin());
Assert.AreEqual("0px", style.GetMargin());
Assert.AreEqual("rgba(255, 0, 0, 1)", style.GetColor());
Assert.AreEqual("bold", style.GetFontWeight());
Assert.AreEqual("italic", style.GetFontStyle());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#nullable disable
namespace AngleSharp.Css.Tests.Styling
{
using AngleSharp.Dom;
using NUnit.Framework;
using static CssConstructionFunctions;

/// <summary>
/// The computed value of a `border-*-radius` is "a pair of computed &lt;length-percentage&gt;
/// values" (https://www.w3.org/TR/css-backgrounds-3/#border-radius): a percentage stays a
/// percentage, because it refers to the border box, which only layout knows. Resolving it to
/// pixels at style-computation time only works for a box whose width and height are both
/// authored; for an auto-sized box, or a form control whose size is intrinsic, the percentage
/// collapses to `0`. Found in a downstream renderer (AngleSharp.Renderer): a radio button
/// styled `border-radius: 50%` (as every UA stylesheet does) rendered square. Note that
/// `BorderRadiusPercentageResolutionTests` currently expects pixel values for explicitly sized
/// boxes; it needs to change along with this fix.
/// </summary>
[TestFixture]
public class BorderRadiusPercentageComputedValueTests
{
private static string ComputedRadius(string html)
{
var document = ParseDocument($"<body>{html}</body>");
return document.GetElementById("target").ComputeCurrentStyle().GetPropertyValue("border-top-left-radius");
}

[Test]
public void PercentageRadiusOfAnAutoSizedBoxStaysAPercentage()
{
Assert.AreEqual("50%", ComputedRadius("<div id=target style=\"border-radius: 50%\">x</div>"));
}

[Test]
public void PercentageRadiusOfAnIntrinsicallySizedControlStaysAPercentage()
{
Assert.AreEqual("50%", ComputedRadius("<input id=target type=checkbox style=\"border-radius: 50%\">"));
}

[Test]
public void EllipticalPercentageRadiusKeepsBothComponents()
{
Assert.AreEqual("10% 30%", ComputedRadius("<div id=target style=\"border-radius: 10% / 30%\">x</div>"));
}

[Test]
public void LengthRadiusIsStillComputedToPixels()
{
Assert.AreEqual("8px", ComputedRadius("<div id=target style=\"border-radius: 0.5em; font-size: 16px\">x</div>"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ namespace AngleSharp.Css.Tests.Styling
/// <summary>
/// Per https://www.w3.org/TR/css-backgrounds-3/#corner-overlap, a percentage `border-radius`
/// resolves its horizontal component against the border box's own *width* and its vertical
/// component against the border box's own *height* - independently. Confirmed empirically
/// while building `border-radius` support in a downstream renderer (AngleSharp.Renderer) that
/// this does not happen: both components resolve against the containing block's/element's
/// *width* alone, so on a box whose width and height differ, the vertical radius comes out
/// wrong (tracking the wrong axis's dimension entirely, not merely imprecise).
/// component against the border box's own *height* - independently. Since the border box is
/// only known during layout, the computed value keeps both components as percentages (see
/// `BorderRadiusPercentageComputedValueTests`) instead of resolving them prematurely.
/// </summary>
[TestFixture]
public class BorderRadiusPercentageResolutionTests
Expand All @@ -28,36 +26,32 @@ private static IDocument ParseWithRenderDevice(string html, int viewPortWidth =
}

[Test]
public void VerticalPercentageComponentResolvesAgainstTheElementsOwnHeight()
public void PercentageComponentsOfAnExplicitlySizedBoxStayPercentages()
{
// 200x100 box, `border-radius: 10% / 30%` - the horizontal component (10%) should
// resolve against the 200px width (20px); the vertical component (30%) should resolve
// against the 100px height (30px), not against the 200px width (which would give the
// wrong value, 60px).
// 200x100 box, `border-radius: 10% / 30%` - the computed value is a pair of
// <length-percentage> values (https://www.w3.org/TR/css-backgrounds-3/#border-radius),
// so both components stay percentages. Resolving them (the horizontal one against the
// 200px width, the vertical one against the 100px height) is up to layout.
var document = ParseWithRenderDevice("<div id=target style=\"width:200px; height:100px; border-radius: 10% / 30%;\"></div>");
var target = document.GetElementById("target");
var style = target.ComputeCurrentStyle();

var horizontal = style.GetPropertyValue("border-top-left-radius");
Assert.IsTrue(horizontal.Contains("30px"), $"expected the vertical 30% component to resolve to 30px (30% of the 100px height); got '{horizontal}'");
Assert.AreEqual("10% 30%", style.GetPropertyValue("border-top-left-radius"));
}

[Test]
public void VerticalPercentageComponentTracksHeightAcrossDifferentElementHeights()
public void VerticalPercentageComponentIsNotResolvedAgainstTheWidth()
{
// The same vertical percentage against two different heights (but the same width)
// must resolve to two different pixel values if it is genuinely tracking height - if
// it were (incorrectly) tracking width instead, both would resolve identically despite
// the different heights.
// The vertical component must not be turned into a pixel value derived from the
// box's width; it is kept as authored, independent of the box's dimensions.
var shortDocument = ParseWithRenderDevice("<div id=target style=\"width:200px; height:100px; border-radius: 0 / 30%;\"></div>");
var shortTarget = shortDocument.GetElementById("target");
var shortRadius = shortTarget.ComputeCurrentStyle().GetPropertyValue("border-top-left-radius");
var shortRadius = shortDocument.GetElementById("target").ComputeCurrentStyle().GetPropertyValue("border-top-left-radius");

var tallDocument = ParseWithRenderDevice("<div id=target style=\"width:200px; height:200px; border-radius: 0 / 30%;\"></div>");
var tallTarget = tallDocument.GetElementById("target");
var tallRadius = tallTarget.ComputeCurrentStyle().GetPropertyValue("border-top-left-radius");
var tallRadius = tallDocument.GetElementById("target").ComputeCurrentStyle().GetPropertyValue("border-top-left-radius");

Assert.AreNotEqual(shortRadius, tallRadius, "a 30% vertical radius against a 100px-tall box and a 200px-tall box must resolve to different pixel values.");
Assert.AreEqual("0px 30%", shortRadius);
Assert.AreEqual("0px 30%", tallRadius);
}
}
}
5 changes: 4 additions & 1 deletion src/AngleSharp.Css.Tests/Styling/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ public void ObtainDefaultSheet()
// rule, giving generated-content pseudo-elements their spec-correct default display
// (previously they had none at all, so this renderer's general "unset display defaults
// to block" fallback made every ::before/::after start its own new block line).
Assert.AreEqual(50, sheet.Rules.Length);
// 60, not 50: the UA stylesheet now also carries the rules for elements that are
// never rendered (`[hidden]`, `input[type=hidden]`) and the default look of form
// controls (text fields, buttons, checkboxes/radios, color and range inputs).
Assert.AreEqual(60, sheet.Rules.Length);
}
}
}
4 changes: 2 additions & 2 deletions src/AngleSharp.Css.Tests/Styling/CssBlendFilterProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ public void BackdropFilterNone()
}

[Test]
public void BackdropFilterInvalid()
public void BackdropFilterAcceptsFilterFunctions()
{
var element = _document.CreateElement("div") as IHtmlElement;
element.GetStyle().SetProperty("backdrop-filter", "blur(5px)");
Assert.AreEqual("", element.GetStyle().GetPropertyValue("backdrop-filter"));
Assert.AreEqual("blur(5px)", element.GetStyle().GetPropertyValue("backdrop-filter"));
}

[Test]
Expand Down
4 changes: 2 additions & 2 deletions src/AngleSharp.Css.Tests/Styling/CustomPropertyCycles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void ResolvedTokensAreNotConvertedUsingTheCustomPropertyConverter()
var style = document.QuerySelector("div").ComputeCurrentStyle();
Assert.AreEqual("0", style.GetPropertyValue("--a"));
Assert.AreEqual("0", style.GetPropertyValue("opacity"));
Assert.AreEqual("0", style.GetPropertyValue("width"));
Assert.AreEqual("0px", style.GetPropertyValue("width"));
}

[TestCase("--a:red;--b:var(--a);--c:var(--a);--d:var(--b) var(--c)", "red red")]
Expand Down Expand Up @@ -178,7 +178,7 @@ public void OnlyTheWinningDeclarationParticipatesInTheGraph(String text, String
Assert.AreEqual(expected, document.QuerySelector("div").ComputeCurrentStyle().GetPropertyValue("visibility"));
}

[TestCase("--a:var(--a);margin:var(--a)", "0", "0")]
[TestCase("--a:var(--a);margin:var(--a)", "0px", "0px")]
[TestCase("--a:var(--a);margin:var(--a,1px 2px)", "1px", "2px")]
[TestCase("--a:1px 2px;margin:var(--a)", "1px", "2px")]
[TestCase("--a:var(--a);margin:3px var(--a,4px)", "3px", "4px")]
Expand Down
Loading
Loading