# Elena Authoring

> Critical rules for authoring Elena components. Component types, props, events, templates, lifecycle, mixins, and framework compatibility. Use when this capability is needed.

- Skill: `tomevault-io/elena-authoring` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/elena-authoring`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/elena-authoring/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/elena-authoring

---


# Elena Authoring — Critical Rules

> Full reference: https://getelena.github.io/elena/llms-full.txt
> Page index: https://getelena.github.io/elena/llms.txt

## Component types

Three types — the type determines whether `render()` is present:

- **Primitive**: owns its DOM, MUST have `render()` returning an `html` tagged template. Elena calls `replaceChildren()` on every render.
- **Composite**: wraps composed children, must NOT have `render()`. Never touches light DOM children.
- **Declarative**: hybrid using `<template shadowrootmode="open">` in the HTML markup. No `render()`.

## Props

- Props must be declared in BOTH `static props` AND as class field defaults. Neither alone is sufficient.
- The type is inferred from the default value: `false` → Boolean, `0` → Number, `[]` → Array, `{}` → Object, `"..."` → String.
- `"text"` is a reserved built-in prop. Never include it in `static props` — Elena throws: `"text" is reserved.`
- Use `{ name: "icon", reflect: false }` in `static props` to suppress attribute reflection for a specific prop.
- Annotate every prop with JSDoc `@property` and `@type` on the class field.

## Events

- `static events` tells Elena which events to manage from the inner element.
- Bubbling events (like `click`, `change`, `input`) pass through to the host naturally with all their original properties intact.
- Non-bubbling events (like `focus` and `blur`) are forwarded to the host as plain `Event` instances.
- Never override `handleEvent()` on a component that uses `static events` — Elena uses it internally for delegation.
- Dispatch custom events with `new CustomEvent("my-event", { bubbles: true, composed: true, detail: { value } })`.

## Templates

- `render()` must return an `html` tagged template literal. Import `html` and `nothing` from `@elenajs/core`.
- Use `nothing` (not `""` or `false`) in conditional expressions to avoid template shape changes.
- Nested `html` fragments pass through without double-escaping. Plain string values are auto-escaped (XSS-safe).
- `this.text` captures the element's `textContent` on connect. Use it in `render()` for text content.
- `unsafeHTML(str)` bypasses escaping — only use for trusted, sanitized content.
- `html` does NOT block JavaScript URIs. Always validate URLs before interpolating into `href` or other URL attributes: `const safeUrl = /^https?:\/\//.test(url) ? url : "#";`

## Lifecycle

- `willUpdate()` — runs before every render. Must NOT call `super`. Use for derived state.
- `firstUpdated()` — runs once after the first render. `this.element` is available here.
- `updated()` — runs after every render, including the first. Runs after `firstUpdated()`.
- `this.element` is available in `render()`, lifecycle methods, and custom methods. It is resolved after the first render completes.
- `requestUpdate()` — manually schedule a re-render when Elena cannot detect a change (e.g. mutating an array in place).
- `updateComplete` — Promise that resolves after the current render microtask finishes. Use to await DOM updates: `await element.updateComplete`.
- All lifecycle methods except `willUpdate()` should call `super`.

## Mixins

- Apply mixins after `Elena()`: `class Foo extends Draggable(Elena(HTMLElement)) {}`.
- Mixins that override lifecycle methods must call `super` (except `willUpdate()`).
- Props introduced by a mixin must be listed in `static props` on the final concrete class.

## Registration

- Always call `ClassName.define()` after the class body, not inside it.
- `define()` is a no-op in non-browser environments (SSR-safe).

## Framework compatibility

- Never render a framework component inside a Primitive Component. Elena calls `replaceChildren()` on render, destroying the framework tree.
- For dynamic text content in frameworks, use the `text` property: `<elena-button text={label} />`. Children won't update after hydration.
- Avoid letting the framework and Elena both mutate the same attribute — the framework's reconciler will win on next render.
- **Angular:** Text children are inserted after `connectedCallback` fires, so Elena has already replaced the host's inner DOM by then. Always use `text` as a property binding, never as a child node: `<elena-button [text]="label"></elena-button>`.
- **React 17:** Does not pass `Array` or `Object` type props or event handlers to custom elements correctly. Use React 18+ or pass all props as string attributes.

---
> Source: [getelena/elena](https://github.com/getelena/elena) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-23 -->

