Thursday, 14 September, 2017 UTC


Summary

Thanks to Shadow DOM, it’s very easy to style your Web Components using simple CSS selectors. In this post we’ll cover a few extra styling options available to us to style custom elements. On top of that, we’ll see how to allow the creation of styling hooks that make it easy for a custom element’s user to customize the style from the outside.
In our previous post about attributes and properties for Custom Elements, we created a counter element with a template looks like this:
const template = document.createElement('template'); template.innerHTML = ` <style> button, span { font-size: 3rem; font-family: monospace; padding: 0 .5rem; } button { background: pink; color: black; border: 0; border-radius: 6px; box-shadow: 0 0 5px rgba(173, 61, 85, .5); } button:active { background: #ad3d55; color: white; } </style> <div> <button type="button" increment>+</button> <span></span> <button type="button" decrement>-</button> </div> `;
In this post, we’ll expand on that template’s style declaration to tweak the styles a bit more.
Styling a Custom Element With :host
The :host selector makes it easy to select and style the custom element itself, meaning the shell of the element. Let’s add some styles to our counter element:
:host { all: initial; display: block; contain: content; text-align: center; background: linear-gradient(to left, hotpink, transparent); max-width: 500px; margin: 0 auto; border-radius: 8px; transition: transform .2s ease-out; } :host([hidden]) { display: none; } :host(:hover) { transform: scale(1.1); }
  • We set all: initial to reset all global style values so that they don’t affect our element’s styles. Note that this should be the first property that you set in your CSS rule, otherwise what follows could be reset too.
  • By default, custom elements are set to display: inline, so here we specify that it should be display: block instead.
  • As you can see, the :host selector can also be used as a function to select only the element if it matches the provided selector. We use that here to set display: none on the element if has the hidden attribute.
  • We also use a new CSS property, contain, to hint to the CSS engine that the styles are contained and independent. This can help with layout performance.
Using :host, we could also style the element differently if it had a specific class name, rainbow in this example:
:host(.rainbow) { color: white; background: linear-gradient(to left, red, orange, yellow, green, blue, indigo, violet); } :host(.rainbow) button { background: white; }

Note that if a custom element is styled externally by the user of the element with something like this…
my-counter { background: none; }
…the external styles will always win if they collide with the ones provided with :host. So in that case, our element wouldn’t have its background color anymore.
Theming Using :host-context
We can also use a :host-context() selector that will select our custom element only if it or any of its ancestors match the provided selector. This makes it really easy to style your element differently if, say, the body element has the dark class:
:host-context(.dark) { background: linear-gradient(to left, white, transparent); } :host-context(.dark) button { background: white; box-shadow: 0 0 5px rgba(255, 255, 255, .5); } :host-context(.dark) button:active { color: red; background: #f1f1f1; }
Style Hooks
So far we’ve seen how we can easily style a custom element from within, but what if we want to let the user of the element customize the styles without having to modify the element? This is where CSS custom properties come in.
In your custom element’s style definition, you’d use the custom properties like this:
:host { all: initial; display: block; contain: content; text-align: center; background: linear-gradient(to left, var(--bg-color, hotpink), transparent); max-width: 500px; margin: 1rem auto; border-radius: 8px; transition: transform .2s ease-out; } button { cursor: pointer; background: var(--btn-bg-color, pink); color: var(--btn-color, black); border: 0; border-radius: 6px; box-shadow: var(--btn-box-shadow, 0 0 5px rgba(173, 61, 85, .5)); }
With this in place, our default colors will be used if variables are not defined, and now the colors can be customized in page’s style using something like this:
my-counter-styled { --bg-color: lime; --btn-bg-color: #47b747; --btn-color: white; --btn-box-shadow: 3px 0 8px rgba(108,138,51,1); }
You'll most likely want to document the available styling hooks that are made available so that it's easy to know which custom properties can be set without having to go into the internals of the custom element.