The current version of shadowDOM requires you to place your CSS inside the shadowDOM.
Most CSS is fairly small and only adds a few bytes to a few hundred bytes in each element. Some of the largest I have see add about 2k of CSS into each copy. But this is still very small compared to the data represented in the DOM structure.
There are a few things that will bleed through from the outside, like font information, but not many.
Here are some ways you can affect the from the outside:
1. CSS variables
A CSS Variable allows you to set a variable to a value that is used within the CSS whether in shadowDOM or not.
2. Attributes
Attributes can be captured and migrated into the shadowDOM CSS. I have a few components that use an attribute to define a theme.
3. Properties
Properties can also be taken and applied to internal CSS.
There are other ways in discussion, but those will have to wait until V2.
class MyEL extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
.outer {
color: var(--myColor, 'black');
</style>
<div class="outer">
<h4>Title</h4>
<slot></slot>
</div>`;
}
static get observedAttributes() {
return ['bgcolor'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal !== newVal) {
this.shadowRoot.querySelector('.outer').style.backgroundColor = newVal;
}
}
get border() {
return this.shadowRoot.querySelector('.outer').style.border;
}
set border(value) {
this.shadowRoot.querySelector('.outer').style.border = value;
}
}
customElements.define('my-el', MyEL);
setTimeout(() => {
document.querySelector('my-el').border = '2px dashed blue';
},1000);
const btn = document.getElementById('toggle');
let color = '';
btn.addEventListener('click', () => {
color = color === '' ? 'white' : '';
document.querySelector('my-el').style.setProperty('--myColor', color);
});
<my-el bgcolor="red"></my-el>
<hr/>
<button id="toggle">toggle</button>
UPDATED
As stated in my comment below there are a few CSS rules that will penetrate the shadowDOM if they are not overwritten. Things that penetrate are color
, background
, font
, and other things related to those.
This code shows an example of how they penetrate:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>
When you click on the toggle button you will see that things change color, background color, fonts, etc. But something like rotation only happens to the component and not to the sub elements withing the component. And that is a good thing. Imagine you the user could rotate things within your component... Your entire component would break.
I was looking for a good article that talked about all of the things that penetrate but couldn't find it. I have seen it in the past but forgot to bookmark it. So if someone finds that article please add it into the comments below.
Now here is the same code above with several things overridden so you can see how your component can override what is done on the outside:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<style>
:host * {
background: white;
color: yellow;
font-size: 14px;
}
</style><h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>