Adobe

decor

Web Platform Team Blog

Making the web awesome

CSS Regions and Selectors

Selectors

The CSS Selectors system is the driving force behind the stylesheets of a Web page. The selector of a rule defines what elements or pseudo-elements get to be styled using that rule. The selectors language has advanced a lot lately and it allows a lot of complex constructs using tag names, ids, classes, pseudo-classes and so on.

An important part of the language are the combinators which define relations between two selectors. The Editors Draft of CSS Selectors Level 3 lists four combinators:

  • the descendant combinator (E F)
  • the child combinator (E > F)
  • the following-sibling combinator (E ~ F)
  • the next-sibling combinator (E + F)

There’s a general forward direction in the DOM tree when combining selectors. There is no “parent” or “ancestor” combinator. However, the CSS Selectors Level 4 draft defines the :has pseudo-class that’s supposed to fill in that role. The major limitation of this pseudo-class is that browsers are not supposed to implement it for dynamic selectors, just for cases that don’t impact performance too much, such as the Document.querySelector function.

To understand why :has is a problem if used for rule definitions let’s see how browsers actually do selectors matching. A browser doesn’t start with the rule and try to determine what elements must use that rule. It’s the other way around. The browser knows the element it needs to style and then it evaluates each selector from right to left, eliminating rules that don’t match. In the case of a combinator, once the right side was validated using the element, the browser then recursively verifies if the left side can be matched against the corresponding elements for that type of combinator. For example, the child combinator will use the parent of the element and the following-sibling combinator will verify all the previous siblings of the element to find a match. If all the selectors in the combinator match, the rule is used for the element.

This algorithm is efficient because most of the rules are usually eliminated in the early stages of the matching process. Also, when styling an element, only the ancestors and the previous siblings of the element matter. The :has pseudo-class complicates these rules. The elements matched by :has need to track all the changes in their DOM subtree that can enable or disable the rule. When misused, the :has pseudo-class can easily lead to document wide style invalidation and major performance degradations.

Regions

How CSS Regions can help in this situation? One of the interesting properties of CSS Regions is that content collected in a named flow can be rendered in regions located anywhere inside the document as long as cycles are avoided. This means that you can write stuff like this:

<div class="content"></div>
<div class="region"></div>

<style>
.content {
    flow-into: flow;
}
.region {
    flow-from: flow;
}
</style>

This code will display the content element inside the region element. In this situation, a rule with a .content + .region selector will actually behave like a parent selector. It will style the box containing the content.

I think this is especially powerful when dealing with interactivity. For example, the :hover pseudo-class is constrained by the selector rules. You can’t hover an element on a page and apply an effect to another element unrelated (not a child or sibling) to the first one. With regions this limitation can be avoided:

  1. Place the element to be hovered (A) as a previous sibling of the element you want to be styled (B).
  2. Add a region in the page and flow B inside it.
  3. Write a next sibling combinator rule between them A:hover + B and B will be styled even though it’s not rendered as a sibling of A any more.

Here is an example that better demonstrates the feature in Safari 7+:

Check out this Pen!

You can also add both elements to a flow thread and use forced breaks to split them in two different regions. This technique is used in the following example that works in Internet Explorer 10+:

Check out this Pen!

Conclusions

Feel free to experiment with this idea and bring new ones to the table. We’re always looking for ways to improve the Web Platform and empowering users to achieve the effects they want is a priority.

4 Comments

  1. July 30, 2014 at 1:12 am, Björn said:

    You’ve accidentally switched the names of the next & following combinators in the list at the top. ‘+’ is next and ‘~’ is following.

    • July 30, 2014 at 1:31 am, Andrei Bucur said:

      Whoops, good catch! Thanks for the heads up!

  2. July 30, 2014 at 7:56 am, Elaine said:

    I am guessing you are only proofing this in chrome, but you must have an open div or a missing float or something, your content doesn’t start until below your menu, at least in firefox.

    • July 31, 2014 at 11:41 pm, Andrei Bucur said:

      You talk about the article or one of the examples? The article renders fine in Firefox on my machine.