OverviewGetting startedTokens onlyComponents in any appAuthor custom elementsA full Oddkin applicationDocument your library with WorkbenchInstallationComponentsFoundationsCore and stateTheming and layoutAgent toolingAI toolingChange impactRecipesArchitectureRoadmapHelp and releasesGetting startedInstallationCore and stateGuidesRecipes

Declare the element

OddkinElement.with() declares public properties with their attribute conversion, defaults, and reflection; the metadata and the TypeScript types come from the same declaration.

demo-greeting.ts
import { OddkinElement, prop } from '@oddkin/core';

export class DemoGreeting extends OddkinElement.with({
  name: prop.string({ default: 'world', description: 'Who to greet.' }),
  excited: prop.boolean({ reflect: true, description: 'Adds emphasis.' }),
}) {}

Template, styles, and registration

A definition joins the class, its template, and its styles; define() registers it. Keep registration in its own module so importing the element registers nothing.

demo-greeting.define.ts
import { compose, css, define, html } from '@oddkin/core';
import { DemoGreeting } from './demo-greeting.js';

export const demoGreetingDefinition = compose({
  name: 'demo-greeting',
  element: DemoGreeting,
  template: (el: DemoGreeting) => html`<p>Hello, ${el.name}${el.excited ? '!' : '.'}</p>`,
  styles: css`
    :host {
      display: block;
    }
  `,
});

define(demoGreetingDefinition);

Reactivity and ownership

Reading a property or a signal’s .value while rendering tracks it, so the element renders again when it changes. Work started in connected(scope) belongs to that scope and stops when the element disconnects. Core concepts explains both.