CSS4J User Guide

Carlos Amengual

This project is an implementation of W3C's CSS Object Model API in the Java™ language, and also adds CSS support to the DOM4J package. It targets several different use cases, with functionalities from style sheet error detection to style computation.


This implementation can be used in several ways: with stand-alone style sheets, with its own DOM implementation, combined with DOM4J, or by wrapping a pre-existing DOM tree.

You can play with independent style sheets created with the createStyleSheet("title", "media") method of the CSSStyleSheetFactory interface. There are three implementations of that interface:

The document back-end is only important if you plan to use the sheets inside a document. The resulting style sheets are empty, but you can load a style sheet with the AbstractCSSStyleSheet.parseStyleSheet(reader) method (see Parsing a Style Sheet).

One of the most important functionalities in the library is the ability to compute styles for a given element:

In practice, to obtain the 'computed' or 'used' values required for actual rendering a box model implementation is needed, and also device information. The library provides a simple box model that could be used, but the details of the rendering device can be more difficult.

Depending on the use case, the target device may not be the same one where the library is running (and some exact details hence not available). To help in the computation, the library defines the DeviceFactory interface to supply device and media-specific data (also provides the objects required by media queries to work).


The library is organized in several modules:

  • css4j core module, which depends on the next two:
  • carte-util, a very small collection of interfaces and utility classes.
  • tokenproducer, the low-level parser at the core of css4j.
  • css4j-agent, a collection of agent-related classes that makes the processing of remote documents a bit easier.
  • css4j-awt, a small set of AWT-related utility classes.
  • css4j-dom4j: to use css4j together with dom4j.
  • xml-dtd is a small set of classes to aid in the processing of XML DTDs, including the useful DefaultEntityResolver.

If you use Apache Maven, you may want to look at each module's POM files to check for the specific dependencies.

Using css4j's native DOM implementation

You can create a DOM document from scratch and use the related DOM methods to programatically build a document: just use the provided DOM implementation (io.sf.carte.doc.dom.CSSDOMImplementation). But if you want to parse an existing document, the procedure depends on the type of document: to parse an XML document (including XHTML documents), you can use this library's XMLDocumentBuilder, while to parse an HTML one (or an XHTML document that does not use namespace prefixes) you can use the validator.nu HTML5 parser (be sure to use the latest version from the nu.validator Maven group id).

An example with that parser follows:

import java.io.Reader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import io.sf.carte.doc.dom.CSSDOMImplementation;
import io.sf.carte.doc.dom.DOMElement;
import io.sf.carte.doc.dom.HTMLDocument;
import io.sf.carte.doc.style.css.CSSComputedProperties;
import io.sf.carte.doc.style.css.CSSTypedValue;
import io.sf.carte.doc.style.css.RGBAColor;
import io.sf.carte.doc.xml.dtd.DefaultEntityResolver;
import nu.validator.htmlparser.dom.HtmlDocumentBuilder;

// Instantiate DOM implementation (with default settings: no IE hacks accepted) 
// and configure it
CSSDOMImplementation impl = new CSSDOMImplementation();
// Alternatively, impl = new CSSDOMImplementation(flags);
// Now load default HTML user agent sheets
// Prepare builder
HtmlDocumentBuilder builder = new HtmlDocumentBuilder(impl);
// Read the document to parse, and prepare source object
Reader re = ... [reader for HTML document]
InputSource source = new InputSource(re);
// Parse. If the document is not HTML, you want to use DOMDocument instead
HTMLDocument document = (HTMLDocument) builder.parse(source);
// Set document URI

Then you have a CSS-enabled document. To compute styles, use getComputedStyle:

DOMElement element = document.getElementById("someId");
CSSComputedProperties style = element.getComputedStyle(null);

// Next line could be 'String display = style.getDisplay();'
String display = style.getPropertyValue("display");

// If you use a factory that has been set to setLenientSystemValues(false), next
// line may throw an exception if the 'color' property was not specified.
// The default value for lenientSystemValues is TRUE.
RGBAColor color = ((CSSTypedValue) style.getPropertyCSSValue("color")).toRGBColorValue();

// Suppose that the linked style sheet located at 'css/sheet.css' declares:
// background-image: url('foo.png');

