Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f399f63
Evaluate the media query list in matchMedia against the render device
lahma Sep 2, 2026
19e9fe0
Add an opt-in switch for CSSOM-compliant color serialization
lahma Sep 2, 2026
5fa484a
Merge pull request #228 from lahma/fix/match-media-evaluation
FlorianRappl Sep 2, 2026
3beca43
Merge pull request #229 from lahma/feature/#227
FlorianRappl Sep 2, 2026
883fa7e
Changed version
FlorianRappl Sep 3, 2026
54f32fb
Fixed #233
FlorianRappl Sep 3, 2026
ad80087
Updated changelog
FlorianRappl Sep 3, 2026
a88ad31
Fixed #231
FlorianRappl Sep 3, 2026
bd968f1
Fixed #232
FlorianRappl Sep 3, 2026
87a30c7
Fixed #230
FlorianRappl Sep 3, 2026
16a92f4
Answer the user-preference media features from the render device
lahma Sep 3, 2026
c5c01be
Make the render-device preference helper public
lahma Sep 3, 2026
6435c50
Merge pull request #235 from lahma/feature/#234
FlorianRappl Sep 3, 2026
1d570d0
Updated changelog
FlorianRappl Sep 3, 2026
3680c6a
Fix calc() computation in trimmed / NativeAOT apps
sebastienros Sep 3, 2026
291ba64
Merge pull request #236 from sebastienros/fix/aot-calc-metric-values
FlorianRappl Sep 4, 2026
a6f4cc1
Fixed unitless scaling in calc
FlorianRappl Sep 4, 2026
c77da45
Improved scaling code
FlorianRappl Sep 4, 2026
2eddd84
Fix truncation of unquoted url() values containing ; { or }
meziantou Sep 5, 2026
207340b
Fixed case-sensitive matching of CSS keywords in parsers
meziantou Sep 5, 2026
0988087
Fixed associativity of repeated calc() operators
meziantou Sep 5, 2026
8449e1b
Fixed the unit of calc() division results
meziantou Sep 5, 2026
afbb9cd
Updated changelog
meziantou Sep 5, 2026
e53aba1
Make bad url() handling match the spec and browsers
meziantou Sep 5, 2026
af66a2f
Fix cyclic custom properties during computed style resolution
sebastienros Sep 5, 2026
c8d42af
Merge pull request #239 from meziantou/feature/calcparser-operator-as…
FlorianRappl Sep 5, 2026
8b7fd03
Merge pull request #240 from meziantou/feature/css-keyword-case-sensi…
FlorianRappl Sep 5, 2026
b1a0767
Merge pull request #238 from meziantou/feature/css-unquoted-data-uris…
FlorianRappl Sep 5, 2026
ee38e1a
Updated changelog
FlorianRappl Sep 5, 2026
e35d336
Fixed handling of invalid keyframe selectors
FlorianRappl Sep 5, 2026
99c535a
Preserve public declaration and variable value behavior
sebastienros Sep 5, 2026
b3972e3
Keep cyclic solution in history
FlorianRappl Sep 5, 2026
4f469d6
Merge pull request #242 from sebastienros/sebros/custom-property-cycles
FlorianRappl Sep 5, 2026
1999a58
PR review improvements
FlorianRappl Sep 5, 2026
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# 1.1.0

Released on Saturday, September 5 2026

