The Elena adapter uses Elena for light DOM rendering. Components render directly into the document — no Shadow DOM wrapper, no Declarative Shadow DOM, no hydration step. CSS encapsulation uses the @scope CSS at-rule instead.
pnpm create @beatzball/litro my-app --adapter elena
The client entry (app.ts) is the simplest of all three adapters — no hydration script needed:
// app.ts
import '@beatzball/litro/adapter/elena/runtime';
import { routes } from './routes.generated.js';
const outlet = document.querySelector('litro-outlet');
if (outlet) outlet.routes = routes;
Pages extend LitroPage from the Elena adapter path. Use Elena's html template tag and static props for reactive properties.
// pages/index.ts
import { html } from '@elenajs/core';
import { LitroPage } from '@beatzball/litro/adapter/elena/page';
import { definePageData } from '@beatzball/litro/runtime/page-data.js';
export const pageData = definePageData(async (event) => {
return { message: 'Hello from the server (Elena)!' };
});
export class HomePage extends LitroPage {
static override tagName = 'page-home';
override async fetchData() {
const res = await fetch('/api/hello');
return res.json();
}
render() {
const data = this.serverData as { message: string } | null;
return html`
<h1>Welcome to Litro (Elena)</h1>
<p>${data?.message ?? 'Loading...'}</p>
`;
}
}
HomePage.define();
export default HomePage;
@scope CSS — style encapsulation uses the CSS @scope at-rule instead of Shadow DOMstatic tagName — component tag is declared as a static property, not via a decorator.define() — call ComponentClass.define() to register (no @customElement decorator)Elena SSR works fundamentally differently from Lit and FAST:
@lit-labs/ssr or @microsoft/fast-ssr — Litro's Elena adapter renders components directly: instantiate, call render(), stringify the TemplateResult<template shadowrootmode="open"> wrappersThe SSR output for a page looks like:
<page-home hydrated>
<h1>Welcome to Litro (Elena)</h1>
<p>Hello from the server (Elena)!</p>
</page-home>
Compare with Lit/FAST DSD output:
<page-home>
<template shadowrootmode="open">
<style>:host { display: block; }</style>
<h1>Welcome to Litro</h1>
<p>Hello from the server!</p>
</template>
</page-home>
@scopeSince Elena renders into the light DOM, styles are not automatically scoped by the browser. Use the CSS @scope at-rule for encapsulation:
@scope (page-home) {
:scope {
display: block;
padding: 2rem;
}
h1 { color: #1a1a2e; }
p { line-height: 1.6; }
}
@scope is supported in Chrome 118+, Edge 118+, and Safari 17.4+. For older browsers, the styles apply globally (graceful degradation).
The Elena adapter has minimal configuration:
HTMLElement shim and customElements registry for Node.js SSRmyProp in HTML becomes myprop after parsing. Use myprop in the props declaration.html tag escapes interpolations — use unsafeHTML() from @elenajs/core when you need to render raw HTML (e.g. Markdown content).innerHTML — components that capture innerHTML in connectedCallback must fall back to this.innerHTML in render() for SSR compatibility.@scope browser support — older browsers see unscoped styles (functional but not encapsulated).