String image_css = style.getPropertyValue("background-image");
String image_uri = ((CSSTypedValue) style.getPropertyCSSValue("background-image")).getStringValue();

// Then, because we already set the document URI to "http://www.example.com/mydocument.html",
// image_css will be set to "url('http://www.example.com/css/foo.png')",
// and image_uri to "http://www.example.com/css/foo.png"

The CSSComputedProperties interface extends W3C's CSSStyleDeclaration for computed styles, adding methods like getComputedFontSize() or getComputedLineHeight().

If you are computing styles for a specific medium, tell the document about it (see "Media Handling"):


Conformance with the DOM specification

This library's native DOM implementation has some minor behavior differences with what is written in the DOM Level 3 Core Specification. For example, on elements and attributes the Node.getLocalName() method returns the tag name instead of null when the node was created with a DOM Level 1 method such as Document.createElement(). Read the io.sf.carte.doc.dom package description for additional information.

Short example with DOM4J

To use the library with dom4j (using dom4j-like documents, elements and factory) you need the css4j-dom4j module in addition to the core module. The -dom4j module optionally depends on the -agent module, so you do not need the latter unless you plan to use the DOM4J agent.

This is the easiest way to use this package with DOM4J, using that library's SAXReader:

Reader re = ... [reader for XHTML document]
InputSource source = new InputSource(re);
SAXReader reader = new SAXReader(XHTMLDocumentFactory.getInstance());
reader.setEntityResolver(new DefaultEntityResolver());
XHTMLDocument document = (XHTMLDocument) reader.read(source);

And once you got the element you want the computed style for (see, for example, the DOM4J Quick Start Guide), just get it with a procedure analogous to the ViewCSS interface:

CSSComputedProperties style = ((CSSStylableElement) element).getComputedStyle(null);
String display = style.getPropertyValue("display");

Be careful to have the XHTMLDocumentFactory loaded with the desired defaults, like the user agent style sheet which was loaded by default in 1.0 but not in 2.0.

It is also possible to parse an HTML5 document into a css4j-dom4j tree with the aforementioned validator.nu HTML5 parser:

XHTMLDocumentFactory factory = XHTMLDocumentFactory.getInstance();
// Next line is optional: default is TRUE, and is probably what you want
// factory.getStyleSheetFactory().setLenientSystemValues(false);
HtmlDocumentBuilder builder = new HtmlDocumentBuilder(factory);
// We do not set the EntityResolver, the HtmlDocumentBuilder does not need it
Reader re = ... [reader for HTML document]
InputSource source = new InputSource(re);
XHTMLDocument document = (XHTMLDocument) builder.parse(source);

Or use a SAX parser to parse an XML document into a css4j-dom4j tree, with XMLDocumentBuilder instead of dom4j's SAXReader:

XHTMLDocumentFactory factory = XHTMLDocumentFactory.getInstance();
// Next line is optional: default is TRUE, and is probably what you want
// factory.getStyleSheetFactory().setLenientSystemValues(false);
XMLDocumentBuilder builder = new XMLDocumentBuilder(factory);
builder.setEntityResolver(new DefaultEntityResolver());
Reader re = ... [reader for XML document]
InputSource source = new InputSource(re);
XHTMLDocument document = (XHTMLDocument) builder.parse(source);

The code above uses the default JAXP SAX parser. You could use a different SAXParserFactory:

SAXParserFactory parserFactory = ...
XMLDocumentBuilder builder = new XMLDocumentBuilder(factory, parserFactory);

Usage with the DOM wrapper

If you choose to build your document with your favorite DOM implementation instead of the CSS4J one or the DOM4J back-end, you can use the DOMCSSStyleSheetFactory.createCSSDocument(document) method to wrap a pre-existing DOM Document. Example:

DOMCSSStyleSheetFactory cssFactory = new DOMCSSStyleSheetFactory();
CSSDocument document = cssFactory.createCSSDocument(otherDOMdocument);

Unlike the native DOM or the DOM4J back-end, the DOM resulting from the DOM wrapper is read-only, although you can change the values of some nodes.

Consistency of different DOM implementations

Beware that the computed styles found by each method (native DOM, DOM4J back-end or DOM wrapper) may not be completely identical, due to differences in the underlying document DOM implementations. Behaviour may vary due to, for example, a pseudo-class like :target being used (DOM4J has no documentURI support so it never matches).

