Adobe

decor

Web Platform Team Blog

Making the web awesome

Working with CSS Regions and Shadow DOM

When we set out to develop CSS Regions we knew that most innovative applications of the technology would come from creative integrations with other web standards. Shadow DOM is one such example of a web standard just itching to be experimented with.

Shadow DOM is the enabling technology for Web Components, a W3C proposal which helps create self-contained units of functionality for web pages. These units are portable across pages because they’re built with strict encapsulation of DOM elements, styles and scripting provided by Shadow DOM. You can learn more by watching Christian Cantrell’s short video introduction to Shadow DOM and Web Components.

Shadow DOM in a nutshell

Shadow DOM provides the means to encapsulate DOM nodes to strictly control tampering by scripts and styles defined outside their boundaries. This allows developers to build complex components with bullet-proof style and behavior behind a single DOM node, similar to how the HTML5 <video> tag works.

Dimitri Glazkov, the author of the proposal, describes this in more detail on his blogpost about Shadow DOM.

It turns out that there’s a use case for CSS Regions that Shadow DOM is a perfect candidate for.

The CSS Regions Workflow

With CSS Regions you collect content from one or more elements and associate it to a logical structure called a named flow. Then, you use the named flow to point the collected content to render in other containers. Those containers become regions and they can be chained together with content flowing from one to the other.

If this all sounds strange, maybe the CSS Regions samples can help you understand the concepts.

One way of doing this is by having empty placeholder elements in the document, waiting to become regions. But those elements have no semantic value; they’re just presentation helpers.

Developments such as CSS Pagination Templates show that using empty placeholders isn’t the only way to render content in regions. But, for the sake of experimentation, let’s tackle this limitation. Enter Shadow DOM.

The Use Case

With Shadow DOM we create as many placeholder elements as required under a single element. The catch is that we keep them outside the main DOM, nested under what’s called a ShadowRoot, invisible to scripts defined outside this boundary.

Working with Shadow DOM

At the time of this writing Shadow DOM is a developing standard. Parts of the proposal are implemented with prefixes in WebKit and in Google Chrome behind runtime flags. In the spirit of the open web, the text of this article uses the unprefixed property names for Shadow DOM functionality. In the spirit of developer sanity, the code samples use Webkit prefixes where necessary at this point in time.

ShadowRoot Encapsulation

A good metaphor for Shadow DOM is a document within a document. A new Shadow DOM tree may be created under any DOM element with the ShadowRoot constructor. It takes a single DOM node argument. The DOM node acts as a host for the Shadow DOM root and it defines its boundaries. The original content of the host element is replaced by the content of the shadow root.

<div id="host"></div>

<script type="text/javascript">
  var host = document.querySelector('#host')
  var root = new WebKitShadowRoot(host)
</script>

Working under the ShadowRoot we may create other elements, attach event handlers and styles just as we would on a normal node.
var paragraph = document.createElement('p')
paragraph.textContent = 'in the shadow'

// The paragraph belongs to the shadow root
root.appendChild(paragraph)

By design, queries for elements made from outside the shadow root’s boundaries do not match the elements inside. This ensures encapsulation. The ShadowRoot implements its own query methods that match only against its child nodes: root.getElementById(), root.querySelector(), etc.
var elements = document.querySelectorAll('p') // 0 nodes
var elements = root.querySelectorAll('p') // 1 node

Stylesheets and Shadow DOM

By default, styles defined outside the shadow root will not be applied on its child elements. This also ensures encapsulation to protect the elements within.

<style type="text/css">
  p {
    color: green;
  }
</style>

<script type="text/javascript">
    var p = root.querySelector('p')
    window.getComputedStyle(p, null).color // rgb(0, 0, 0) black, not green
</script>

According to the Shadow DOM proposal there is a way to allow stylesheets defined outside the shadow root to apply on elements nested under it.

The boolean applyAuthorStyles flag on the shadow root will toggle this behavior.

// Apply stylesheets defined outside the shadow root
root.applyAuthorStyles = true

CSS Regions and Shadow DOM

By this point we have all the necessary components to meet the use case. A quick reminder about it: generate regions as placeholder elements under a ShadowRoot so as not to pollute the main DOM with non-semantic empty elements.

CSS Regions prerequisites

First, we need to collect the content of a source element into a named flow. We’ll call it “myFlow”. The element and its content will still be part of the main document and accesible via the DOM. The content, however, is not rendered. This is expected behavior.

We define a CSS class, .region, which, when applied to an element, will render content from the “myFlow” named flow into that element.

<style type="text/css">
  article{
    -webkit-flow-into: myFlow;
  }
  .region{
    -webkit-flow-from: myFlow;
    width: 150px;
    height: 300px;
  }
</style>
<article>
  <p>Lorem ipsum dolor...</p>
</article>

Creating Regions in a Shadow DOM

To render the content from the “myFlow” named flow we need to create some regions that pull content from it. We’ll create those under the shadow root.

<div id="host"></div>

<script type="text/javascript">
  var host = document.querySelector('#host')
  var root = new WebKitShadowRoot(host)

  // Apply stylesheets defined outside the shadow root
  root.applyAuthorStyles = true

  // Create an empty div and make it a region
  var region = document.createElement('div')
  region.className = 'region'

  // Add the region to the shadow root
  root.appendChild(region)
