Here is some code that created a component with shadowDOM. For simplicity I am emulating a long load time for the CSS by using setTimeout. After 1 second I apply the CSS into the element.
As you stated the element looks one way until the CSS is loaded.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FOUC Prevention for WC</title>
<script>
var delayedStyles = document.createElement('style');
delayedStyles.textContent = `
:host {
background-color: #DEE;
border: 1px solid #999;
display: block;
width: 400px;
}
h1 {
color: green;
font: 18px/1em Tahoma;
padding: 3px 6px;
}
p {
margin: 5px 10px;
padding: 10px;
}
`;
var template = document.createElement('div');
template.innerHTML = `
<h1>The header</h1>
<p>Some body content</p>
`;
// Class for `<my-component>`
class MyComponent extends HTMLElement {
constructor() {
super();
var sr = this.attachShadow({mode: 'open'});
setTimeout(() => sr.appendChild(delayedStyles.cloneNode(true)), 1000);
sr.appendChild(template.cloneNode(true));
}
}
// Define our web component
customElements.define('my-component', MyComponent);
</script>
</head>
<body>
<my-component></my-component>
</body>
</html>
With a minor change we can hide the element until the CSS loads:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FOUC Prevention for WC</title>
<script>
var delayedStyles = document.createElement('style');
delayedStyles.textContent = `
:host {
background-color: #DEE;
border: 1px solid #999;
display: block;
width: 400px;
}
.innerShell {
display: block !important;
}
h1 {
color: green;
font: 18px/1em Tahoma;
padding: 3px 6px;
}
p {
margin: 5px 10px;
padding: 10px;
}
`;
var template = document.createElement('div');
template.setAttribute('style', 'display: none;');
template.className = 'innerShell';
template.innerHTML = `
<h1>The header</h1>
<p>Some body content</p>
`;
// Class for `<my-component>`
class MyComponent extends HTMLElement {
constructor() {
super();
var sr = this.attachShadow({mode: 'open'});
setTimeout(() => sr.appendChild(delayedStyles.cloneNode(true)), 1000);
sr.appendChild(template.cloneNode(true));
}
}
// Define our web component
customElements.define('my-component', MyComponent);
</script>
</head>
<body>
<my-component></my-component>
</body>
</html>
To do this I set the style tag of my top-level element in my shadow DOM. I set it to display: none; This hides the inner content of the shadow DOM.
Then, 1 second later, when the CSS is loaded, it overrides the display: none; with display: block !important. I have to use !important to become more specific then the css set in the style tag.
Only after the CSS loads does my element becomes visible.
As another options You could also place an onload event handler on your <link> tag:
<link rel="stylesheet" href="mystylesheet.css" onload="sheetLoaded()"
onerror="sheetError()">
Which would let you know, in code, that it was loaded and then just remove the style attribute.