Such cases would be rare, though (no real-world tests have shown that). If you find a difference in styles computed from different back-ends that you believe to be a bug, please report.

Configuring the cascade

Depending on your use case, you may need to set the user agent (UA) style sheet. The library supports two different UA sheets, one for STRICT ('standards') mode and another for QUIRKS. To set either of those sheets, first obtain an instance of the factory that you are using:

// Instantiate the new factory or get it from an object that you are already using.
AbstractCSSStyleSheetFactory cssFactory = ...

If you are using the DOM4J classes, you may want to do:

AbstractCSSStyleSheetFactory cssFactory = XHTMLDocumentFactory.getInstance().getStyleSheetFactory();

If you are processing HTML, css4j's default HTML5 UA sheet (based on W3C/WHATWG recommendations) should be appropriate for you:


But if you want to set your own UA sheet, first obtain a reference to the sheet:

BaseCSSStyleSheet sheet = cssFactory.getUserAgentStyleSheet(CSSDocument.ComplianceMode.STRICT);

This is assuming the STRICT mode, i.e. that you use documents with a DOCTYPE, otherwise use QUIRKS (or you may want to set both UA sheets).

If the UA sheet already contains rules (it is empty by default), clean it:


And now load the new sheet:

Reader reader = ... [reads the UA sheet]
sheet.parseStyleSheet(reader, CSSStyleSheet.COMMENTS_IGNORE);

As the UA sheet's comments are rarely of interest at the OM level, they were ignored during the parse process (notice the COMMENTS_IGNORE flag).

Note: this implementation does not support important style declarations in the UA sheet.

Setting the user style sheet

There is also the possibility to set a user style sheet (with 'user' origin) via setUserStyleSheet:

Reader reader = ... [reads the user sheet]

Both important and normal declarations are supported in the user style sheet.

Media handling

By default, computed styles only take into account generic styles that are common to all media. If you want to target a more specific medium, you have to use the CSSDocument.setTargetMedium("medium") method. For example, if your document has the following style sheets linked:

<link href="http://www.example.com/css/sheet.css" rel="stylesheet" type="text/css" />
<link href="http://www.example.com/css/sheet_for_print.css" rel="stylesheet" media="print" type="text/css" />

Computed styles will initially take into account only the "sheet.css" style sheet. However, if you execute the following method:


Then all subsequently computed styles will account for the merged style sheet from "sheet.css" and "sheet_for_print.css".

This way to tie a document with a medium is not totally standard, as the W3C APIs would probably expect a DeviceFactory-related object implementing the ViewCSS interface and referencing the document, but this approach allows to isolate DOM logic inside DOM objects and keep the DeviceFactory for media-specific information only.

Style sheet sets

The library supports alternative style sheets: see CSSDocument's methods enableStyleSheetsForSet, getStyleSheetSets, getSelectedStyleSheetSet and setSelectedStyleSheetSet. For example, if you have a document with these linked sheets:

<link href="http://www.example.com/commonsheet.css" rel="stylesheet" type="text/css" />
<link href="http://www.example.com/alter1.css" rel="alternate stylesheet" type="text/css" title="Alter 1" />
<link href="http://www.example.com/alter2.css" rel="alternate stylesheet" type="text/css" title="Alter 2" />
<link href="http://www.example.com/default.css" rel="stylesheet" type="text/css" title="Default" />

Initially, sheets 'alter1.css' and 'alter2.css' will not be used to compute styles. But then you can write code like the following:

String defset = document.getSelectedStyleSheetSet();  // Sets 'defset' to "Default"
document.setSelectedStyleSheetSet("Alter 1");  // Selects the set with title "Alter 1"
document.setSelectedStyleSheetSet("Alter 2");  // Selects the set with title "Alter 2"

These methods have been removed from the DOM standard unfortunately, but you can still read the specification at the CSSOM 5 December 2013 Working Draft.

Style sheet error checking

You can check for errors and warnings in the document's sheets using the non-standard getErrorHandler method. Example:

if (document.getStyleSheets().item(0).getErrorHandler().hasSacErrors())
	... error processing / reporting

The merged style sheet obtained from the getStyleSheet method has the merged error/warning state from the document's active sheets.

