Elena Progressive Web Components

website·Indexed 32 days ago

https://elenajs.com

A library for building Progressive Web Components with support for server-side rendering via @elenajs/ssr, a command-line interface via @elenajs/cli, and TypeScript integration. Features include a tagged template literal for HTML, scoped styling, and a comprehensive lifecycle for component rendering and attribute synchronization.

Tokens
21.6K
Snippets
145
Records
176
Agent score
66%

What's inside Elena

  1. Overview of @elenajs/mcp server

    @elenajs/mcp is an experimental Model Context Protocol (MCP) server that exposes Elena component metadata, scaffolding tools, and authoring guidance to AI assistants. It utilizes the Custom Elements Manifest (CEM) generated by elena build to provide resources, tools, and prompts for web component development.
  2. Overview of Elena and Progressive Web Components

    Elena is a lightweight library designed for building Progressive Web Components. A Progressive Web Component consists of two layers: a base layer of HTML and CSS that renders immediately without JavaScript, and an enhancement layer of JavaScript that adds reactivity and event handling. This approach allows developers to render HTML and CSS before JavaScript loads, reducing layout shifts and improving accessibility.
  3. Understand Elena Server-Side Rendering (SSR) basics

    Elena components are primarily HTML and CSS, making them naturally SSR-compatible. Components without a render() method are fully compatible by default. Components with a render() method provide partial support: the initial state renders without JavaScript, but interactivity requires client-side hydration. For stronger isolation with SSR, Elena supports Declarative Shadow DOM.
  4. Understand Elena's DOM update strategies

    Elena uses two strategies to update the DOM after calling render() to ensure efficiency and preserve state:

    • Patch: Used when the template shape remains the same. Elena only updates changed text nodes and attribute values, preserving element identity, focus state, and scroll position.
    • Morph: Used when the template shape changes. Elena rebuilds the affected DOM portion and morphs it into place, updating attributes and text in existing nodes where possible.
  5. Understand Progressive Web Component design patterns in Elena

    A Progressive Web Component is a native Custom Element designed with two layers: a base layer of HTML and CSS for immediate rendering without JavaScript, and an enhancement layer of JavaScript for reactivity and event handling. Elena supports three primary architectural approaches for building these components:

    1. Composite Components: Wrap and enhance HTML composed inside them. All HTML and CSS reside in the Light DOM.
    2. Primitive Components: Self-contained components that render their own HTML. The base HTML for the initial state and all CSS reside in the Light DOM.
    3. Declarative Components: A hybrid approach that utilizes Declarative Shadow DOM.
  6. Understand the Elena component lifecycle

    Elena's component lifecycle consists of two main parts: the standard custom element lifecycle and a reactive update cycle triggered by property changes. Updates are batched asynchronously; if multiple properties change before an update begins, all changes are captured in a single update cycle.
  7. Handle batched updates in Elena

    Elena does not render synchronously upon property changes. Instead, it queues a microtask. Multiple property changes occurring before the microtask runs are batched into a single render to prevent flickering and improve performance.
    // These three changes produce one update, not three
    button.variant = "primary";
    button.disabled = true;
    button.text = "Save";
  8. Implement Declarative Shadow DOM to avoid layout shifts

    Declarative Shadow DOM allows you to define a shadow root directly in HTML using a <template shadowrootmode="open"> element. This ensures the component's shadow content is visible before JavaScript loads. If an Elena component with static shadow finds an existing shadow root upon connection, it will skip attachShadow() and use the existing one. Content inside the component tag remains in the light DOM and is projected via <slot>.
    <elena-button>
      <template shadowrootmode="open">
        <link rel="stylesheet" href="button.css" />
        <button><slot></slot></button>
      </template>
      Click me
    </elena-button>
    import { Elena } from "@elenajs/core";
    
    export default class Button extends Elena(HTMLElement) {
      static tagName = "elena-button";
      static shadow = "open";
    }
    
    Button.define();
  9. Install Elena Agent Skills

    Agent skills provide coding agents with automatic context on Elena authoring patterns without requiring the full MCP server. Use the following command to browse and install available skills.
    npx skills add https://elenajs.com
  10. Define custom and internal methods in Elena components

    You can define custom methods on any Elena component. To mark a method as an implementation detail that should not be part of the public API, use the @internal JSDoc tag.
    export default class Button extends Elena(HTMLElement) {
      focus() {
        this.element?.focus();
      }
    
      /** @internal */
      _buildLabel() {
        return `${this.firstName} ${this.lastName}`;
      }
    }
  11. Generate documentation and types using Elena build

    Elena components use JSDoc annotations in the source code to automate documentation. Running elena build triggers the @elenajs/bundler, which performs a Rollup build and a CEM analysis to produce three primary outputs: a custom-elements.json manifest, individual .d.ts declaration files for each component, and a global custom-elements.d.ts for JSX integration types.
    elena build
  12. Import Elena components dynamically to ensure Node.js shims

    When using Elena components in a server-side environment, use await import() for component modules instead of static import statements. Because Elena components extend HTMLElement, they require a Node.js shim provided by @elenajs/ssr. Dynamic imports ensure this shim is loaded before the components are initialized, preventing issues caused by import sorters that might reorder static imports.