- Improved evaluation of comma-separated media queries (#230)
- Fixed wrong media feature used for scripting (#233)
- Fixed wrong rientation and scan evaluation (#232)
- Fixed `not <known media type>` is always false (#231)
- Fixed `calc()` computations in AoT-compiled applications (#236) @sebastienros
- Fixed usage of `calc()` with unitless scaling (multiplication / division)
- Fixed case-sensitive matching of `and` / `or` in `@supports` and `from` / `to` in `@keyframes` (#240) @meziantou
- Fixed operator associativity and result units in `calc()` (#239) @meziantou
- Fixed unquoted and invalid `url()` handling (#238) @meziantou
- Fixed handling of invalid keyframe selectors
- Fixed stackoverflow due to cyclic CSS variables (#241)
- Added optional CSSOM compliant color seralization (#229) @lahma
- Added user-preference media features to the render device (#235) @lahma
- Added media query list evaluation using `IRenderDevice` (#228) @lahma

# 1.0.2

Released on Friday, August 21 2026.
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ AngleSharp.Css contains code written by (in order of first pull request / commit
* [MaceWindu](https://github.com/MaceWindu)
* [Serhan Apaydın](https://github.com/monoblaine)
* [scasteran](https://github.com/scasteran-jw)
* [Sébastien Ros](https://github.com/sebastienros)

Without these awesome people AngleSharp.Css could not exist. Thanks to everyone for your contributions! :beers:

Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ var config = Configuration.Default

If no specific `IRenderDevice` (e.g., via creating an `DefaultRenderDevice` object) instance is created a default implementation will be set.

The render device also carries the *user preferences* of the Media Queries Level 5 (and Level 4) user-preference features. `DefaultRenderDevice` implements `IRenderDevicePreferences` for that, and any custom `IRenderDevice` can implement it as well. The dictionary is keyed by the media feature name and holds the keyword that the feature should answer with:

```cs
var config = Configuration.Default
.WithCss()
.WithRenderDevice(new DefaultRenderDevice
{
Preferences = new Dictionary<String, String>
{
{ "prefers-color-scheme", "dark" },
{ "prefers-reduced-motion", "reduce" },
},
});
```

With this device `@media (prefers-color-scheme: dark)` applies in the cascade and `window.MatchMedia("(prefers-color-scheme: dark)").IsMatched` is `true`. A key that is not set leaves its media feature unknown, i.e., a query using it never matches. The keys a browser would set are:

| Key | Keywords |
| --- | --- |
| `prefers-color-scheme` | `light`, `dark` |
| `prefers-reduced-motion` | `no-preference`, `reduce` |
| `prefers-reduced-transparency` | `no-preference`, `reduce` |
| `prefers-contrast` | `no-preference`, `more`, `less`, `custom` |
| `prefers-reduced-data` | `no-preference`, `reduce` |
| `forced-colors` | `none`, `active` |
| `hover`, `any-hover` | `none`, `hover` |
| `pointer`, `any-pointer` | `none`, `coarse`, `fine` |
| `display-mode` | `fullscreen`, `standalone`, `minimal-ui`, `browser` |

The value is compared to the queried keyword case insensitively, so a keyword that is newer than this library works as well. Used without a value, e.g., `@media (prefers-reduced-motion)`, the feature evaluates in a boolean context, where `no-preference` (and `none` for `forced-colors`, `hover`, `any-hover`, `pointer` and `any-pointer`) is `false`. Without a preference `hover` and `pointer` keep answering as they did before, i.e., as a device with no input mechanism.

Going a bit further it is possible to `Render` the current document. This render tree information can then be used to retrieve or other information, e.g.,

```cs
Expand Down
29 changes: 29 additions & 0 deletions docs/general/02-Values.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,32 @@ Console.WriteLine($"Computed font-size: {computedFontSize}");
- Shorthand values (e.g., `margin`, `background`) are decomposed internally to longhands.
- Variables (`var(--x)`) may defer full resolution until cascade context is available.
- Comparing raw source strings is often misleading; compare parsed or computed values instead.

## Custom Properties At Computed-Value Time

Custom properties are resolved only during style computation, for each element before
they are inherited. `GetDeclarations`, `ComputeExplicitStyle`, `ComputeCascadedStyle`,
and render-tree `SpecifiedStyle` retain the original variable expressions. Computed
results are separate declarations and do not rewrite stylesheet or inline values.

During computation, an inherited
alias keeps the parent's resolved value; changing its dependencies on a child does not
resolve that alias again. A declaration explicitly matching both elements is resolved
locally on each element.

Following [CSS Variables dependency-cycle rules](https://drafts.csswg.org/css-variables-1/#cycles),
every property in a cycle becomes guaranteed-invalid, including cycles through unused
fallbacks. A consuming `var(--name, fallback)` can recover from an invalid or missing
custom property. Without a usable fallback, the consuming declaration uses its inherited
or initial value, not an earlier declaration from the cascade. A valid custom-property
value that does not match the consumer's grammar does not trigger the `var()` fallback.

Dependency analysis and fallback substitution are iterative, including deeply nested
fallbacks. The public parser still represents nested `var()` fallbacks as `CssVarValue`
objects, and direct `CssReferenceValue.Compute` calls honor the supplied `References`
array, including subsequent changes to its entries.

Expanded values during style computation are limited to 1,048,576 UTF-16 code units (including token
separators) to bound exponential substitution; an expansion exceeding this limit is
invalid at computed-value time. Property-specific parsing, unit conversion, and layout
support still determine which resolved values can be used by a consuming property.
33 changes: 33 additions & 0 deletions docs/general/05-Extensibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ AngleSharp.Css is designed to be composed through services in the AngleSharp con
: Add pseudo-element behavior.
- `IRenderDevice`
: Provide device characteristics for style computation.
- `IRenderDevicePreferences`
: Provide the user preferences answering the user-preference media features.

## Override The Default Stylesheet

Expand Down Expand Up @@ -56,6 +58,37 @@ var config = Configuration.Default
.WithRenderDevice(renderDevice);
```

## Provide The User Preferences

Beside the dimensions a render device carries the user preferences, which answer the user-preference media features. `DefaultRenderDevice` implements `IRenderDevicePreferences` for that; a custom `IRenderDevice` can implement it as well and is picked up the same way.

```cs
var renderDevice = new DefaultRenderDevice
{
Preferences = new Dictionary<String, String>
{
{ "prefers-color-scheme", "dark" },
{ "prefers-reduced-motion", "reduce" },
},
};
```

The dictionary is keyed by the media feature name and holds the keyword the feature answers with. A key that is not set leaves its media feature unknown, i.e., a query using it never matches.

| Key | Keywords |
| --- | --- |
| `prefers-color-scheme` | `light`, `dark` |
| `prefers-reduced-motion` | `no-preference`, `reduce` |
| `prefers-reduced-transparency` | `no-preference`, `reduce` |
| `prefers-contrast` | `no-preference`, `more`, `less`, `custom` |
| `prefers-reduced-data` | `no-preference`, `reduce` |
| `forced-colors` | `none`, `active` |
| `hover`, `any-hover` | `none`, `hover` |
| `pointer`, `any-pointer` | `none`, `coarse`, `fine` |
| `display-mode` | `fullscreen`, `standalone`, `minimal-ui`, `browser` |

The value is compared to the queried keyword case insensitively, so a keyword that is newer than this library works as well. Used without a value, e.g., `@media (prefers-reduced-motion)`, the feature evaluates in a boolean context, where `no-preference` (and `none` for `forced-colors`, `hover`, `any-hover`, `pointer` and `any-pointer`) is `false`. Without a preference `hover` and `pointer` keep answering as they did before, i.e., as a device with no input mechanism.

## Composition Pattern

Start from the default registrations and replace only what you need:
Expand Down
22 changes: 17 additions & 5 deletions docs/tutorials/04-Questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,36 @@ section: "AngleSharp.Css"

## How to change the color output?

By default, AngleSharp.Css uses `rgba()` for the serialization of `Color`. To change this you can set
By default, AngleSharp.Css uses `rgba()` for the serialization of `CssColorValue`. To change this you can set

```cs
Color.UseHex = true;
CssColorValue.UseHex = true;
```

which will automatically use hex for all non-transparent colors. All other colors would still be represented via the `rgba()` function.

So you'd get:

```cs
Color.UseHex = true;
var color1 = new Color(65, 12, 48);
CssColorValue.UseHex = true;
var color1 = new CssColorValue(65, 12, 48);
// color1.CssText = #410C30
var color2 = new Color(65, 12, 48, 10);
var color2 = new CssColorValue(65, 12, 48, 10);
// color2.CssText = rgba(65, 12, 48, 0.04)
```

Alternatively, you can follow the serialization rules from the CSSOM specification, which omit the alpha channel of an opaque color:

```cs
CssColorValue.UseSpecSerialization = true;
var color1 = new CssColorValue(65, 12, 48);
// color1.CssText = rgb(65, 12, 48)
var color2 = new CssColorValue(65, 12, 48, 10);
// color2.CssText = rgba(65, 12, 48, 0.04)
```

Both switches are global and `UseHex` wins if both are active.

## Why is my linked stylesheet not loaded?

Most commonly, resource loading is not enabled. For external stylesheets, configure a requester and enable resource loading.
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.0.2",
"version": "1.1.0",
"preview": true,
"description": "The doclet for the AngleSharp.Css documentation.",
"keywords": [
Expand Down
7 changes: 7 additions & 0 deletions src/AngleSharp.Css.Tests/CssConstructionFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ internal static Predicate<IRenderDevice> CreateValidator(String name, String val
return device => validator.Validate(feature, device);
}

internal static Predicate<IRenderDevice> CreateBooleanValidator(String name)
{
var validator = CreateMediaFeatureValidator(name);
var feature = new MediaFeature(name);
return device => validator.Validate(feature, device);
}

internal static CssFontFeatureValuesRule ParseFontFeatureValuesRule(String source)
{
ICssParser parser = new CssParser();
Expand Down
Loading
Loading