cssutils

A validating Python library for parsing, building, and serializing CSS Cascading Style Sheets through a DOM Level 2 API.

Library
PyPI
v2.15.0
92stars
LGPL-3.0-or-later

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity12
Maintenance32
Community52
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture74
Code Quality78
Innovation62
Learning Curve80

cssutils gives Python code a full DOM Level 2 CSS implementation: it parses stylesheets from a string, file, or URL into a tree of rule, selector, and property objects, then serializes that tree back to CSS text. It’s DOM-only — there’s no rendering engine here — so it’s aimed at programs that need to read, validate, transform, or generate CSS rather than display it.

The parser tracks parts of CSS 2.1, CSS3 Syntax, Selectors, Media Queries, Namespaces, and the early CSS Variables draft, and is deliberately tolerant of real-world CSS “in the wild” rather than strict spec compliance. Maintained under Jason R. Coombs’ jaraco skeleton with an active CI matrix spanning CPython 3.10-3.15, PyPy, and free-threaded builds, it ships three CLI utilities (csscapture, csscombine, cssparse) alongside the importable API.

What You Get

  • A CSSParser that reads CSS from a string, file, or URL (with a pluggable fetcher) into a navigable CSSStyleSheet DOM
  • Typed rule objects for every major at-rule and rule type — CSSStyleRule, CSSMediaRule, CSSImportRule, CSSNamespaceRule, CSSFontFaceRule, CSSPageRule, and more
  • A validating CSSSerializer with configurable preferences for re-emitting CSS text, including minified output
  • Selector and property-value parsing (selectorlist, selector, property, value) for inspecting and rewriting individual declarations
  • Three ready-to-run CLI scripts — csscapture, csscombine, cssparse — for capturing, combining, and parsing stylesheets from the command line
  • An ErrorHandler-based logging/exception system so parsing problems can be logged or raised depending on raiseExceptions

Common Use Cases

  • Programmatically rewriting property values (e.g. bulk color or unit changes) across a CSS file while preserving the rest of the sheet
  • Combining and inlining @import-linked stylesheets into a single file before deployment
  • Validating third-party or user-submitted CSS against DOM Level 2 / CSS3 grammar before accepting it
  • Extracting selectors, media queries, or namespaces from a stylesheet for static analysis or linting tools
  • Minifying CSS output by configuring the serializer’s preferences prior to writing cssText

Under The Hood

Architecture cssutils layers a hand-written tokenizer (tokenize2.Tokenizer, driven by the productions/macros defined in cssproductions.py) underneath a generic production-parser framework (prodparser.py’s ProdParser, Sequence, Choice, Prod) that both the css and stylesheets packages reuse to build their DOM classes; each rule type (css/cssstylerule.py, css/cssmediarule.py, css/cssimportrule.py, etc.) subclasses a common CSSRule and composes its own grammar out of that shared parser toolkit, so parsing and DOM construction happen in the same pass rather than as separate tree-building steps. CSSParser (parse.py) is the public entry point that wires a fetcher (URL/file resolution via helper.path2url) into this pipeline and hands back a populated CSSStyleSheet; a module-level errorhandler.ErrorHandler singleton (cssutils.log) is threaded through every layer so validation problems surface as either log records or exceptions depending on raiseExceptions. The design means changing the core tokenizer or ProdParser contract would ripple through every rule class in css/ and stylesheets/, since none of them re-implement their own lexing.

Tech Stack A pure-Python 3.10+ package with a minimal runtime footprint — only more_itertools and encutils (the maintainer’s own encoding-detection helper) as dependencies — built with setuptools>=77 plus setuptools_scm for version derivation and coherent.licensed for license metadata, per the jaraco/skeleton project template. It exposes three console-script entry points (csscapture, csscombine, cssparse) and integrates with xml.dom for its DOM-registration surface; there’s no web framework, ORM, or database involved since this is a standalone parsing/serialization library.

Code Quality Testing uses pytest (>=6, excluding 8.1.x) with a large, one-test-file-per-rule-type suite under tests/ (e.g. test_cssstylerule.py, test_cssmediarule.py, test_csspagerule.py) plus pytest-checkdocs, pytest-ruff for lint-as-test, and pytest-cov for coverage — all wired through pytest-enabler so they run as part of the default test invocation. Linting is enforced via ruff.toml; a mypy.ini exists but is explicitly disabled for this project per a skeleton-tracked issue, so type checking is not currently enforced despite the config being present. CI (.github/workflows/main.yml) runs the matrix across CPython 3.10 through 3.15 (including free-threaded builds), PyPy, and three platforms (Ubuntu/macOS/Windows), which is a notably wide compatibility net for a library this size.

What Makes It Unique Rather than treating CSS as text to regex over, cssutils implements an actual DOM Level 2 Style / DOM Level 2 Stylesheets object model with a validating parser, which is uncommon among Python CSS tools — most either wrap a C library for rendering or do lightweight tokenizing without a standards-based DOM. Its explicit design goal of parsing “real world” CSS hacks and non-conformant stylesheets rather than only well-formed input, combined with a reusable production-parser framework shared across every rule type, is the distinguishing architectural choice over a simpler one-off tokenizer-per-rule approach.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search