Back for another year, it's 8 days of Web Components tips!
Candle 1: Hiding Unwanted Content
On the 1st night, let's start at the end. When the Maccabees liberated the Holy Temple in Jerusalem from Seleucid Greek invaders, they refused to light the Menorah with the impure oil they found left over.
To prevent your element from displaying unwanted content, use the ::slotted
pseudo element and the :not
pseudo class in your CSS.
::slotted(shemen-zayit:not([type="zach"])) {
display: none !important;
}
Candle 2: CSS Shadow Parts
CSS shadow parts let users of your element style parts of it from the
outside, but did you know that just like the class
attribute, part
can have
multiple part names, & like the class selector (.
), the ::part()
pseudo
element can take a space-separated list?
That means you can create multiple overlapping sets of parts, to let your users choose how mehudar (beautiful) their elements should be.
render() {
return html`
<ul>${Array.from({ length: 8 }, (_, i) => html`
<li part="candle ${this.night < i ? '' : 'un'}lit"></li>`)}
<li part="candle shamash lit"></li>
</ul>
`;
}
web-menorah::part(candle lit)::after {
content: '🕯️';
}
web-menorah::part(candle unlit)::after {
content: '🕳️';
}
web-menorah::part(candle shamash)::after {
translate: 0 8px;
}
Candle 3: Form-Associated Custom Elements
Form-Associated Custom Elements can participate in the lifecycle of HTML form
elements. Set the static formAssociated
boolean flag on the element class and
call attachInternals()
to hook into the browser's accessibility tree.
Create custom form controls that work like native ones!
<form>
<fieldset>
<legend>Create Your Own Custom Controls</legend>
<label for="menorah">Which Night?</label>
<web-menorah id="menorah" name="night" night="3"></web-menorah>
<label for="location">Light Outside?</label>
<custom-switch id="location" checked></custom-switch>
</fieldset>
<custom-button>Light!</custom-button>
</form>
Read more in my post on FACE.
Candle 4: Private State
Want to style your elements based on their state, but don't want to create new
public APIs or new HTML attributes? Use Lit's classMap
directive to set a
class on a shadow container based on the private state.
Not using Lit? No problem, classMap
is a performance optimization but you
could do the same thing imperatively via the DOM or using your component
library's APIs.
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ApolloQueryController } from '@apollo-elements/core';
import { MyQuery } from './my.query.graphql';
export class StatefulElement extends LitElement {
#query = new ApolloQueryController(this, MyQuery);
render() {
const { loading, error } = this.#query;
return html`
<div id="container"
class="${classMap({ loading, error })}">
<p>${this.#query.data}</p>
</div>`;
}
}
#container {
display: contents;
}
.loading {
opacity: 0.5;
background: grey;
}
.error {
color: red;
}
Candle 5: Noscript
Not every end-user of your web component can or wants to enable
javascript. Make sure to add <noscript>
examples to your documentation.
Even if your custom element does fancy footwork like syntax-highlighting non-js
script tags by reading textContent
, you can still gracefully degrade for
noscript users.
<code-block>
<script type="text/snippet-js">
console.log("With JS on, this gets highlighted but not run");
</script>
</code-block>
<style>
code-block:not(:defined),
code-block:not(:defined) > script {
display: block;
}
</style>
<noscript>
<style>
code-block, code-block > script {
display: block;
}
</style>
</noscript>
Candle 6: Bare Module Specifiers
The Maccabees fought for the freedom to control their own destiny.
If your web component imports dependencies (like a base class or utilities) from an NPM module, use "bare" module specifiers in your sources. This gives your users maximum flexibility, letting them bundle, serve via module-transforming CDN (like JSPM or UNPKG) or use import maps to resolve the import specifiers to URLs.
// ✅ DO
import { LitElement } from 'lit';
// ❌ DON'T
import { LitElement } from '../../node_modules/lit/index.js';
Candle 7: Interop, Part 1
The Seleucid king Antiochus tried to impose a uniform Hellenistic culture on the Jews, but your organization doesn't need to suffer a front-end monoculture. Web components work everywhere HTML does, so use them in your CMS' templates, in templates, in templates, in templates, in templates, in in in in your in your your your your your your your Angular forms, in your Vue or Preact apps, or on performant and accessible web pages.
Write once, use everywhere.
Candle 8: Interop, Part 2
The last night of Hannukah is called "This is Hannukah", since it encapsulates the entirety of the holiday.
The big idea of web components is interoperability, meaning you can use components with each other, mixing & matching, no matter how they were written. The common interface of HTML, CSS, & DOM events/props ties them together.
Use lit components with stencil, or hybrids with FAST, and vice versa.