Shadow DOM: Styles (Cont.)
In yesterday’s post we covered the basics of working with styles in Shadow DOM. But we’ve only just scratched the surface! Today we’ll continue from where we left off and explore how to work with distributed nodes and how to poke holes in our components so the outside world can reach in and customize ‘em.
Before we get started I wanted to thank Eric Bidelman for his amazing article on styling the Shadow DOM. Most of this article is my interpretation of his post. Definitely go read everything on HTML5 Rocks that pertains to Web Components when you get a chance.
In order to try the examples I suggest you use Chrome Canary v31 or greater.
Also make sure you’ve enabled the following in Chrome’s
√ Experimental Web Platform features
I believe Shadow DOM is supported in Chrome without experimental flags but we may touch on other Web Component technologies that require them. Better to just turn them on now I think :)
I’ve created a sketchbook for this post and future Web Components related stuff. You can grab the sketchbook on GitHub. For each of the examples that I cover I’ll link to the sketch so you can quickly try things out.
Sketch 9: styling-distributed-nodes
From reading various blog posts I learned that when working with the shadow DOM you should make sure to keep your content separate from your presentation. In other words, if you have a button that’s going to display some text, that text should come from the page and not be buried in a shadow DOM template. Contents which come from the page and are added to the shadow DOM using the
<content> tag are know as distributed nodes.
Initially I struggled to understand how it was possible to style these distributed nodes. I kept writing my CSS like this:
Thinking that if
button came from the shadow host I should be able to just style it once it was inside my
<content> tag. But that’s not quite how things work. Instead, distributed nodes need to be styled with the
::content pseudo selector. This actually makes sense because we might want buttons inside of our shadow template to be styled differently from buttons which appear inside of our
Let’s look at an exmaple:
Here we’re pulling in the
button from our
.widget shadow host and tossing it into our
<content> tag. Using the
::content pseudo selector, we target the
button as a child with
> and set our fancy pants styles.
In previous versions of Chrome the syntax for styling distributed nodes looked like this:
But this style is being deprecated. As of today (August 29, 2013)
::content is the proper syntax.
Sketch 10: styling-parts
Up to this point we’ve celebrated the encapsulation benefits of the shadow DOM but sometimes you want to poke a few holes in the shadow boundary so the user can style your component.
Let’s say you’re creating a sign in form. Inside of your template you’ve defined the text size for the inputs but you’d like the user to be able to change this so it fits better with her site. Using the
::part pseudo selector and
part attribute we can expose any fields we want, thereby giving the user total freedom to override our defaults if they so choose.
There are three important bits of syntax I want to point out in this example.
The first is how we specify that an element is going to be a
part. We do this through the use of the
Next we specify a default style for this
part using an attribute selector inside of our template.
Finally, we reach into the shadow DOM with the
::part pseudo selector to override the style defaults.
In previous versions of Chrome the
part attribute was known as
pseudo attribute would need to be prefixed with an
x- and selecting the
pseudo attribute looked like this:
This syntax is now deprecated and you should use
Sketch 11: styling-with-variables
Working with all of these hard coded styles is making my inner nerd sad. If you’ve been spoiled by LESS or SCSS, like I have, then you’ll quickly be longing for variables to hold all of your configurable values. Thankfully CSS3 variables are supported by the shadow DOM so let’s look at how we can tidy things up a bit.
We’ll use our previous example but this time we’ll work a little variable magic on it:
We should wind up with the exact same output but things are a bit cleaner now.
Personally I’m not a fan of the CSS3 variable syntax but if you want to go preprocessor free I think it’s your best bet.
Inheriting and Resetting Styles
Sketch 12: inheriting-and-resetting-styles
Applying Author Styles
If your component contains text or other inheritable properties you may want it to match the page that’s hosting it. This is where
applyAuthorStyles comes in to play. By setting
true you’re telling the document that it’s ok for the user’s CSS to bleed through and affect your component. This is great for things like typography where you want your component to use the same font families and header sizes as the parent document.
If we take a look at the CSS we can see that the user has directly styled all
p’s. Also everything on the page should inherit a
font-family of Helvetica. By using
applyAuthorStyles we’ve allowed the direct styles and the inherited
font-family to bleed through.
In some cases we may wish to only allow the direct styles while resetting anything that would be inherited. Using the example above that would mean excluding the
font-family of Helvetica which is inherited from the
body style. To reset style inheritance we use the aptly named
Now our component reverts to the default
font-family of Times New Roman while still allowing direct author styles on
p. Eric Bidelman’s great post on Shadow DOM 201 has a handy cheat sheet so you can sort out when you want to use
applyAuthorStyles and when you want to use