Lion without polyfills
The only reason Lion always loaded a polyfill was because of its usage of @open-wc/scoped-elements. From today on this polyfill became optional.
When using component composition in a Lion Component we always made it very explicit which sub-components are used. On top of that we scoped these sub components to the current shadow root allowing multiple version to be used simultaneously.
This means that you can use a Lion Component like lion-listbox
(which uses component composition) and never have to worry about if the internally used components clash with others you are already using.
How does it work?
- Within Lion classes we only import other classes (e.g. in class
MyCard
we useMyCardHeader
via composition) - We define them as
scopedElements
(my-card-header: MyCardHeader
) and let the ScopedElementsMixin handle the rest
To clarify: within Lion class files we never import files that run customElement.define
import { LitElement, html } from 'lit';
import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
import { MyCardHeader } from './MyCardHeader.js';
export class MyCard extends ScopedElementsMixin(LitElement) {
static scopedElements = {
'my-card-header': MyCardHeader,
};
render() {
return html`
<div>
<my-card-header></my-card-header>
<slot></slot>
</div>
`;
}
}
Known challenges in previous releases
The code above totally makes sense - however we always assumed that a scoped registry will be available.
Which was somewhat of a valid assumption as all our components are using the ScopedElementsMixin
and it in turn loads a polyfill for the scoped registry.
We however over time got feedback from multiple consumers that lion components "break the app as soon as you load them".
The reasons is/was that not everyone is always using ScopedElementsMixin
or in full control of the app (or its load order).
To quote the release notes of the latest version of ScopedElementsMixin
:
ScopedElementsMixin 2.x tried to be as convenient as possible by automatically loading the scoped custom elements registry polyfill. This however led to a fatal error whenever you registered any component before ScopedElementsMixin was used.
And this was the case.
How do we fix it?
With the latest release of Lion we now updated to the latest version of ScopedElementsMixin
which means Lion now works in all apps as long as there is no need for actual scoping.
To rephrase it:
Lion works without loading any polyfills
If you extend Lion components and you imperatively create scoped custom elements, you should now use a helper function that will work in scoped and unscoped cases.
- const myButton = this.shadowRoot.createElement('my-button');
+ const myButton = this.createScopedElement('my-button');
Be explicit and stay forward compatible
Be sure to always define ALL the sub elements you are using in your template within your scopedElements
property.
import { LitElement, html } from 'lit';
import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
import { MyCardHeader } from './MyCardHeader.js';
export class MyCard extends ScopedElementsMixin(LitElement) {
static scopedElements = {
'my-card-header': MyCardHeader,
};
render() {
return html`
<div>
<my-card-header></my-card-header>
<slot></slot>
<my-card-footer></my-card-footer>
</div>
`;
}
}
☝️ here we are missing a definition for my-card-footer
in scopedElements
.
This means as soon as there is support for the scoped registry (be it native of via a polyfill) this component will not be available anymore because every new scoped registry starts off empty (there is no inheritance of a global or parent registry).
Therefore always define all your sub elements.
How to get scoping
You need scoping if you want to:
- use 2 major versions of a web component (e.g. in an SPA pageA uses 1.x and pageB uses 2.x of color-picker)
- use the same tag name with different implementations (use tag color-picker from foo here and from bar here)
This usually is only needed in bigger Single Page Applications. In smaller applications or static sites (like 11ty, wordpress, ...) these tag name clashes are unlikely.
If you need scoping and the browser you are using does not support a scoped registry yet (which is none in April 2022) then you need to install and load a polyfill first thing in your HTML.
npm i @webcomponents/scoped-custom-element-registry
It could look something like this:
<script src="/node_modules/@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min.js"></script>
or if you have an SPA you can load it at the top of your app shell code
import '@webcomponents/scoped-custom-element-registry';
Learn more
If you want to learn more please check the