wcb/README.md
Ayo 1b3e251a9d
Some checks are pending
Tests / Unit (push) Waiting to run
Tests / E2E (push) Waiting to run
feat!: reflect boolean props as bare attributes
Boolean props follow the HTML presence/absence semantics.
- We stick close to standard HTML behavior: flag="false" is treated as
true in most cases. Standard bare attributes disabled and required for
form fields are interpreted as true with non-existence as false.
- The use-cases for aria-*="false" and contenteditable="false" are also
supported by typing them in the JS class as string ("true" | "false").
This "true" | "false" opt-in is types-only. At runtime it's just a
string, and strings already serialize literally and are never removed —
so the enumerated/aria path needs no runtime code.
2026-07-20 19:57:23 +02:00

4.7 KiB

Web Component Base

Warning

Breaking change in v6 — boolean props are now bare attributes. A boolean prop follows the HTML convention in both directions: presence means true, absence means false. true reflects as a bare attribute and false removes it, so toggleAttribute() and [attr] CSS selectors finally work as expected.

Any present value is true — including the literal flag="false", just like native disabled="false" is still disabled. If you write boolean attributes as setAttribute(name, String(bool)), that now always means true; switch those call sites to toggleAttribute(name, bool). wcb warns in the console when it sees a boolean attribute written as "true"/"false" so the change cannot fail silently. Attributes whose "false" is meaningful (aria-*, contenteditable) should be declared as string props.

See Prop Access for details.

Package information: NPM version Package information: NPM license Package information: NPM downloads Bundle Size

🤷‍♂️ zero-dependency, 🤏 tiny JS base class for creating reactive custom elements easily

This is the base class used for web components in Ayo's projects, primarily cozy-games, mcfly, his personal site, his blog, and others.

Next actions:

  1. Read the docs
  2. View a demo on CodePen.

counter example code snippet

When you extend the WebComponent class for your component, you only have to define the template and properties. Any change in any property value will automatically cause just the component UI to render.

The result is a reactive UI on property changes.

When the template is an html tagged template (a vnode tree), a re-render patches the existing DOM in place instead of rebuilding it: elements of the same tag are reused, only changed props/attributes/text are touched, and leftover nodes are trimmed. Transient DOM state on the rendered nodes therefore survives a re-render — focus, caret/selection, an <input>'s uncommitted value, :hover, and running CSS transitions. A controlled text field can write every keystroke back to a prop without losing focus. The first render still builds the tree from scratch, and string templates still assign to innerHTML.

Patching is index-based and non-keyed: list items are matched by position, not identity. The resulting DOM is always correct, but preserved state follows the position rather than the item — remove the first row of a list and the node that held row 1 is rewritten to hold row 2, so a caret, an uncommitted value, or a running transition stays where it was while the content shifts under it. Fixed-order lists and append/remove-at-the-end are unaffected; reorderable lists of focusable or animated items are the sharp edge. A key prop for identity-based matching is not implemented.

Want to get in touch?

There are many ways to get in touch:

  1. Open a GitHub issue or discussion
  2. Submit a ticket via SourceHut todo
  3. Email me: hi@ayo.run

Inspirations and thanks

  1. htm - I use it for the html function for tagged templates, and take a lot of inspiration in building the rendering implementation. It is highly likely that I will go for what Preact is doing... but we'll see.
  2. fast - When I found that Microsoft has their own base class I thought it was super cool!
  3. lit - lit-html continues to amaze me and I worked to make wcb generic so I (and others) can continue to use it

Size change log

See size-change-log.md for a running record of how each correctness/feature change affects the WebComponent base class bundle size, with the reason and benefit of each.


Just keep building.
A project by Ayo