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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 1.0.0

Released on Sunday, July 26 2026.

- First stable version

# 0.18.0

Released on Saturday, July 25 2026.
Expand Down
8 changes: 4 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ We have more detailed information regarding the following subjects:

- [API Documentation](tutorials/01-API.md)
- [Examples](tutorials/02-Examples.md)
- [FAQ](tutorials/03-Questions.md)
- [Render Tree Examples](tutorials/04-Render-Tree.md)
- [Render Tree Examples](tutorials/03-Render-Tree.md)
- [FAQ](tutorials/04-Questions.md)

## Recommended Reading Order

Expand All @@ -29,9 +29,9 @@ If you are new to AngleSharp.Css, this order gives a practical ramp-up:
3. [API Documentation](tutorials/01-API.md) for deeper understanding of the CSSOM model.
4. [CSSOM](general/03-CSSOM.md) and [Core Interfaces](general/04-Core-Interfaces.md) once you need a deeper model understanding.
5. [Supported At-Rules](general/07-Supported-At-Rules.md) and [Supported Declarations](general/08-Supported-Declarations.md) when checking implementation coverage and constraints.
6. [Value Model](general/02-Values.md) and [Render Tree Examples](tutorials/04-Render-Tree.md) for style computation workflows.
6. [Value Model](general/02-Values.md) and [Render Tree Examples](tutorials/03-Render-Tree.md) for style computation workflows.
7. [Extensibility](general/05-Extensibility.md) and [Provided Services](general/06-Provided-Services.md) for custom integrations.
8. [FAQ](tutorials/03-Questions.md) for common pitfalls and output customization.
8. [FAQ](tutorials/04-Questions.md) for common pitfalls and output customization.

## What You Can Do With AngleSharp.Css

Expand Down
4 changes: 4 additions & 0 deletions docs/general/07-Supported-At-Rules.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
title: "Supported Rules"
section: "AngleSharp.Css"
---
# Supported At-Rules

This page documents the at-rules recognized by the AngleSharp.Css parser (`CssBuilder.AtRuleMap`) and the corresponding CSSOM rule implementations.
Expand Down
4 changes: 4 additions & 0 deletions docs/general/08-Supported-Declarations.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
title: "Supported Declarations"
section: "AngleSharp.Css"
---
# Supported Declarations

This page documents declaration/property support in AngleSharp.Css based on the registration in `DefaultDeclarationFactory` and declaration classes in `src/AngleSharp.Css/Declarations`.
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/02-Examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,5 @@ This preserves comments, but not their exact original positions in every case. D
## 11. Where To Go Next

- Read [API Documentation](01-API.md) for deeper CSSOM details.
- Read [Render Tree Examples](04-Render-Tree.md) for style-aware tree traversal and resource download workflows.
- Read [FAQ](03-Questions.md) for serializer behavior and common edge cases.
- Read [Render Tree Examples](03-Render-Tree.md) for style-aware tree traversal and resource download workflows.
- Read [FAQ](04-Questions.md) for serializer behavior and common edge cases.
File renamed without changes.
File renamed without changes.
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": "0.18.0",
"version": "1.0.0",
"preview": true,
"description": "The doclet for the AngleSharp.Css documentation.",
"keywords": [
Expand Down
43 changes: 19 additions & 24 deletions src/AngleSharp.Css/Parser/CssBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -887,34 +887,13 @@ private void CollectTrivia(ICssStyleSheet owner, ref CssToken token)
{
if (token.Type == CssTokenType.Comment)
{
var comment = NormalizeCommentData(token.Data);
_commentRuleTarget?.Add(new CssCommentRule(owner, comment));
_commentRuleTarget?.Add(new CssCommentRule(owner, token.Data));
}

token = _tokenizer.Get();
}
}

private static String NormalizeCommentData(String comment)
{
if (!String.IsNullOrEmpty(comment) && comment.StartsWith("/*", StringComparison.Ordinal) && comment.EndsWith("*/", StringComparison.Ordinal))
{
return comment.Substring(2, comment.Length - 4);
}

if (!String.IsNullOrEmpty(comment) && comment.StartsWith("/*", StringComparison.Ordinal))
{
return comment.Substring(2);
}

if (!String.IsNullOrEmpty(comment) && comment.EndsWith("*/", StringComparison.Ordinal))
{
return comment.Substring(0, comment.Length - 2);
}

return comment;
}