A typical source of errors are the non-compliant IE hacks, like prefixing property names with an asterisk (you may want to use the proper NSAC flags when creating the factory, see Compatibility with legacy browsers), or charset rules found in the wrong place.

Override styles

Override styles, that come after the author style sheet in the cascade algorithm, are not part of the formal standard but are supported by this library; use CSSElement's getOverrideStyle. For example:

element.getOverrideStyle(null).setCssText("padding: 6pt;");

Override styles are defined at the DocumentCSS interface description.

Parsing a style sheet

Although document's style sheet fetching and parsing is automatic with this library (for sheets referenced from a document), it is possible to manually parse rules from a source stream with the CSSStyleSheet.parseStyleSheet(Reader, short) method:

Reader re = ...
sheet.parseStyleSheet(re, CSSStyleSheet.COMMENTS_AUTO);

When the second argument is COMMENTS_IGNORE, the comments in the source stream are ignored when parsing (see Accessing Style Sheet Comments).

The new CSS rules found in the source stream are added to the already present ones, i.e. the sheet is not reset by this method (although the error handler is), so if you want to refill a sheet you need to clear the rules before parsing:


Compatibility with legacy browsers

Today's style sheets often contain non-conformant styles that target specific versions of old web browsers, like Internet Explorer. Several web sites contain information about that, including:

Although it could be argued whether those hacks should be used or not, the point is that actual style sheets do contain them, so this library supports them.

By default, a factory is configured to use a flagless NSAC parser which would produce an error on any of those non-standard constructs, but a set of compatibility flags can be specified in the constructors for the factory implementations. The different flags are documented in the NSAC javadocs.

The flags available at the time of this guide's update are the following:

  • STARHACK. When set, the parser will handle asterisk-prefixed property names as accepted, normal names.
  • IEVALUES supports values ending with \9 or \0, as well as progid filters and IE expressions.
  • IEPRIO allows values ending with the '!ie' priority hack.
  • IEPRIOCHAR accepts values with an '!important!' priority hack (note the '!' at the end).

The object model manages these compatibility values in parallel to standard ones. For example, after parsing this declaration with IEVALUES set:

width: 900px; width: 890px\9;

its serialization would be identical (if the flag was set correctly):

width: 900px; width: 890px\9;

but the declaration's length shall be only 1. And computed styles only use the standard values unless there are no alternatives (no standard value was set). The workings are similar for IEPRIO and IEPRIOCHAR:

width: 890px !ie;
width: 890px !important!;

with the last one being handled as of important priority. Values created by IEPRIOCHAR are never used in computed styles.

Instead, declarations including asterisk-prefixed property names (created by STARHACK) always increase the declaration's length. For example, the length of the following declaration would be 2:

width: 900px;
*width: 890px;

If you want to use these flags at the NSAC level (instead of the Object Model), you may want to read the 'Parser Flags' section in the NSAC package description, as well as the documentation for the individual flags in Parser.Flag.

CSS style formatting

The serialization of the cssText attribute in rules and style declarations can be customized with an implementation of the StyleFormattingContext interface. You can set your StyleFormattingFactory (which produces your customized formatting context) to the sheet factory with the CSSStyleSheetFactory.setStyleFormattingFactory method, or subclass your style sheet factory and override the createDefaultStyleFormattingFactory method.

Look at the DefaultStyleFormattingContext class for an example of a formatting context implementation.

There is also the possibility to customize the default serialization of string values, with the CSSStyleSheetFactory.setFactoryFlag(byte) method. You can set two flags that govern which quotation you prefer, or keep the default behaviour:

  • Default: Try to keep the original quotation (single or double quotes), unless the alternative is more efficient.
  • STRING_DOUBLE_QUOTE: Use double quotes unless single quotes are more efficient (when the string contains more double quotes than single).
  • STRING_SINGLE_QUOTE: Use single quotes unless double quotes are more efficient (when the string contains more single quotes than double).

Accessing style sheet comments

CSS style sheets often have comments, like:

/* This is a preceding comment */
p {color: blue; } /* This is a trailing comment */

(XML-style comments can also be present in a style sheet, but both NSAC and the CSSOM skip them.)

There is no standard CSSOM API for accessing comments in style sheets, but the CSSRule.getPrecedingComments() and CSSRule.getTrailingComments() methods are provided for that:

