The web Components v1 specification consists of three main technologies that can be used to create reusable custom elements:
- Custom elements
- HTML templates
- Shadow DOM
:host
In addition to these technologies, CSS has also been updated to support web components. For instance, the new :host
pseudo selector has been added to select the web component from inside itself.
This pseudo selector can also be combined like you would in other scenarios.
/* <my-button> styles block */
:host{} /* selects the my-button tag */
:host(.primary){} /* selects the my-button when it has the primary class */
:host([variant="primary"]){} /* selects the my-button when the `primary` variant is set */
We'll see this in action in the following example.
Theming with CSS variables
There are different ways to pierce the shadow DOM when it comes to styling, the main one being CSS variables.
Quick example:
/* global-styles.css */
:root {
--brand-color: blue;
}
<!-- our web component template-->
<template>
<style>
button {
background-color: var(--brand-color);
}
</style>
...
</template>
Here, the template's button background will get the --brand-color
defined in :root
. In a nutshell, that's the primary mechanism for theming web components.
Theming the <my-button>
component
/* styles.css */
:root {
--background-color: #fff;
--color-brand: #0b66fa;
--color-white: #fff;
--color-black: #000000de;
--background-low-contrast: var(--color-white);
--background-high-contrast: var(--color-black);
--text-low-contrast: var(--color-white);
--text-high-contrast: var(--color-black);
--border-low-contrast: 1px solid var(--color-white);
--border-high-contrast: 1px solid var(--color-black);
}
html[data-theme='dark'] {
--background-color: #212a2e;
--background-low-contrast: var(--color-black);
--background-high-contrast: var(--color-white);
--text-low-contrast: var(--color-black);
--text-high-contrast: var(--color-white);
--border-low-contrast: 1px solid var(--color-black);
--border-high-contrast: 1px solid var(--color-white);
}
body {
background-color: var(--background-color);
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
<script src="my-button.js"></script>
<script>
function toggleTheme() {
if (document.querySelector('html').dataset.theme === 'dark') {
document.querySelector('html').dataset.theme = '';
} else {
document.querySelector('html').dataset.theme = 'dark';
}
}
</script>
</head>
<body>
<template id="my-button-template">
<style>
button {
cursor: pointer;
font-size: 20px;
font-weight: 700;
padding: 12px;
min-width: 180px;
border-radius: 12px;
}
:host([variant='primary']) button {
background-color: var(--color-brand);
color: var(--color-white);
border: 0;
}
:host([variant='secondary']) button {
border: var(--border-high-contrast);
background-color: var(--background-low-contrast);
color: var(--text-high-contrast);
}
</style>
<button><slot></slot></button>
</template>
<my-button variant="primary" onclick="toggleTheme()"
>Toggle theme</my-button
>
<my-button variant="secondary">Secondary</my-button>
<button class="primary">Plain HTML <button></button>
</body>
</html>
// my-button.js
class MyWebComponent extends HTMLElement {
constructor() {
super();
const template = document.getElementById('my-button-template').content;
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.cloneNode(true));
}
}
customElements.define('my-button', MyWebComponent);
By clicking the Toggle theme button, you will see how the overall look and feel changes from light to dark and vice versa.
A couple of interesting facts about this example:
- We've eliminated all style-handling logic from the component and moved it to the CSS declaration. In particular:
This Js code:
const variant = this.getAttribute('variant') || '';
this.shadowRoot.querySelector('button').className = variant;
Was replaced by this CSS:
:host([variant='primary']) button { ... }
:host([variant='secondary']) button { ... }
So we could remove all life cycle callbacks and the render()
function.
- Activating the dark theme is as easy as adding
data-theme="dark"
to the main<html>
tag.
<html data-theme="dark">...</html>
The magic happens by pure CSS matching :)
Coming up next
The following article will show how web components compose by building a dropdown.
Top comments (0)