private void SkipDeclarations(CssToken token)
{
RaiseErrorOccurred(CssParseError.InvalidToken, token.Position);
Expand All @@ -932,9 +911,25 @@ private String CreateValue(ref CssToken token, out Boolean important)
{
var keyword = CssKeywords.BangImportant;
var value = _tokenizer.ContentFrom(token.Position.Position);
important = value.EndsWith(keyword, StringComparison.OrdinalIgnoreCase);
var keywordStart = value.Length - keyword.Length;
important = keywordStart >= 0 &&
value[keywordStart] == Symbols.ExclamationMark &&
String.Compare(value, keywordStart, keyword, 0, keyword.Length, StringComparison.OrdinalIgnoreCase) == 0;
token = NextToken();
return important ? value.Substring(0, value.Length - keyword.Length).Trim() : value;

if (!important)
{
return value;
}

var end = keywordStart;

while (end > 0 && value[end - 1].IsSpaceCharacter())
{
end--;
}

return value.Substring(0, end);
}

private String GetArgument(ref CssToken token)
Expand Down
12 changes: 6 additions & 6 deletions src/AngleSharp.Css/Parser/CssTokenExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace AngleSharp.Css.Parser
/// </summary>
static class CssTokenExtensions
{
public static Boolean IsPotentiallyNested(this CssToken token)
public static Boolean IsPotentiallyNested(this in CssToken token)
{
if (token.Is(CssTokenType.Hash, CssTokenType.Colon, CssTokenType.SquareBracketOpen))
{
Expand All @@ -35,7 +35,7 @@ public static Boolean IsPotentiallyNested(this CssToken token)
/// <param name="a">The first type to match.</param>
/// <param name="b">The alternative match for the token.</param>
/// <returns>Result of the examination.</returns>
public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b)
public static Boolean Is(this in CssToken token, CssTokenType a, CssTokenType b)
{
var type = token.Type;
return type == a || type == b;
Expand All @@ -49,7 +49,7 @@ public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b)
/// <param name="b">The 2nd type to match.</param>
/// <param name="c">The 3rd type to match.</param>
/// <returns>Result of the examination.</returns>
public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, CssTokenType c)
public static Boolean Is(this in CssToken token, CssTokenType a, CssTokenType b, CssTokenType c)
{
var type = token.Type;
return type == a || type == b || type == c;
Expand All @@ -68,7 +68,7 @@ public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, Cs
/// <param name="g">The 7th type to match.</param>
/// <param name="h">The 8th type to match.</param>
/// <returns>Result of the examination.</returns>
public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, CssTokenType c, CssTokenType d, CssTokenType e, CssTokenType f, CssTokenType g, CssTokenType h)
public static Boolean Is(this in CssToken token, CssTokenType a, CssTokenType b, CssTokenType c, CssTokenType d, CssTokenType e, CssTokenType f, CssTokenType g, CssTokenType h)
{
var type = token.Type;
return type == a || type == b || type == c || type == d || type == e || type == f || type == g || type == h;
Expand All @@ -82,7 +82,7 @@ public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, Cs
/// <param name="a">The first type to unmatch.</param>
/// <param name="b">The alternative unmatch for the token.</param>
/// <returns>Result of the examination.</returns>
public static Boolean IsNot(this CssToken token, CssTokenType a, CssTokenType b)
public static Boolean IsNot(this in CssToken token, CssTokenType a, CssTokenType b)
{
var type = token.Type;
return type != a && type != b;
Expand All @@ -97,7 +97,7 @@ public static Boolean IsNot(this CssToken token, CssTokenType a, CssTokenType b)
/// <param name="b">The alternative unmatch for the token.</param>
/// <param name="c">The final unmatch for the token.</param>
/// <returns>Result of the examination.</returns>
public static Boolean IsNot(this CssToken token, CssTokenType a, CssTokenType b, CssTokenType c)
public static Boolean IsNot(this in CssToken token, CssTokenType a, CssTokenType b, CssTokenType c)
{
var type = token.Type;
return type != a && type != b && type != c;
Expand Down
50 changes: 27 additions & 23 deletions src/AngleSharp.Css/Parser/CssTokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public CssTokenizer(TextSource source)
public CssToken Get()
{
var current = GetNext();
_position = GetCurrentPosition();
return Data(current);
}

Expand All @@ -70,27 +69,37 @@ public String ContentFrom(Int32 position)

while (current != Symbols.EndOfFile)
{
if (current is Symbols.DoubleQuote or Symbols.SingleQuote && previous != Symbols.ReverseSolidus)
if (current == Symbols.Semicolon || current == Symbols.CurlyBracketOpen || current == Symbols.CurlyBracketClose)
{
break;
}

if ((current == Symbols.DoubleQuote || current == Symbols.SingleQuote) && previous != Symbols.ReverseSolidus)
{
trailingWhitespace = 0;
var quote = current;
sb.Append(current);
previous = current;
current = GetNext();

while (current != Symbols.EndOfFile && current != quote)
{
sb.Append(current);

if (current == Symbols.ReverseSolidus)
{
sb.Append(current);
previous = current;
current = GetNext();

if (current == Symbols.EndOfFile)
{
break;
}

sb.Append(current);
}

sb.Append(current);
previous = current;
current = GetNext();
}

Expand All @@ -100,27 +109,23 @@ public String ContentFrom(Int32 position)
previous = current;
current = GetNext();
}

continue;
}
else if (current is Symbols.Semicolon or Symbols.CurlyBracketOpen or Symbols.CurlyBracketClose)

sb.Append(current);

if (current == Symbols.Space || current == Symbols.Tab || current == Symbols.LineFeed || current == Symbols.CarriageReturn || current == Symbols.FormFeed)
{
break;
trailingWhitespace++;
}
else
{
sb.Append(current);

if (current.IsSpaceCharacter())
{
trailingWhitespace++;
}
else
{
trailingWhitespace = 0;
}

previous = current;
current = GetNext();
trailingWhitespace = 0;
}

previous = current;
current = GetNext();
}

if (trailingWhitespace > 0)
Expand Down Expand Up @@ -603,24 +608,23 @@ private CssToken HashRest()
private CssToken Comment()
{
var current = GetNext();
StringBuffer.Append(Symbols.Solidus).Append(Symbols.Asterisk);

while (current != Symbols.EndOfFile)
{
StringBuffer.Append(current);

if (current == Symbols.Asterisk)
{
current = GetNext();
StringBuffer.Append(current);

if (current == Symbols.Solidus)
{
return NewComment(FlushBuffer());
}

StringBuffer.Append(Symbols.Asterisk).Append(current);
}
else
{
StringBuffer.Append(current);
current = GetNext();
}
}
Expand Down
40 changes: 36 additions & 4 deletions src/AngleSharp.Css/Parser/Micro/IdentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,25 @@ public static ICssValue ParseConstant<T>(this StringSource source, IDictionary<S
/// </summary>
public static Boolean IsFunction(this StringSource source, String name)
{
var pos = source.Index;
var rest = source.Content.Length - source.Index;

if (rest >= name.Length + 2)
{
var content = source.Content;
var hasEscape = content.IndexOf(Symbols.ReverseSolidus, pos, name.Length) >= 0;

if (!hasEscape &&
content[pos + name.Length] == Symbols.RoundBracketOpen &&
String.Compare(content, pos, name, 0, name.Length, StringComparison.OrdinalIgnoreCase) == 0)
{
source.NextTo(pos + name.Length + 1);
source.SkipSpacesAndComments();
return true;
}

var length = 0;
var current = source.Current;
var pos = source.Index;

while (length < name.Length)
{
Expand All @@ -122,7 +134,7 @@ public static Boolean IsFunction(this StringSource source, String name)
current = next[0];
}

if (Char.ToLowerInvariant(current) != Char.ToLowerInvariant(name[length]))
if (!EqualsIgnoreCase(current, name[length]))
break;

length++;
Expand Down Expand Up @@ -245,18 +257,38 @@ public static String ParseLiteral(this StringSource source)
public static String ParseAnimatableIdent(this StringSource source)
{
var pos = source.Index;
var test = source.ParseNormalizedIdent();
var test = source.ParseIdent();

//TODO Replace Animatables with call to DeclarationFactory - get from flags
if (test != null && (test.Isi(CssKeywords.All) || Animatables.Contains(test)))
{
return test;
return test.ToLowerFast();
}

source.BackTo(pos);
return null;
}

private static Boolean EqualsIgnoreCase(Char a, Char b)
{
if (a == b)
{
return true;
}

if (a >= 'A' && a <= 'Z')
{
a = (Char)(a + 32);
}

if (b >= 'A' && b <= 'Z')
{
b = (Char)(b + 32);
}

return a == b;
}

private static String Start(StringSource source, Char current, StringBuilder buffer)
{
if (current == Symbols.Minus)
Expand Down
12 changes: 11 additions & 1 deletion src/AngleSharp.Css/Parser/Micro/NumberParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,17 @@ public static class NumberParser
}

var negative = value.StartsWith("-");
var allNumbers = (negative ? value.Substring(1) : value).All(m => m.IsDigit());
var allNumbers = true;
var start = negative ? 1 : 0;

for (var i = start; i < value.Length; i++)
{
if (!value[i].IsDigit())
{
allNumbers = false;
break;
}
}

if (allNumbers)
{
Expand Down
Loading
Loading