Importing styles into a web component
NB!!!
THIS ANSWER IS OUTDATED
PLEASE CHECK THE ANSWER BY Himanshu Sharma
Up-to-date answer: https://stackoverflow.com/a/48202206/2035262
According to Polymer documentation:
Polymer allows you to include stylesheets in your<polymer-element>
definitions, a feature not supported natively by Shadow DOM.
This is a bit weird reference, but I could not google the straight one. It looks like at the moment there is no rumors about supporting links inside templates.
That said, whether you want to use vanilla web component, you should either inline your css with <style>
tag, or load and apply your css manually in javascript.
Constructable Stylesheets
This is a new feature that allows for the construction of CSSStyleSheet
objects. These can have their contents set or imported from a css file using JavaScript and be applied to both documents and web components' shadow roots. It will be available in Chrome with version 73 and probably in the near future for Firefox.
There's a good writeup on the Google developers site but I'll summarize it briefly below with an example at the bottom.
Creating a style sheet
You create a new sheet by calling the constructor:
const sheet = new CSSStyleSheet();
Setting and replacing the style:
A style can be applied by calling the methods replace
or replaceSync
.
replaceSync
is synchronous, and can't use any external resources:sheet.replaceSync(`.redText { color: red }`);
replace
is asynchronous and can accept@import
statements referencing external resources. Note thatreplace
returns aPromise
which needs to be handled accordingly.sheet.replace('@import url("myStyle.css")') .then(sheet => { console.log('Styles loaded successfully'); }) .catch(err => { console.error('Failed to load:', err); });
Applying the style to a document or shadow DOM
The style can be applied by setting the adoptedStyleSheets
attribute of either the document
or a shadow DOM.
document.adoptedStyleSheets = [sheet]
The array in adoptedStyleSheets
is frozen and can't be mutated with push()
, but you can concatenate by combining with its existing value:
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
Inheriting from the document
A shadow DOM can inherit constructed styles from the document's adoptedStyleSheets
in the same way:
// in the custom element class:
this.shadowRoot.adoptedStyleSheets = [...document.adoptedStyleSheets, myCustomSheet];
Note that if this is run in the constructor, the component will only inherit the style sheets that were adopted prior to its creation. Setting adoptedStyleSheets
in the connectedCallback
will inherit for each instance when it is connected. Notably, this will not cause an FOUC.
Example with Web Components
Let's create a component called x-card
that wraps text in a nicely styled div
.
// Create the component inside of an IIFE
(function() {
// template used for improved performance
const template = document.createElement('template');
template.innerHTML = `
<div id='card'></div>
`;
// create the stylesheet
const sheet = new CSSStyleSheet();
// set its contents by referencing a file
sheet.replace('@import url("xCardStyle.css")')
.then(sheet => {
console.log('Styles loaded successfully');
})
.catch(err => {
console.error('Failed to load:', err);
});
customElements.define('x-card', class extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'
});
// apply the HTML template to the shadow DOM
this.shadowRoot.appendChild(
template.content.cloneNode(true)
);
// apply the stylesheet to the shadow DOM
this.shadowRoot.adoptedStyleSheets = [sheet];
}
connectedCallback() {
const card = this.shadowRoot.getElementById('card');
card.textContent = this.textContent;
}
});
})();
<x-card>Example Text</x-card>
<x-card>More Text</x-card>
If you need to place external styles inside the <template>
tag you could try
<style> @import "../my/path/style.css"; </style>
however I have a feeling this will start importing after the element has been created.
Now direct <link>
tag is supported in shadow dom.
One can directly use:
<link rel="stylesheet" href="yourcss1.css">
<link href="yourcss2.css" rel="stylesheet" type="text/css">
It has been approved by both whatwg and W3C.
Useful links for using css in shadow dom:
- https://w3c.github.io/webcomponents/spec/shadow/#inertness-of-html-elements-in-a-shadow-tree
- https://github.com/whatwg/html/commit/43c57866c2bbc20dc0deb15a721a28cbaad2140c
- https://github.com/w3c/webcomponents/issues/628
Direct css link can be used in shadow dom.