ScreenshotNeo

BlogHow-to

How to Load CSS from a String in C#

Learn when to parse CSS as CSSOM, load HTML containing a style block, or add styles to an AngleSharp document in C#.

By the ScreenshotNeo team29 September 20269 min read

How to Load CSS from a String in C#

To load CSS from a string in C#, first identify the input and the result you need:

  • CSS text to rules or CSSOM: use the companion AngleSharp.Css package and its CSS parser.
  • HTML text that contains CSS: load the HTML string into an AngleSharp document, usually with OpenAsync(req => req.Content(html)) or ParseDocument(html).
  • CSS attached to a document: put the text in a <style> element and enable the CSS service with WithCss().

These operations are related, but they produce different objects. Parsing a stylesheet string gives you rules; loading HTML gives you a DOM; adding a style element associates CSS with that DOM. The examples below show each route and explain when to choose it.

1. Choose the operation before writing code

Goal Recommended route Result
Inspect selectors, declarations, and rules AngleSharp.Css parser Stylesheet/CSSOM objects
Parse an HTML fragment or page supplied as a string AngleSharp document input IDocument and DOM nodes
Carry CSS with the HTML document Inline <style> plus WithCss() Document with style sheets available to the configured services
Use XPath or XSLT over HTML Html Agility Pack LoadHtml HTML DOM

AngleSharp’s basics documentation covers both ParseDocument(source) and the asynchronous content pattern. Its examples configure CSS with Configuration.Default.WithJs().WithCss() and include a <style> element. The Html Agility Pack documentation describes LoadHtml for HTML strings; it does not establish that API as a CSS stylesheet parser. See the AngleSharp documentation and the AngleSharp repository for the current package surface.

A CSS string can become stylesheet rules, a style element, or part of an HTML document depending on the API you choose.
A CSS string can become stylesheet rules, a style element, or part of an HTML document depending on the API you choose.

2. Install AngleSharp and the CSS companion

Create a console project and add packages that match one another. The CSS project is maintained separately from AngleSharp core, so check the compatible version before upgrading.

dotnet new console -n CssStringDemo
cd CssStringDemo
dotnet add package AngleSharp
dotnet add package AngleSharp.Css

The core repository currently lists targets including netstandard2.0, net8.0, and net10.0, with additional Windows targets. Verify the target framework and the exact AngleSharp.Css release in your project before relying on a method signature shown in third-party API references.

3. Load HTML that contains CSS

This is the most common interpretation of “load CSS from a string” when the input is a complete HTML page. Put the CSS in a style element, then parse the HTML.

using AngleSharp;
using AngleSharp.Dom;
using System;
using System.Threading.Tasks;

const string css = "body { color: #222; } .notice { font-weight: 700; }";
const string html = $"<!doctype html><html><head><style>{css}</style></head><body><p class='notice'>Hello</p></body></html>";

var context = BrowsingContext.New(Configuration.Default);
IDocument document = await context.OpenAsync(req => req.Content(html));

Console.WriteLine(document.Title);
Console.WriteLine(document.QuerySelector("style")?.TextContent);
Console.WriteLine(document.QuerySelector(".notice")?.TextContent);

OpenAsync is useful when your pipeline is already asynchronous or when the source may later come from a stream or request abstraction. For a synchronous, in-memory string, the direct parser is shorter:

using AngleSharp;
using AngleSharp.Dom;

var document = BrowsingContext.New(Configuration.Default)
    .GetService<AngleSharp.Html.Parser.IHtmlParser>()!
    .ParseDocument("<html><head><style>.card { padding: 1rem; }</style></head><body><div class='card'>Card</div></body></html>");

If you use the parser directly, keep the version-specific namespace and construction pattern from the package installed in your project. The documented high-level alternatives are OpenAsync(req => req.Content(source)) and ParseDocument(source).

4. Enable CSS services when you need styling support

Parsing a <style> element into the DOM is different from asking AngleSharp to provide CSS styling services. Configure the CSS extension when your code needs style-sheet objects or computed-style related services.

using AngleSharp;
using AngleSharp.Dom;
using System.Threading.Tasks;

var configuration = Configuration.Default
    .WithCss();
var context = BrowsingContext.New(configuration);

var html = "<html><head><style>.warning { color: red; }</style></head><body><div class='warning'>Warning</div></body></html>";
IDocument document = await context.OpenAsync(req => req.Content(html));

var styleElement = document.QuerySelector("style");
Console.WriteLine(styleElement?.TextContent);

Add WithJs() only when scripts are part of your use case. JavaScript and CSS services increase the amount of work performed and do not turn AngleSharp into a complete pixel-rendering browser.

5. Parse a CSS stylesheet string as CSSOM

When the input is only CSS, do not wrap it in fake HTML unless you actually need a document. Use AngleSharp.Css. Its documented ICssParser API parses a stylesheet from a string and exposes rule collections and insertion methods such as Insert(string rule, int index).

The exact parser factory and namespace can vary by the installed companion-package version. The API reference captured for AngleSharp.Css 1.0.0-alpha-99 is not a promise that a current release has identical constructors. Inspect IntelliSense or the package’s XML documentation after installation, then use the equivalent pattern:

string css = "body { margin: 0; } .button { color: white; background: navy; }";

