To the extent possible under law, the editors have waived all copyright and related or neighboring rights to this work. In addition, as of 24 January 2014, the editors have made this specification available under the Open Web Foundation Agreement Version 1.0, which is available at http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. Parts of this work may be from an existing specification document. If so, those parts are instead covered by the license of the existing specification document.
This module introduces the ability to nest one style rule inside another, with the selector of the child rule relative to the selector of the parent rule. This increase the modularity and maintainability of CSS stylesheets.
This is an unofficial draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.
This section is not normative.
This module describes support for nesting a style rule within another style rule, allowing the inner rule’s selector to reference the elements matched by the outer rule. This feature allows related styles to be aggregated into a single structure within the CSS document, improving readability and maintainability.
This module introduces new parser rules that extend the [CSS21] parser model. This module introduces selectors that extend the [SELECTORS4] module.
This specification does not define any new properties or values.
CSS Rules for even moderately complicated web pages include lots of duplication for the purpose of styling related content. For example, here is a portion of the CSS markup for one version of the [CSS3COLOR] module:
table.colortable td { text-align:center; } table.colortable td.c { text-transform:uppercase; } table.colortable td:first-child, table.colortable td:first-child+td { border:1px solid black; } table.colortable th { text-align:center; background:black; color:white; }
Nesting allow the grouping of related style rules, like this:
table.colortable {{ td { text-align:center; {&.c { text-transform:uppercase }} {&:first-child, &:first-child + td { border:1px solid black }} } th { text-align:center; background:black; color:white; } }}
Besides removing duplication, the grouping of related rules improves the readability and maintainability of the resulting CSS.
Style rules cannot be nested directly inside of style rules, as selectors are ambiguous with properties on a syntax level. Instead, the nesting block, which is simply a pair of "{}" characters surrounding the nested rules, is used. The nesting block separates the nested rules from surrounding properties so that they can be parsed unambiguously.
A nesting block is valid inside of style rules and style
attributes,
at the same level as declarations.
The selectors of style rules inside a nesting block are relative to the results of the selector of the parent rule. Effectively, nesting is equivalent to appending the parent and child selectors with a descendant selector, handling selector lists appropriately.
The idea of nesting came from CSS preprocessors, but none of them need something like a nesting block in order to nest. Generally, they let you mix properties and selectors without reservation. So why do we need the nesting block here?
In general, selectors and properties are grammatically ambiguous with each other.
For example, foo:hover bar baz qux...
looks like both a selector,
and a "foo" property with hover bar baz qux
as its value.
In some cases, you can’t tell the difference between the two until you hit a semicolon or curly brace,
which may be an arbitrary number of tokens away.
Preprocessors are okay with needing arbitrary lookahead to parse CSS. Browsers aren’t - they want fast, parallizable parsing, and that means fixed, small amounts of lookahead.
The nesting block lets the browser immediately tell that it’s not looking at a property, so it can switch into a selector-parsing mode right away.
div, p {{ .keyword, .constant {color: red;} }}
...produces the same results as the following CSS:
div .keyword, div .constant, p .keyword, p .constant { color:red; }
Multiple nesting blocks can be embedded within a style rule, and can be embedded arbitrarily deeply. Nesting blocks and properties can both appear in a single declaration block, and in any order.
div, p { font-size: 10px; {.keyword {color: green;}} {.constant { color: red; background-color: green; {&:hover::after { content: " [" attr(value) "]";}} }} }
...produces the same results as the following CSS:
div, p {font-size: 10px;} div .keyword, p .keyword {color: green;} div .constant, p .constant {color: red; background-color: green;} div .constant:hover::after, p .constant:hover::after {content: " [" attr(value) "]";}
Note: Though it’s unlikely that stylesheets authored with Nesting will be useful in legacy user agents, as the nested sections will be ignored by the error-handling rules, authors can minimize the damage caused by error-recovery by always putting properties before nested rules. In the preceding example, the font-size declaration would be honored by legacy user-agents, even while the rest of the rule is ignored.
The selectors inside of a nesting block are implicitly relative to the results of the parent rule’s selector, effectively joined with the parent’s selector by a descendant combinator.
This isn’t always what is intended.
One might wish to simply specialize the element selected by the parent,
applying some properties to every div
but additional rules just to div
with a particular class
;
one might wish to select the sibling of an element;
one might wish to specialize the already-selected elements by filtering them based on ancestors.
All of these and more can be accomplished by explicitly indicating in the nested selector where the elements matched by the parent rule’s selector should go. To this end, the & selector, or nesting selector, is introduced.
Within a nesting block, the & selector matches the elements matched by the parent rule’s selector. Outside of a nesting block, the & selector matches nothing.
Introducing the & character will cause issues with CSS embedded directly in XML, as it’s the first character used in CSS syntax that either requires escaping or using CDATA. Do we need to change this?
div {{ .keyword {color: red;} &:hover {background-color: rgb(200, 255, 255);} section > & {border: 2px solid gray;} }}
...produces the same results as the following CSS:
div .keyword {color:red;} div:hover {background-color: rgb(200, 255, 255);} section > div {border: 2px solid gray;}
Note: The nesting selector does not change any of the usual rules about compound selectors,
such as the requirement that type selectors come first.
For example, “&div
” is invalid;
one must instead write “div&
”
This specification alters the CSS Core Grammar and the Selectors grammar.
If this spec is accepted, I’ll just modify Syntax directly to accommodate Nesting.
None of this chapter is correct now that I’m using nesting blocks as the disambiguator.
...
...
...
The following attribute is required to be added to the CSSStyleRule object defined in Section 6.4.3 of [CSSOM]:
partial interface CSSStyleRule { readonly attribute DOMString expandedSelectorText; readonly attribute CSSRuleList cssRules; };
The cssRules
attribute must return a CSSRuleList
object for the list of CSS rules specified within the style rule.
The expandedSelectorText
attribute must return a DOMString
object containing the result of replacing the & selector in the selectorText
attribute with the expandedSelectorText
attribute from the parent rule. If the rule has no parent rule, the expandedSelectorText
attribute must return a DOMString
object containing the same text as the selectorText
attribute.
The OM here is meant to reflect the OM for @media rules.
In particular, the lack of a link from a rule to its parent matches rules nested in @media.
Should we add a parentRule
property to both of these?
No properties defined.