diff --git a/agent-docs/lit-muscle-memory-gotchas.md b/agent-docs/lit-muscle-memory-gotchas.md index 80f271677..6fe882d5c 100644 --- a/agent-docs/lit-muscle-memory-gotchas.md +++ b/agent-docs/lit-muscle-memory-gotchas.md @@ -64,6 +64,66 @@ Practical consequences for agents writing webjs code. (optimistic UI, in-flight indicators tied to client state, keyboard shortcuts). +## Use lit idioms, not vanilla DOM (the whole point of lit-style components) + +webjs components are lit-shaped on purpose: the value is the declarative +DX (typed reactive props, signals, `html` templates, declarative +bindings), not raw DOM scripting. Reaching for vanilla web-component +muscle memory (`this.getAttribute`, `this.setAttribute`, `this.classList`, +`this.addEventListener`, `this.innerHTML`, `document.createElement`, +manual `observedAttributes` / `attributeChangedCallback`, manual +`customElements.define`) inside a component is the anti-pattern. Use the +lit form unless the vanilla API is genuinely unavoidable. + +| Vanilla muscle memory | Lit-style webjs form | +|---|---| +| `this.getAttribute('x')` / `this.hasAttribute('x')` for own config | a reactive prop: `static properties = { x: {...} }` + `declare x`, read `this.x` (the prop rides the `x` attribute) | +| `this.setAttribute('x', v)` / `removeAttribute` to reflect own state | a reactive prop with `reflect: true`, or for non-attribute state a `signal` | +| `state: true` reactive prop for internal state | a `signal` (instance signal in the constructor, or module-scope) | +| `this.classList.add/toggle(...)` on self | a `class=${...}` binding in `render()` | +| `this.innerHTML = ...` / `appendChild` / `document.createElement` | return the markup from `render()` as `` html`...` `` | +| `this.addEventListener('click', ...)` on own/child elements | a `@click=${...}` binding in the template | +| `this.querySelector(...)` to reach own rendered DOM | the `ref()` directive + `createRef()`, or read a `