</script>

The key part here is setting the applyAuthorStyles flag to boolean ‘true’ on the ShadowRoot. This intentionally circumvents the Shadow DOM encapsulation. It allows the content defined outside, in the article tag to be rendered through CSS Regions in elements defined under the shadow root.

More regions can be created as necessary to render the whole content. This can be done by leveraging the CSS Regions JavaScript API, but that’s an experiment for another blog post.

Taken further, this idea is laying the groundwork for defining article templates with Shadow DOM. This approach helps achieve a clear separation between the semantic markup of the content from the auxiliary markup necessary to do the layout.

Next steps

At this point, both CSS Regions and Shadow DOM are under development and constantly evolving. A good way to test out these features together is to use Google Chrome Canary browser. You’ll need to type chrome://flags in the browser’s address bar then follow the instructions to enable the CSS Regions and the Shadow DOM flags.

Demo

Assuming you’re running an appropriate browser, you can see a demo of CSS Regions and Shadow DOM on GitHub. Be sure to take a peek at the source code, as well.

Have fun!

11 Comments

  1. June 26, 2012 at 1:37 pm, CSS Regions and Shadow DOM | Qtiva said:

    [...] One of my issues with CSS regions (we explored them a bit here ) was the use of empty container divs for layout options. Razvan Caliman talks about fixing that by building out your own Shadow DOM tree. Clever, cutting edge stuff Direct Link [...]

  2. June 26, 2012 at 3:29 pm, My Stream | CSS Regions and Shadow DOM | My Stream said:

    [...] out your own Shadow DOM tree. Clever, cutting edge stuff. You can see it work in Chrome Canary.Direct Link to Article — Permalink…CSS Regions and Shadow DOM is a post from CSS-TricksOriginally posted from [...]

  3. June 27, 2012 at 12:24 am, Revision 77: Do not Track, IHK-Petition und Web Components | Working Draft said:

    [...] [00:29:05] X-Tag – Web Components Workshop und Working with CSS Regions and Shadow DOM [...]

  4. June 27, 2012 at 1:57 am, CSS Blog said:

    I really like the idea of CSS Regions and tried to explain it with a small CSS tutorial. The Idea of moving elements from one region to another ist an interestign way to use different positions on different devices.

  5. June 27, 2012 at 3:16 am, Razvan Caliman said:

    Hi,
    thanks for putting in all that effort to teach about CSS Regions ;)

  6. June 27, 2012 at 7:30 am, Yohann Paris said:

    This is mind blowing… But they will not be too much work at the end?

    I try to explain. I code my HTML to be semantic and well done. Then I will use JS to insert a shadow DOM inside my DOM for layout purposes. This will not make things more complex by giving some part of the layout into JS, when some of it is already in the CSS (like float, margin: 0 auto, etc.)?

    Does that mean we have to rethink how to separate the code like we did when we remove inline CSS from HTML? Now it will be HTML for content like a DB of the front-end, then CSS for the design, JS for user interaction, JS for AJAX and finally a mix of CSS and JS for layout purposes.

    I might go deeper in this idea and write an article about it.

  7. June 28, 2012 at 10:20 am, David Millar said:

    I’m interested in trying the demo, but all I get is “No Shadow DOM support. Use Google Chrome Canary” – which is kind of odd because I *am* using Google Chrome Canary. Thoughts?

  8. June 28, 2012 at 11:21 am, Razvan Caliman said:

    Hi David,
    be sure to enable the “CSS Regions” and “Shadow DOM” flags in Chrome Canary. You can find these options if you type chrome://flags/ in the address bar.

    You should be able to see the working demo after you enable those flags and restart the browser.

    Let me know if you have any trouble.

  9. June 28, 2012 at 11:32 am, Razvan Caliman said:

    @Yohann,
    thanks for the thoughtful review! You do make a valid point.

    While this is an experiment and I do mean it to open up discussion, it should not encourage you to break your semantic workflow.

    This experiment highlights the idea that you could have an article “template” in Shadow DOM and then feed content into it, semantic HTML. The idea described here is closer to Web Components, similar to how you’d think about a working with a tag.

    I don’t think you need to think about separating your content any more than you’re already doing. One advantage that I believe CSS Regions with Shadow DOM do bring is that your layout may be completely, and I emphasize on this, completely, decoupled from your content. This means that you can have greater flexibility when designing for multiple screen sizes, for example.

    I do encourage your to write an article if you feel the need to explore this in depth. Please keep me posted. I’d love to read it!

    Have fun! ;)

  10. July 04, 2012 at 2:44 am, bertrandkeller » Les perspectives des CSS regions et du binding de données said:

    [...] Lire Working with CSS Regions and Shadow DOM. [...]

  11. July 24, 2012 at 5:51 am, CSS Regions Aren’t Just For Columns « Christian Cantrell said:

    [...] Here’s a nice simple diagram of CSS regions I stole from Razvan Caliman’s post, Working with CSS Regions and the Shadow DOM: [...]