Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Selectors, values, and at-rules

Selectors

Matching is delegated to the selectors crate from Servo, so CSS3 selectors mostly work as they are.

SelectorsSupportedNotes
Type (p) and universal (*)
Class (.foo) and ID (#foo)
Attribute ([a], [a=v], [a^=v], [a$=v], [a*=v], [a~=v], [a|=v])The case-insensitive i flag works too
Descendant (whitespace) and child (>)
Adjacent sibling (+) and general sibling (~)
Selector lists (,)
Namespaces (ns|E)@namespace itself is not supported

Pseudo-classes

Pseudo-classesSupportedNotes
:root
:first-child, :last-child, :only-childIn streaming mode :last-child never matches; see below
:nth-child(), :nth-last-child()As above; :nth-last-child() never matches in streaming mode
:first-of-type, :last-of-type, :only-of-type, :nth-of-type(), :nth-last-of-type()As above
:emptyAs above
:not()
:is(), :where(), :has()A parse error; the whole selector is ignored
:hover, :active, :focus, :focus-within, :focus-visible, :target, :enabled, :disabled, :checked, :visited⚠️They parse but never match, since a static PDF has no interaction state for them to describe
:link, :any-linkMatches an <a> that has an href

A selector containing an unsupported pseudo-class, such as :is(), causes the whole rule to be dropped. Ones that parse, such as :hover, survive as rules but never match.

Pseudo-elements

Pseudo-elementsSupportedNotes
::before, ::after⚠️Generated text through content only. It is drawn with the computed style of the host element and has no box style of its own, so margin, padding, display, and the like do not apply. Nothing is generated on an element that has block children
::first-letter⚠️Only font-family, font-size, font-weight, font-style, color, text-decoration-line, and text-transform can be overridden; float and the box model properties are not supported
::first-lineA parse error
::marker, ::selection, ::placeholderA parse error

Values, units, and functions

Lengths

UnitSupported
px, em, rem
mm, cm, in, pt, pc, Q
%, on properties that take a percentage
A bare 0
ex, ch, vw, vh, vmin, vmax, lh

Absolute units are read with one inch as 96px, so 10mm is 37.795px. Physical dimensions can be written directly for print, which means @page { size: 210mm 297mm; margin: 15mm; } works as written.

As an exception, size in @page also accepts page size keywords such as a4 and letter, and landscape or portrait.

Angles, used only by transform

deg, rad, grad, and turn are supported, as is a bare 0 ✅.

Function

FunctionSupportedNotes
calc()⚠️+, -, *, /, and nested parentheses. The terms may be lengths, whether absolute units or em and rem, percentages, and numbers. It works on any property that takes a length or a percentage
min(), max(), clamp()
var()⚠️Custom properties (--foo) are resolved by text substitution before parsing. Fallbacks such as var(--x, 10px) and references between custom properties work. Unlike the specification, this does not follow the cascade or inheritance; it is a simple resolution in which the last declaration in the document wins
url()In background-image, list-style-image, the src of @font-face, and @import. Relative URLs are resolved against <base href> or the location of the input
attr()⚠️Only inside content
counter(), counters()Inside content. The style in the second argument comes from the list-style-type values
Gradients such as linear-gradient()
env(), image-set(), element()

Colours

NotationSupported
Named colours, the CSS colour keywords such as red
#rgb, #rgba, #rrggbb, #rrggbbaa
rgb(), rgba(), both comma separated and space separated
hsl(), hsla(), hwb()
lab(), lch(), oklab(), oklch()✅ (converted to sRGB for drawing)
currentcolor, transparent
color(), such as color(display-p3 ...)
color-mix(), and the relative colour syntax rgb(from ...)

Colours with alpha are drawn through a PDF ExtGState, for both fills and backgrounds.

At-rules

At-rulesSupportedNotes
@media⚠️Only the media type is evaluated. A screen block, and any negation other than not screen, is ignored entirely, while print, all, and a missing type are applied. Feature queries such as (min-width: ...) are skipped without being evaluated, so the contents apply as long as the type matches
@page⚠️size, taking a keyword, one or two <length> values, or landscape and portrait, plus the margin properties. The pseudo-classes :first, :left, and :right are supported on their own; named pages such as @page intro, :blank, and compound pseudo-classes such as :first:left are not
The margin boxes inside @page⚠️All sixteen of them, @top-left-corner, @top-left, @top-center, @top-right, and so on. Only text from content is drawn; decoration such as background colours and borders is not supported. Page numbers can be printed with counter(page) and counter(pages), though counter(pages) is unavailable in streaming mode
@font-face⚠️The font-family, src, unicode-range, font-weight, and font-style descriptors are supported, including local() and url() with format() or tech() inside src. Other descriptors such as font-display are ignored. Font files may be TTF or OTF only; WOFF and WOFF2 are not supported
@importNesting is supported to a depth of 16, beyond which the offending import alone is ignored, and cycles are detected. A media condition such as @import url(...) screen; is not evaluated; the file is always imported
@charsetThe input is assumed to be UTF-8
@supports, @keyframes, @namespace, @counter-style, @layer, @container, @propertyThe whole block is ignored

Restrictions specific to streaming mode

In Mode::Batch the whole DOM is available, so none of this applies. The following holds only in Mode::Streaming.

  • Selectors that look backwards never match: :last-child, :last-of-type, :nth-last-child(), :nth-last-of-type(), and :empty. They cannot be decided until the parent’s child list is complete
  • A <style> tag after <body> starts is an error and returns EngineError::UnsupportedInStreamingMode. Keep all <style> in <head>, so that the layout does not quietly fall apart
  • position: absolute and fixed are ignored
  • counter(pages), the total page count, is unavailable