// Use the ICssParser implementation supplied by the AngleSharp.Css
// version referenced by your project.
// The parser operation is conceptually:
// var sheet = parser.ParseStylesheet(css);
// foreach (var rule in sheet.Rules)
//     Console.WriteLine(rule.CssText);

That version check matters because the public API has been marked alpha in some references. Treat package documentation shipped with your dependency as authoritative for the constructor and return type. The important distinction remains stable: a CSS parser returns stylesheet rules, while an HTML parser returns a document tree.

6. Add or replace CSS after parsing

You can create a style element and append it to an existing document. This is convenient for generated themes, test fixtures, or user-selected color schemes.

using AngleSharp;
using AngleSharp.Dom;
using System.Threading.Tasks;

var context = BrowsingContext.New(Configuration.Default.WithCss());
var document = await context.OpenAsync(req => req.Content("<html><head></head><body><main>Content</main></body></html>"));

var style = document.CreateElement("style");
style.TextContent = "main { max-width: 60rem; margin: 0 auto; }";
document.Head!.AppendChild(style);

Console.WriteLine(document.Head.InnerHtml);

Use TextContent rather than concatenating untrusted text into markup. If CSS comes from users, apply an allowlist or sanitizer appropriate to your application; CSS can contain external resource references and should not be treated as harmless formatting input.

7. HTML parser versus browser renderer

AngleSharp gives you standards-oriented HTML, DOM, and CSS services for parsing and inspection. The examples in its documentation demonstrate document parsing and style services; they do not guarantee complete browser layout or pixel rendering. If you need screenshots, font loading, animations, or browser-specific layout behavior, use a browser engine or a screenshot service.

For a DOM inspection task, stay in AngleSharp. For a visual capture task, a browser must load the page and settle resources before the image is taken.

8. Troubleshooting

“The CSS parser type cannot be found”

Cause: only AngleSharp is installed, or the companion package version is incompatible.

Fix: add AngleSharp.Css, align package versions, run dotnet restore, and inspect the installed assembly’s namespaces. Do not copy a constructor from an API page for a different release.

“My CSS string appears as text, but styles do nothing”

Cause: the text was placed in a normal element or the CSS service was not configured.

Fix: put the string in a <style> element inside <head> and configure Configuration.Default.WithCss() when style services are required.

Selectors return no elements

Cause: the HTML string is malformed, the selector is evaluated before the element is added, or the class/id differs from the CSS.

Fix: print document.DocumentElement.OuterHtml, validate the input, and query after appending nodes.

Styles differ from Chrome

Cause: a parser and CSS service are not the same as a full browser engine. Missing fonts, JavaScript-generated markup, viewport rules, and unsupported layout features can change the result.

Fix: use a real browser for rendering, or reduce the requirement to DOM and rule inspection.

CSS contains URLs or imports that do not resolve

Cause: an in-memory document has no useful base URL, and external resources may be intentionally disabled.

Fix: provide an appropriate document URL and resource loader configuration for your package version, or inline the dependencies. Keep network access controlled when processing untrusted CSS.

9. Performance, reliability, and cost

  • Reuse contexts carefully: creating a context per document is simple; a long-lived context can reduce setup overhead but requires isolation between tenants and careful disposal.
  • Keep strings bounded: reject unexpectedly large HTML or CSS before parsing to avoid memory pressure.
  • Separate parse and render stages: parse once when you only need selectors or rules; invoke a browser only for visual output.
  • Make failures observable: log input size, parser exceptions, and the package version. Never log secrets embedded in generated CSS.
  • Expect version drift: pin compatible AngleSharp and AngleSharp.Css versions and review release notes before upgrading.

AngleSharp itself has no network charge for parsing an in-memory string. A browser or hosted screenshot API adds resource loading, execution, and service costs, so choose the smallest tool that produces the required result.

10. Or skip the browser setup

If your actual goal is a clean image or PDF of a page after its CSS has loaded, ScreenshotNeo provides a single HTTP request. It handles the browser session and returns PNG, JPEG, WebP, or PDF. Before capture it accepts consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the verdict with X-Page-Verdict and X-Billed headers.

A screenshot workflow can remove obstructing overlays before the page is captured.
A screenshot workflow can remove obstructing overlays before the page is captured.

See the ScreenshotNeo API documentation for all options, including full-page capture, CSS selector element capture, dark mode, device presets, retina scale, PDF paper and margin settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous webhooks, bulk capture, usage data, and the OpenAPI specification.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const body = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', body);

ScreenshotNeo also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

11. FAQ

Can I use Html Agility Pack to parse CSS?

Its documented LoadHtml API is for loading HTML strings and working with an HTML DOM. Use AngleSharp.Css when you need a stylesheet parser.

Should I use ParseDocument or OpenAsync?

Both accept in-memory HTML. Use the asynchronous request pattern when your surrounding pipeline is asynchronous; use the direct parser for a small synchronous utility.

Does adding CSS make AngleSharp render a screenshot?

No. CSS services help parse and model styles. Pixel output requires a browser rendering engine or a screenshot service.

How do I support a dark theme?

Generate a second style string or use a media query in the style element. For a visual capture, pass the dark-mode option to the rendering system you choose.

What should I pin in production?

Pin compatible versions of AngleSharp and AngleSharp.Css, test your target framework, and verify parser APIs after upgrades.