From lit
Lit web component library: LitElement, reactive properties, templates, reactive controllers, @lit/task, @lit/context, directives, Shadow DOM a11y. Use for files importing from 'lit', 'lit/decorators.js', '@lit/task', '@lit/context', or 'lit/directive.js'. Not for the lit-router, web-component-router, or jh-design-system skills.
npx claudepluginhub christopherdavenport/christopherdavenport-marketplace --plugin litThis skill uses the workspace's default tool permissions.
Lit is a lightweight (~5 KB) library for building standard web components — native browser support, framework-agnostic, interoperable. Three pillars: reactive state, declarative `html` templates (no virtual DOM), and Shadow DOM style scoping.
references/accessibility.mdreferences/code-examples.mdreferences/context.mdreferences/custom-directives.mdreferences/forms-and-element-internals.mdreferences/lifecycle.mdreferences/reactive-controllers.mdreferences/reactive-properties.mdreferences/signals.mdreferences/task-controller.mdreferences/templates-and-directives.mdreferences/testing.mdProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Applies Acme Corporation brand guidelines including colors, fonts, layouts, and messaging to generated PowerPoint, Excel, and PDF documents.
Share bugs, ideas, or general feedback.
Lit is a lightweight (~5 KB) library for building standard web components — native browser support, framework-agnostic, interoperable. Three pillars: reactive state, declarative html templates (no virtual DOM), and Shadow DOM style scoping.
Covers: LitElement, reactive properties (@property / @state), html and css tagged templates, lifecycle, decorators, @lit/task, reactive controllers, @lit/context, form-associated elements, ElementInternals, custom directives, Shadow DOM accessibility (ARIA, focus management), and testing.
LitElement, html, css from lit and decorators from lit/decorators.js@customElement('tag-name') — tag name must contain a dashstatic styles using the css tagged template@property() (public API) or @state() (internal)render() returning an html tagged templateHTMLElementTagNameMap via declare globalConsult references/reactive-properties.md for property options, type conversion, and decorators. Consult references/code-examples.md for full component patterns.
@lit/task: npm i @lit/taskTask field with task (async function) and args (reactive inputs){ signal } to fetch for automatic cancellationthis._task.render({ pending, complete, error }) in render()autoRun: false and call .run() manually if the task should not run on every args changeConsult references/task-controller.md for the full API, argument tracking, and advanced patterns.
Event with a static readonly type and typed propertiesbubbles: true, composed: true in the constructor so events cross Shadow DOMawait this.updateComplete so listeners see the rendered stateConsult references/reactive-properties.md for the typed event pattern.
ReactiveController with a constructor that accepts ReactiveControllerHost and calls host.addController(this)hostConnected() for setup (listeners, timers, observers) and hostDisconnected() for cleanupthis._host.requestUpdate() when state changesReactiveControllerHost (not LitElement) to keep the controller framework-agnosticConsult references/reactive-controllers.md for interfaces, lifecycle integration, common patterns, and composition.
@lit/context: npm i @lit/contextcreateContext<T>(Symbol('key')) — always use Symbol for uniqueness@provide({ context }) with @property({ attribute: false }) on a provider component@consume({ context, subscribe: true }) with @property({ attribute: false }) on consumers — subscribe: true is required for runtime updatesContextRoot to the app shell if providers may load after consumers (lazy loading, dynamic rendering)Consult references/context.md for the full API, nested providers, ContextRoot, pitfalls, and context vs signals vs properties guidance.
@lit-labs/signals: npm i @lit-labs/signals — Labs package, flag the experimental status to the usersignal(initial); read via .get(), write via .set()SignalWatcher(LitElement) mixin to any element that reads signalshtml tag from @lit-labs/signals for implicit watching, or the watch(signal) directive for pinpoint updates@lit/context instead of using subscribe: trueConsult references/signals.md for the full API, rendering patterns, context integration, and pitfalls.
static formAssociated = true and private _internals = this.attachInternals()this._internals.setFormValue() in every input handler and whenever the value changessetValidity(), providing the anchor element for validation popupsformResetCallback() to clear internal state when the form resetsthis._internals.role and this._internals.ariaLabel for accessible ARIA (not ID-based attributes)Consult references/forms-and-element-internals.md for the full API, lifecycle callbacks, validation, and a complete example. Consult references/accessibility.md for focus management, keyboard interaction, and screen reader patterns.
vitest and happy-dom: npm i -D vitest happy-domvitest.config.ts with environment: 'happy-dom'document.createElement() and append to the DOMawait el.updateComplete before asserting on DOM contentel.shadowRoot!.querySelector() — not el.querySelector() (that's light DOM)Consult references/testing.md for a reusable fixture() helper, event testing, context providers, Task state testing, and common pitfalls.
Default to @property for inputs and typed events for outputs. Reach for context, signals, or controllers only when that pattern would force prop-drilling through 3+ intermediate components, lose reactivity across boundaries, or duplicate the same lifecycle logic in multiple components. The table below lists the escape hatches and the specific condition that justifies each.
| Scenario | Use | Why |
|---|---|---|
| External component input | @property() | Formal API with attribute binding |
| Private render-driving state | @state() | No attribute; triggers updates |
| Async data (fetch, DB) | Task | Status tracking, cancellation, race prevention |
| Ongoing external resource (timer, observer, WebSocket) | Reactive controller | Lifecycle-aware setup/teardown, composable, reusable |
| Reusable cross-component behavior | Reactive controller | Has-a composition; multiple instances per host |
| Derived/computed values | willUpdate() | Computed once per cycle, before render |
| One-time DOM setup | firstUpdated() | DOM guaranteed to exist |
| Post-render side effects | updated() | DOM reflects latest state |
| Dynamic CSS classes | classMap() | Clean conditional class binding |
| Dynamic inline styles | styleMap() | Direct property binding |
| Efficient list rendering | repeat() with key | Minimizes DOM churn on reorder |
| External style customization | CSS custom properties | Pierce Shadow DOM cleanly |
| Parent-child data flow (any depth that's still tractable) | @property down, typed events up | Default Lit pattern — explicit, type-safe, no extra primitives |
| Value needed by descendants 3+ levels deep, rarely changes | @lit/context (no subscribe) | Avoids prop drilling once intermediate components have nothing to do with the value |
| Same shared value but changes at runtime | @lit/context with subscribe: true | Consumers stay in sync; tolerable when changes are infrequent |
| Shared value changes frequently AND needs fine-grained updates | @lit-labs/signals (often via context) | Pinpoint DOM updates without full re-render or subscribe re-fire |
Custom element inside a <form> | Form-associated + ElementInternals | Enables form submission, validation, reset |
| Reusable template transformation | Custom directive | Direct DOM Part access, async lifecycle |
| Testing component behavior | Vitest + happy-dom + fixture() helper | Fast unit tests with DOM simulation |
These cross-cutting rules influence which path Claude takes on most Lit tasks. API-specific guidance lives in the topic references below.
@property/@state/context objects or arrays in place. Lit's change detection is reference equality (!==), so in-place mutations don't trigger updates. Use spread/assignment, or call requestUpdate() if you must mutate.render() must be pure — no side effects, no property mutations, no DOM reads. Move derived state to willUpdate(), post-render side effects to updated().willUpdate() for derived state — computed once per cycle, before render. Not render() (impure) and not updated() (causes a second cycle).await this.updateComplete — listeners see the fully rendered state, not a stale DOM.super in constructor, connectedCallback, and disconnectedCallback. Lit relies on the base class running its own setup/teardown.Task when it's a request/response. Reach for inline lifecycle hooks only for one-off, component-specific logic.Example 1: User says "create a user card component" Actions:
@customElement('user-card') with @property() for name, emailstatic stylesrender() with property bindingsdeclare global for HTMLElementTagNameMap
Result: A typed, styled, self-contained web componentExample 2: User says "fetch user data in my Lit component" Actions:
@lit/task importTask with args: () => [this.userId] and { signal } in the fetch call_task.render({ pending, complete, error }) for loading/error states
Result: Async data fetching with automatic cancellation and status UIExample 3: User says "my component isn't re-rendering when I push to an array" Actions:
this.items.push(...))this.items = [...this.items, newItem]Each reference below has a "Common Pitfalls" section covering the bugs specific to its API. Route by symptom:
| Symptom | Reference |
|---|---|
| Component not re-rendering, stale DOM after mutation | reactive-properties.md |
| Custom event not received, doesn't cross Shadow DOM | reactive-properties.md |
Styles leaking, missing :host, inline handler issues | templates-and-directives.md |
| Task runs every render, stale results, never runs | task-controller.md |
| Form ignores component, value missing, doesn't reset | forms-and-element-internals.md |
ARIA not announced, focus issues, aria-labelledby broken | accessibility.md |
Lifecycle ordering, updateComplete timing | lifecycle.md |
Context value not updating, @consume stuck on initial | context.md |
| Signal change doesn't update DOM, polyfill mismatch | signals.md |
| Test stale, fixture not awaiting, mocked fetch leaks | testing.md |
Consult these for detailed API, options, patterns, and code examples:
@property() options, @state(), type conversion, custom converters, mutation rules, decorators, HTMLElementTagNameMap, typed eventsnothingupdateComplete, changedProperties, async patternsReactiveController interface, lifecycle hooks, custom controllers, composition, controller vs mixin vs Task@lit/context API, @provide/@consume, ContextRoot, nested providers, context vs signals vs properties@lit-labs/signals (Labs), SignalWatcher mixin, watch() directive, signals html tag, effect(), context integration@lit/task API, status states, auto-run vs manual, cancellation, race conditions, argument trackingsetFormValue(), constraint validation, form lifecycle callbacks, ARIA via AOMDirective, AsyncDirective, noChange vs nothing, Part types, setValue(), async lifecycleElementInternals AOM, focus management, delegatesFocus, roving tabindex, keyboard interaction, focus trappingfixture() helper, updateComplete, event testing, context providers, Task state testing