Skip to content

Repository files navigation

template web component

A template for vanilla web components.

see also

use

  1. Use the template button in github. Or clone this then rm -rf .git && git init. Then npm i && npm init.
  • Use the template system to re-name this module and start the docs:
node ./bin/cli.js

The CLI prompts for several variables

  • gh-namespace -- first path segment on github
  • package-name -- package name, including any namespace. eg, @alice/package
  • component-name -- the name of the web component, as used in HTML, eg cool-example
  • repo-name -- repository name, the last segment in github URL, eg, github.com/user/repo-name-here
  1. Edit the source code in src/index.ts.

  2. Edit things

    • edit the build-example command in package.json so that it has the right path for github pages

featuring

  • compile the source to both ESM and CJS format, and put compiled files in dist.
  • ignore dist and *.js in git, but don't ignore them in npm. That way we don't commit any compiled code to git, but it is available to consumers.
  • use npm's prepublishOnly hook to compile the code before publishing to npm.
  • use exports field in package.json to make sure the right format is used by consumers.
  • preversion npm hook -- lint
  • version npm hook -- generate a TOC for the README, and create and add a changelog
  • postversion npm hook -- git push --follow-tags && npm publish
  • eslint -- npm run lint
  • tests run in a real browser via tapout -- see npm test. Assertions come from tapzero. The test bundle needs --bundle, or bare module specifiers will not resolve in the browser.
  • CI via github actions
  • stylelint -- see preversion npm hook

the component

The component extends @substrate-system/web-component, which supplies namespaced events, qs/qsa, and attribute reflection. The conventions are recorded as architecture decision records; the vocabulary is in the glossary.

attributes

Declare attributes with the static arrays. The base class generates the getters and setters and derives observedAttributes from them, so there is no static observedAttributes to maintain:

static reflectedStringAttributes = ['example']
static reflectedBooleanAttributes = ['disabled']

declare example:string|null
declare disabled:boolean

React to a change in a handleChange_<attribute> method. attributeChangedCallback routes to it by name. Dispatch is by convention, so adding an attribute means declaring it and adding the method -- a missing handler is skipped silently. See ADR-004.

events

Emit namespaced events with emit, listen with on, and build the event name with the static event() helper:

el.emit('hello', { detail: 'some data' })   // 'cool-example:hello'
el.on('hello', ev => { /* ... */ })
el.addEventListener(Example.event('hello'), ev => { /* ... */ })

See ADR-005.

rendering

render() is abstract, so every component implements it. The base connectedCallback() calls it; if you override connectedCallback, call super.connectedCallback() or nothing renders.

Attribute changes do not re-render. Update the DOM in place from the handleChange_* method instead.

FOUCE

A custom element is inert markup until its JS defines it, which shows up as a flash of undefined custom element. This template handles it in two layers, described in ADR-006.

src/index.css hides the element until it is defined. This ships with the component:

cool-example:not(:defined) {
    display: none;
}

example/ demonstrates the page-level pattern: a reduce-fouce class on <html>, removed once customElements.whenDefined resolves for every component on the page. Two safeguards are required, not optional -- a Promise.race timeout, and a <noscript> override. Without them, a component that never defines leaves the page permanently blank.

About

A template for web components

Topics

Resources

Stars

Watchers

Forks

Used by

Contributors

Languages