A WebComponent
may include CSS Custom Properties in its encapsulated styles.
This gives consumers of the component a way to customise that component's styles:
Declaring
--fancy-tabs-bg: red
in the main styles means that when the shadow-root
styles include
background-color: var(--fancy-tabs-bg, #9E9E9E)
the background-color
will be red
(or whatever value --fancy-tabs-bg
is set to).
But... I note in the same article it explicitly states:
One gotcha with
:host
is that rules in the parent page have higher specificity than:host
rules defined in the element. That is, outside styles win. This allows users to override your top-level styling from the outside.
and again, later:
Outside styles always win over styles defined in shadow DOM. For example, if the user writes the selector
fancy-tabs { width: 500px; }
, it will trump the component's rule::host { width: 650px;}
So... instead of declaring a value for --fancy-tabs-bg
we could just... set a value for background-color
(?)
Really? Let's find out.
Here's a WebComponent
(largely copied from the article referenced above) where the first two instances of the component are styled using a CSS Custom Property (ie. --fancy-tabs-bg
) and the third instance is styled directly, using the relevant CSS Property (ie. background-color
).
class FancyTabs extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({mode: "open"});
}
connectedCallback() {
this.root.innerHTML = `
<style>
:host {
display: block;
width: 100px;
height: 100px;
margin: 6px;
border-radius: 10px;
}
:host([background]) {
background-color: var(--fancy-tabs-bg, #9E9E9E);
}
</style>
`;
}
}
customElements.define('fancy-tabs', FancyTabs);
fancy-tabs {
float: left;
}
fancy-tabs:nth-of-type(1) {
--fancy-tabs-bg: red;
}
fancy-tabs:nth-of-type(2) {
--fancy-tabs-bg: orange;
}
fancy-tabs:nth-of-type(3) {
background-color: green;
}
<fancy-tabs background>...</fancy-tabs>
<fancy-tabs background>...</fancy-tabs>
<fancy-tabs background>...</fancy-tabs>
There's no difference, is there?
So why use CSS Custom Properties at all? Why highlight in the public interface that a particular CSS property is available for user-customisation? Surely all CSS properties are available for user-customisation, aren't they?
What am I missing?
You won't notice any difference when dealing with the host element but you can clearly see the use of CSS variables when having more elements inside:
Example where CSS variables is useful to update the styling of nested elements. I doubt you can find a better way without CSS variables.
class FancyTabs extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host {
display: block;
width: 100px;
height: 100px;
margin: 6px;
border-radius: 10px;
}
:host([background]) {
background-color: var(--fancy-tabs-bg, #9E9E9E);
}
div {
padding: var(--p,0px);
border:var(--b,0px) solid;
}`
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = this.innerHTML;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
customElements.define('fancy-tabs', FancyTabs);
fancy-tabs {
float: left;
}
fancy-tabs:nth-of-type(1) {
--fancy-tabs-bg: red;
--p:20px;
--b:5px;
}
fancy-tabs:nth-of-type(2) {
--fancy-tabs-bg: orange;
--p:10px;
--b:3px;
}
fancy-tabs:nth-of-type(3) {
--fancy-tabs-bg: orange;
padding:20px; /* will get applied to host*/
border-width:5px; /* will do nothing */
}
<fancy-tabs background>text here</fancy-tabs>
<fancy-tabs background>text here</fancy-tabs>
<fancy-tabs background>text here</fancy-tabs>