StringList pcomments = document.getStyleSheets().item(0).getCssRules().item(3).getPrecedingComments();
StringList tcomments = document.getStyleSheets().item(0).getCssRules().item(3).getTrailingComments();

Other API models were considered for object-model comments, like having a special type of comment rule, but stylesheet-level comments are often related to a rule and if that rule is moved or deleted, those stand-alone comments would have the potential to create a lot of confusion.

By default, comments are parsed with the COMMENTS_AUTO mode, which should be appropriate for human-readable sheets like the one shown above. But a lot of sheets are serialized in a way that there are no newline characters (or only a few). For these cases, COMMENTS_PRECEDING could be used in parseStyleSheet(Reader,short), and all the comments will be considered as belonging to the next rule. With COMMENTS_IGNORE, all comments found while parsing the sheet will be ignored.

The comments preceding/trailing any rule will be included in the text returned by the sheet's AbstractCSSStyleSheet.toString() and toStyleString() methods, while other comments (located at places that cannot be easily related to a rule) are lost.

In css4j version 1.0, comments that were intended to apply to the previous rule may be assigned to the next one, but this is addressed in version 2.0 where NSAC 2.0 allows a more accurate approach.

Comments in the default HTML UA style sheet are not available, as the parser is instructed to ignore them when parsing.

Create an AWT font/color from a computed style (css4j-awt module)

Once you have a computed style, on systems where AWT is available you can create an AWT font or color with the createFont and getAWTColor static methods in the AWTHelper class:

CSSComputedProperties style = ...
java.awt.Font font = AWTHelper.createFont(style);
CSSTypedValue cssColor = (CSSTypedValue) style.getPropertyCSSValue("color");
java.awt.Color color = AWTHelper.getAWTColor(cssColor);

Note that the AWTHelper.getAWTColor method can create colors from a value coming from just a style declaration (the getPropertyCSSValue used above does not require the style to be computed).

These classes and methods belong to the css4j-awt module (the other css4j modules have no dependencies on AWT).

CSS3 support

CSS3 (defined as "all CSS after CSS2.1") is partially supported by the library; the following table summarizes the basic support for setting/retrieving the main CSS features:

CSS3 Spec NameSupportObservations
Background / BorderYes
ColorPartialSome level 4 color functions are not implemented yet; they can be parsed and serialized, but do not convert to RGB in computed styles.
Media QueriesPartialEvent handling with addListener/removeListener is not supported. Relies on CSSCanvas implementations to match/unmatch media features.
Grid / Template / AlignmentPartialLegacy gap properties (grid-row-gap, grid-column-gap, and grid-gap) are not supported, although the longhands can be used if declared explicitly).


The following pseudo-classes are supported by the library: first-child, last-child, only-child, nth-child(), nth-last-child(), first-of-type, last-of-type, only-of-type, nth-of-type(), nth-last-of-type(), link, visited, target, root, empty, blank, disabled, enabled, read-write, read-only, is, where, has, not, placeholder-shown, default, checked and indeterminate.

State pseudo-classes like hover are supported through the CSSCanvas.isActivePseudoClass(CSSElement, String) method.

Rendering-oriented interfaces

To help with the determination of 'used' values and the actual rendering, this library provides a few helper interfaces. The most important are:

  • DeviceFactory. It is not a "factory of devices", but instead the object that delivers the relevant abstractions for a requested medium: StyleDatabase and CSSCanvas.
  • StyleDatabase. Provides medium-specific information like available fonts and colors.
  • CSSCanvas. Has knowledge of medium-specific information that depends (or may depend) on a specific viewport, like supported media features. It is linked to a viewport, if there is any. This interface is used to determine the state of active pseudo-classes.
  • Viewport. It represents a viewport defined as per the CSS specifications.

The differences between style databases and canvases can be subtle, and for some media features it could be argued that they belong to one or the other. The basic idea is that style databases should be relatively easy to implement for a given medium, while canvases are probably only going to exist if there is an actual rendering engine implemented.

Java™ Runtime Environment requirements

The classes in the binary packages have been compiled with a Java compiler with 1.8 compiler compliance level, except for the module-info file which targets Java 11. If you are stuck with Java 7, you may want to use css4j 1.0.