CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, in speech, etc. This module describes, in general terms, the basic structure and syntax of CSS stylesheets. It defines, in detail, the syntax and parsing of CSS - how to turn a stream of bytes into a meaningful stylesheet.
The following features are at risk: …
This section is not normative.
This module defines the abstract syntax and parsing of CSS stylesheets
and other things which use CSS syntax
(such as the HTML style attribute).
It defines algorithms for converting a stream of codepoints (in other words, text) into a stream of CSS tokens, and then further into CSS objects such as stylesheets, rules, and declarations.
This module defines the syntax and parsing of CSS stylesheets. It supersedes the lexical scanner and grammar defined in CSS 2.1.
This section is not normative.
A CSS document is a series of qualified rules, which are usually style rules that apply CSS properties to elements, and at-rules, which define special processing rules or values for the CSS document.
A qualified rule starts with a prelude then has a {}-wrapped block containing a sequence of declarations. The meaning of the prelude varies based on the context that the rule appears in - for style rules, it's a selector which specifies what elements the declarations will apply to. Each declaration has a name, followed by a colon and the declaration value, and finished with a semicolon.
A typical rule might look something like this:
p > a {
color: blue;
text-decoration: underline;
}
In the above rule, "p > a" is the selector,
which, if the source document is HTML,
selects any <a> elements that are children of a <p> element.
"color: blue;" is a declaration specifying that,
for the elements that match the selector,
their 'color' property should have the value ''blue''.
Similiarly, their 'text-decoration' property should have the value ''underline''.
At-rules are all different, but they have a basic structure in common. They start with an "@" character followed by their name. Some at-rules are simple statements, with their name followed by more CSS values to specify their behavior, and finally ended by a semicolon. Others are blocks; they can have CSS values following their name, but they end with a {}-wrapped block, similar to a rule. Even the contents of these blocks are specific to the given at-rule: sometimes they contain a sequence of declarations, like a rule; other times, they may contain additional blocks, or at-rules, or other structures altogether.
Here are several examples of at-rules that illustrate the varied syntax they may contain.
@import "my-styles.css";
The ''@import'' at-rule is a simple statement. After its name, it takes a single string or ''url()'' function to indicate the stylesheet that it should import.
@page :left {
margin-left: 4cm;
margin-right: 3cm;
}
The ''@page'' at-rule consists of an optional page selector (the ":left" pseudoclass), followed by a block of properties that apply to the page when printed. In this way, it's very similar to a normal style rule, except that its properties don't apply to any "element", but rather the page itself.
@media print {
body { font-size: 10pt }
}
The ''@media'' at-rule begins with a media type and a list of optional media queries. Its block contains entire rules, which are only applied when the ''@media''s conditions are fulfilled.
Property names and at-rule names are always idents, which have to start with a letter or a hyphen followed by a letter, and then can contain letters, numbers, hyphens, or underscores. You can include any character at all, even ones that CSS uses in its syntax, by escaping it with a backslash (\) or by using a hexadecimal escape.
The syntax of selectors is defined in the Selectors spec. Similarly, the syntax of the wide variety of CSS values is defined in the Values & Units spec. The special syntaxes of individual at-rules can be found in the specs that define them.
This section is not normative.
When errors occur in CSS, the parser attempts to recover gracefully, throwing away only the minimum amount of content before returning to parsing as normal. This is because errors aren't always mistakes - new syntax looks like an error to an old parser, and it's useful to be able to add new syntax to the language without worrying stylesheets that include it being completely broken in older UAs.
The precise error-recovery behavior is detailed in the parser itself, but it's simple enough that a short description is fairly accurate:
User agents must use the parsing rules described in this specification to generate the CSSOM trees from text/css resources. Together, these rules define what is referred to as the CSS parser.
This specification defines the parsing rules for CSS documents, whether they are syntactically correct or not. Certain points in the parsing algorithm are said to be a parse errors. The error handling for parse errors is well-defined: user agents must either act as described below when encountering such problems, or must abort processing at the first error that they encounter for which they do not wish to apply the rules described below.
Conformance checkers must report at least one parse error condition to the user if one or more parse error conditions exist in the document and must not report parse error conditions if none exist in the document. Conformance checkers may report more than one parse error condition if more than one parse error condition exists in the document. Conformance checkers are not required to recover from parse errors.
The input to the CSS parsing process consists of a stream of Unicode code points, which is passed through a tokenization stage followed by a tree construction stage. The output is a CSSStyleSheet object.
Implementations that do not support scripting do not have to actually create a CSSOM CSSStyleSheet object, but the CSSOM tree in such cases is still used as the model for the rest of the specification.
The stream of Unicode code points that comprises the input to the tokenization stage will be initially seen by the user agent as a stream of bytes (typically coming over the network or from the local file system). The bytes encode the actual characters according to a particular character encoding, which the user agent must use to decode the bytes into characters.
To decode the stream of bytes into a stream of characters, UAs must follow these steps.
The algorithms to get an encoding and decode are defined in the Encoding Standard.
First, determine the fallback encoding:
40 63 68 61 72 73 65 74 20 22 (not 22)* 22 3Bthen get an encoding for the sequence of
(not 22)* bytes,
decoded per windows-1252.
Note: Anything ASCII-compatible will do, so using windows-1252 is fine.
Note: The byte sequence above,
when decoded as ASCII,
is the string "@charset "…";",
where the "…" is the sequence of bytes corresponding to the encoding's name.
If the return value was utf-16 or utf-16be,
use utf-8 as the fallback encoding;
if it was anything else except failure,
use the return value as the fallback encoding.
This mimics HTML <meta> behavior.
charset attribute on the <link> element or <?xml-stylesheet?> processing instruction that caused the style sheet to be included, if any.
If that does not return failure,
use the return value as the fallback encoding.
utf-8 as the fallback encoding.
Then, decode the byte stream using the fallback encoding.
Note: the decode algorithm lets the byte order mark (BOM) take precedence, hence the usage of the term "fallback" above.
Anne says that steps 3/4 should be an input to this algorithm from the specs that define importing stylesheet, to make the algorithm as a whole cleaner. Perhaps abstract it into the concept of an "environment charset" or something?
The input stream consists of the characters pushed into it as the input byte stream is decoded.
Before sending the input stream to the tokenizer, implementations must make the following character substitutions:
Implementations must act as if they used the following state machine to tokenize CSS. The state machine must start in the data state. Most states consume a single character, which may have various side-effects, and either switches the state machine to a new state to reconsume the same character, or switches it to a new state to consume the next character, or stays in the same state to consume the next character. Some states have more complicated behavior and can consume several characters before switching to another state.
The output of the tokenization step is a series of zero or more of the following tokens: ident, function, at-keyword, hash, string, bad-string, url, bad-url, delim, number, percentage, dimension, unicode-range, include-match, dash-match, prefix-match, suffix-match, substring-match, column, whitespace, cdo, cdc, colon, semicolon, comma, [, ], (, ), {, and }.
Ident, function, at-keyword, hash, string, and url tokens have a value composed of zero or more characters. Additionally, hash tokens have a type flag set to either "id" or "unrestricted". The type flag defaults to "unrestricted" if not otherwise set. Delim tokens have a value composed of a single character. Number, percentage, and dimension tokens have a representation composed of 1 or more character, and a numeric value. Number tokens additionally have a type flag set to either "integer" or "number". The type flag defaults to "integer" if not otherwise set. Dimension tokens additionally have a unit composed of one or more characters. Unicode-range tokens have a range of characters.
The type flag of hash tokens is used in the Selectors syntax [[SELECT]]. Only hash tokens with the "id" type are valid ID selectors.
The tokenizer state machine consists of the states defined in the following subsections.
This section is non-normative.
This section presents an informative view of the tokenizer, in the form of railroad diagrams. Railroad diagrams are more compact than a state-machine, but often easier to read than a regular expression.
These diagrams are informative and incomplete; they describe the grammar of "correct" tokens, but do not describe error-handling at all. They are provided solely to make it easier to get an intuitive grasp of the syntax of each token.
Diagrams with names in all uppercase represent tokens. The rest are productions referred to by other diagrams.
The tokenizer can be run with any of several flags that alter its behavior.
transform attribute.
When this is set, whitespace is allowed between the name of a transform function and its opening parenthesis.
This section defines several terms used during the tokenization phase.
Consume the next input character.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, if the input stream starts with an identifier, switch to the ident state. Reconsume the current input character.
Otherwise, if the next 2 input characters are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them, emit a CDC token, and remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to U+002F SOLIDUS (/). Remain in this state.
Otherwise, emit a delim token with its value set to U+003C LESS-THAN SIGN (<). Remain in this state.
Otherwise, this is a parse error. Emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, switch to the ident state. Reconsume the current input character.
Otherwise, if the next input character is U+0073 VERTICAL LINE (|), consume it and emit a column token. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
Otherwise, emit a delim token with its value set to the current input character. Remain in this state.
If a string token has not yet been created since entering this state, create a string token with its value initially set to the empty string.
Consume the next input character.
Otherwise, if the next input character is a newline, consume it. Remain in this state.
Otherwise, this is a parse error. Emit a bad-string token, then switch to the data state.
If a string token has not yet been created since entering this state, create a string token with its value initially set to the empty string.
Consume the next input character.
Otherwise, if the next input character is a newline, consume it. Remain in this state.
Otherwise, this is a parse error. Emit a bad-string token, then switch to the data state.
Create a new hash token with its value initially set to the empty string. If the input stream starts with an identifier, set the hash token's type flag to "id".
Consume the next input character.
Otherwise, this is a parse error. Emit a delim token with its value set to U+0023 NUMBER SIGN (#). Switch to the data state. Reconsume the current input character.
Consume the next input character.
Otherwise, this is a parse error. Emit the hash token. Switch to the data state. Reconsume the current input character.
Consume the next input character.
Otherwise, do nothing and remain in this state.
Consume the next input character.
Otherwise, if the next two characters are U+005C REVERSE SOLIDUS (\) followed by anything but a newline or EOF, create an at-keyword token with its value initially set to U+002D HYPHEN-MINUS. Switch to the at-keyword-rest state.
Otherwise, emit a delim token with its value set to U+0040 COMMERCIAL AT (@). Switch to the data state. Reconsume the current input character.
Otherwise, consume an escaped character. Create an at-keyword token with its value set to the returned character. Switch to the at-keyword-rest state.
Consume the next input character.
Otherwise, consume an escaped character. Append the returned character to the at-keyword token's value. Remain in this state.
Consume the next input character.
Otherwise, if the next two characters are U+005C REVERSE SOLIDUS (\) followed by anything but a newline or EOF, create an identifer token with its value initially set to U+002D HYPHEN-MINUS. Switch to the ident-rest state.
Otherwise, emit a delim token with its value set to U+002D HYPHEN-MINUS (-). Switch to the data state.
Otherwise, consume an escaped character. Create an ident token with its value set to the returned character. Switch to the ident-rest state.
Consume the next input character.
Otherwise, consume an escaped character. Append the returned character to the ident token's value. Remain in this state.
Otherwise, Emit a function token with its value set to the identifier token's value. Switch to the data state.
Otherwise, emit the ident token. Switch to the data state. Reconsume the current input character.
Consume the next input character.
Otherwise, if the next input character is U+0028 LEFT PARENTHESES ((), emit a function token with its value set to the identifer token's value. Switch to the data state.
Otherwise, emit the identifer token. Switch to the data state. Reconsume the current input character.
Note: this state must only be entered after performing validation to ensure that a number token is going to be consumed (see the data state).
Create a number token with its representation initially set to the empty string.
Consume the next input character.
If the next input character is U+002E FULL STOP (.), consume the next two input characters and append them to the number token's representation. Switch to the number-fraction state.
Otherwise, consume the next input character and append it to the number token's representation. Switch to the number-rest state.
Reaching this state indicates an error either in the spec or the implementation.
Consume the next input character.
Otherwise, set the number token's value to the number produced by interpreting the number token's representation as a base-10 number and emit it. Switch to the data state. Reconsume the current input character.
Otherwise, create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to the current input character. Switch to the dimension state.
Otherwise, if the next 2 input characters are U+005C REVERSE SOLIDUS (\) followed by a newline, or U+005C REVERSE SOLIDUS (\) followed by EOF, set the number token's value to the number produced by interpreting the number token's representation as a base-10 number and emit it. Switch to the data state. Reconsume the current input character.
Otherwise, if the next input character is U+005C REVERSE SOLIDUS (\), consume it, then consume an escaped character. Create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to U+002D HYPHEN-MINUS followed by the returned character. Switch to the dimension state.
Otherwise, set the number token's value to the number produced by interpreting the number token's representation as a base-10 number and emit it. Switch to the data state. Reconsume the current input character.
Otherwise, consume an escaped character. Create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to the returned character. Switch to the dimension state.
Set the number token's type flag to "number".
Consume the next input character.
Otherwise, create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to the current input character. Switch to the dimension state.
Otherwise, if the next 2 input characters are U+005C REVERSE SOLIDUS (\) followed by a newline, or U+005C REVERSE SOLIDUS (\) followed by EOF, set the number token's value to the number produced by interpreting the number token's representation as a base-10 number and emit it. Switch to the data state. Reconsume the current input character.
Otherwise, if the next input character is U+005C REVERSE SOLIDUS (\), consume it, then consume an escaped character. Create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to U+002D HYPHEN-MINUS followed by the returned character. Switch to the dimension state.
Otherwise, set the number token's value to the number produced by interpreting the number token's representation as a base-10 number and emit it. Switch to the data state. Reconsume the current input character.
Otherwise, consume an escaped character. Create a dimension token with its representation set to the number token's representation, its value set to the number produced by interpreting the number token's representation as a base-10 number, and a unit initially set to the returned character. Switch to the dimension state.
Consume the next input character.
Otherwise, consume an escaped character. Append the returned character to the dimension token's unit.
Set the number token's type flag to "number".
Consume the next input character.
Let power be the result of interpreting the portion of the number token's representation following the U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e) as a base-10 number.
Set the number token's value to base * 10power.
If the number token's value is not an integer,
set the number token's type flag to "number".
Emit the number token.
Switch to the data state.
Reconsume the current input character.
Consume the next input character.
If a url token has not yet been created since entering this state, create a url token with its value initially set to the empty string.
Consume the next input character.
Otherwise, if the next input character is a newline, consume it and remain in this state.
Otherwise, consume an escaped character. Append the returned character to the url token's value. Remain in this state.
If a url token has not yet been created since entering this state, create a url token with its value initially set to the empty string.
Consume the next input character.
Otherwise, if the next input character is a newline, consume it and remain in this state.
Otherwise, consume an escaped character. Append the returned character to the url token's value. Remain in this state.
Consume the next input character.
If a url token has not yet been created since entering this state, create a url token with its value initially set to the empty string.
Consume the next input character.
Otherwise, consume an escaped character. Append the returned character to the url token's value. Remain in this state.
Consume the next input character.
Otherwise, consume an escaped character. Remain in this state.
Create a new unicode-range token with an empty range.
Consume as many hex digits as possible, but no more than 6. If less than 6 hex digits were consumed, consume as many U+003F QUESTION MARK (?) characters as possible, but no more than enough to make the total of hex digits and U+003F QUESTION MARK (?) characters equal to 6.
If any U+003F QUESTION MARK (?) characters were consumed, first interpret the consumed characters as a hexadecimal number, with the U+003F QUESTION MARK (?) characters replaced by U+0030 DIGIT ZERO (0) characters. This is the start of the range. Then interpret the consumed characters as a hexadecimal number again, with the U+003F QUESTION MARK (?) character replaced by U+0046 LATIN CAPITAL LETTER F (F) characters. This is the end of the range. Set the unicode-range token's range, then emit it. Switch to the data state.
Otherwise, interpret the digits as a hexadecimal number. This is the start of the range.
Consume the next input character.
Otherwise, set the unicode-range token's range and emit it. Switch to the data state. Reconsume the current input character.
This section describes how to consume an escaped character. It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and that the next input character has already been verified to not be a newline or EOF. It will return a character.
Consume the next input character.
This section describes how to set a unicode-range token's range so that the range it describes is within the supported range of unicode characters.
It assumes that the start of the range has been defined, the end of the range might be defined, and both are non-negative integers.
If the start of the range is greater than the maximum allowed codepoint, the unicode-range token's range is empty.
If the end of the range is defined, and it is less than the start of the range, the unicode-range token's range is empty.
If the end of the range is not defined, the unicode-range token's range is the single character whose codepoint is the start of the range.
Otherwise, if the end of the range is greater than the maximum allowed codepoint, change it to the maximum allowed codepoint. The unicode-range token's range is all characters between the character whose codepoint is the start of the range and the character whose codepoint is the end of the range.
This section describes how to check if two characters are a valid escape. The algorithm described here can be called explicitly with two characters, or can be called with the input stream itself. In the latter case, the two characters in question are the current input character and the next input character, in that order.
This algorithm will not consume any additional characters.
If the first character is not U+005D REVERSE SOLIDUS (\), return false.
Otherwise, if the second character is a newline or EOF character, return false.
Otherwise, return true.
This section describes how to check if the input stream starts with an identifier. Including the current input character, it verifies that the input stream's first several characters will make up a valid identifier (or other token that starts with an identifer) if consumed.
This algorithm will not consume any additional characters.
Look at the current input character:
This section describes how to check if the input stream starts with a number. Including the current input character, it verifies that the input stream's first several characters will make up a valid number (or other token that starts with a number) if consumed.
This algorithm will not consume any additional characters.
Look at the current input character:
Otherwise, if the next two input characters are a U+002E FULL STOP (.) followed by a digit, return true.
Otherwise, return false.
This section is non-normative.
Note that the point of this spec is to match reality; changes from CSS2.1's tokenizer are nearly always because the tokenizer specified something that doesn't match actual browser behavior, or left something unspecified. If some detail doesn't match browsers, please let me know as it's almost certainly unintentional.
The input to the parsing stage is a stream or list of tokens from the tokenization stage. The output depends on how the parser is invoked, as defined by the entry points listed later in this section. The parser output can consist of at-rules, qualified rules, and/or declarations.
The parser's output is constructed according to the fundamental syntax of CSS, without regards for the validity of any specific item. Implementations may check the validity of items as they are returned by the various parser algorithms and treat the algorithm as returning nothing if the item was invalid according to the implementation's own grammar knowledge, or may construct a full tree as specified and "clean up" afterwards by removing any invalid items.
The items that can appear in the tree are a mixture of basic tokens and new objects:
This specification places no limits on what an at-rule's value may contain. Individual at-rules must define whether they accept a value, and if so, how to parse it (preferably using one of the parser algorithms or entry points defined in this specification).
Most qualified rules will be style rules, where the prelude is a selector.
The non-preserved tokens listed above are always consumed into higher-level objects, either functions or simple blocks, and so never appear in any parser output themselves.
This section is non-normative.
This section presents an informative view of the parser, in the form of railroad diagrams. Railroad diagrams are more compact than a state-machine, but often easier to read than a regular expression.
These diagrams are informative and incomplete; they describe the grammar of "correct" stylesheets, but do not describe error-handling at all. They are provided solely to make it easier to get an intuitive grasp of the syntax.
The algorithms defined in this specification can be invoked in multiple ways to convert a stream of text into various CSS concepts.
All of the algorithms defined in this section begin in the parser. It is assumed that the input preprocessing and tokenization steps have already been completed, resulting in a stream of tokens.
Other specs can define additional entry points for their own purposes.
The following notes should probably be translated into normative text in the relevant specs, hooking this spec's terms:
CSSStyleSheet#insertRule method,
and similar functions which might exist,
which parse text into a single rule.
style attribute,
which parses text into the contents of a single style rule.
Are there any other things somewhere where some tech (that isn't straight CSS itself) needs to parse some text into CSS?
All of the algorithms defined in this spec may be called with either a list of tokens or of component values. Either way produces an identical result.
To parse a stylesheet from a stream of tokens:
To parse a rule from a stream of tokens:
Otherwise, if the current input token is an at-keyword token:
Otherwise, consume a qualified rule. If nothing was returned, return a syntax error.
To parse a list of declarations:
To parse a component value:
To parse a list of component values:
To parse a comma-separated list of component values:
To parse an+b notation:
Initialize an integer called step to 0, an integer called offset to 0, and a string called repr to the empty string.
Repeatedly consume the next input token and process it as follows:
Trim U+0020 SPACE ( ) characters from the front and back of repr, then process it as follows:
Interpret the first string as follows:
Interpret the second string as follows:
Return step and offset.
The following algorithms comprise the parser. They are called by the parser entry points above.
These algorithms may be called with a list of either tokens or of component values. (The difference being that some tokens are replaced by functions and simple blocks in a list of component values.) Similar to how the input stream returned EOF characters to represent when it was empty during the tokenization stage, the lists in this stage must return an EOF token when the next token is requested but they are empty.
An algorithm may be invoked with a specific list, in which case it consumes only that list (and when that list is exhausted, it begins returning EOF tokens). Otherwise, it is implicitly invoked with the same list as the invoking algorithm.
Create an initially empty list of rules.
Repeatedly consume the next input token:
Create a new at-rule with its name set to the value of the current input token, its prelude initially set to an empty list, and its value initially set to nothing.
Repeatedly consume the next input token:
Create a new qualified rule with its prelude initially set to an empty list, and its value initially set to nothing.
Repeatedly consume the next input token:
Create an initially empty list of declarations.
Repeatedly consume the next input token:
Create a new declaration with its name set to the value of the current input token.
Repeatedly consume whitespace tokens until a non-whitespace token is reached. If this token is anything but a colon token, this is a parse error. Return nothing.
Otherwise, repeatedly consume a component value from the next input token until an EOF token is reached, appending all of the returned values up to that point to the declaration's value.
If the last two non-whitespace tokens in the declaration's value are a delim token with the value "!" followed by an ident token with a value that is an ASCII case-insensitive match for "important", remove them from the declaration's value and set the declaration's important flag to true.
Return the declaration.
This section describes how to consume a component value.
If the current input token is a {, [, or ( token, consume a simple block and return it.
Otherwise, if the current input token is a function token, consume a function and return it.
Otherwise, return the current input token.
This section describes how to consume a component value with the hashless color quirk.
The hashless color quirk list contains the following property names:
If the current input token is a number token, let value be the token's representation. If the current input token is a dimension token, let value be the token's representation followed by the token's unit. If the current input token is an ident token, let value be the token's value.
If value has a length of 3 or 6 and is composed solely of hex digits, create a hash token with its value set to the value and return it.
Otherwise, consume a component value and return the returned value.
This section describes how to consume a component value with the unitless length quirk.
The unitless length quirk list contains the following property names:
If the current input token is a number token, create a dimension token with its value and representation set to the token's value and representation, and its unit set to "px". Return the dimension token.
Otherwise, consume a component value and return the returned value.
This section describes how to consume a simple block.
The ending token is the mirror variant of the current input token. (E.g. if it was called with [, the ending token is ].)
Create a simple block with its associated token set to the current input token.
Repeatedly consume the next input token and process it as follows:
This section describes how to consume a function.
Create a function with a name equal to the value of the current input token, and an argument list which is initially empty. Create an argument, called the current argument, with a value which is initially an empty list.
Repeatedly consume the next input token and process it as follows:
Otherwise, consume a component value. If anything was returned, append the returned value to the value of the current argument; otherwise, set the valid flag to false.
This section is non-normative.
Note that the point of this spec is to match reality; changes from CSS2.1's Core Grammar are nearly always because the Core Grammar specified something that doesn't match actual browser behavior, or left something unspecified. If some detail doesn't match browsers, please let me know as it's almost certainly unintentional.
This specification does not define how to serialize CSS in general, leaving that task to the CSSOM and individual feature specifications. However, there is one important facet that must be specified here regarding comments, to ensure accurate "round-tripping" of data from text to CSS objects and back.
The tokenizer described in this specification does not produce tokens for comments, or otherwise preserve them in any way. Implementations may preserve the contents of comments and their location in the token stream. If they do, this preserved information must have no effect on the parsing step, but must be serialized in its position as "/*" followed by its contents followed by "*/".
If the implementation does not preserve comments, it must insert the text "/**/" between the serialization of adjacent tokens when the two tokens are of the following pairs:
The preceding pairs of tokens can only be adjacent due to comments in the original text, so the above rule reinserts the minimum number of comments into the serialized text to ensure an accurate round-trip. (Roughly. The delim token rules are slightly too powerful, for simplicity.)
Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.
All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [[!RFC2119]]
Examples in this specification are introduced with the words “for example”
or are set apart from the normative text with class="example",
like this:
This is an example of an informative example.
Informative notes begin with the word “Note” and are set apart from the
normative text with class="note", like this:
Note, this is an informative note.
Conformance to CSS Syntax Module Level 3 is defined for three conformance classes:
A style sheet is conformant to CSS Syntax Module Level 3 if it is syntactically valid according to this module.
A renderer is conformant to CSS Syntax Module Level 3 if it parses a stylesheet according to this module.
An authoring tool is conformant to CSS Syntax Module Level 3 if it writes style sheets that are syntactically valid according to this module.
Thanks for feedback and contributions from David Baron, 呂康豪 (Kang-Hao Lu), and Simon Sapin.