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.
Support
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 about:flags
.
√ Experimental Web Platform features
√ Experimental JavaScript
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 :)
Codez!
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.
Distributed Nodes
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 <content>
tags.
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.
Syntax Changes
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.
Parts
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 part
attribute.
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.
Syntax Changes
In previous versions of Chrome the part
attribute was known as pseudo
. A 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 part
and ::part
instead.
Variables
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 applyAuthorStyles
to 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 h2
’s and 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.
Reseting Inheritance
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 resetStyleInheritance
method.
Now our component reverts to the default font-family
of Times New Roman while still allowing direct author styles on h2
and 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 resetStyleInheritance
.
Conclusion
If you’ve read over the last post and this one then you now know as much about styling the shadow DOM as I do. But we haven’t even talked about JavaScript yet! We’ll save that for tomorrow’s post :) As always if you have questions hit me up on twitter or leave a comment. Thanks!