Menu

D14. One component model, headless behaviours (prototype)

Every block type is a component — props schema, template, stylesheet — and the native catalogue is the set of built-in components; screen layouts are screen-level components. Agents create components and fork any template-rendered one, built-ins included; authors edit instances (props form, slots, styles, duplicate, detach). Decided after a review of ten questions (5 Oct 2026):

  • No JavaScript in components. The page holds the SCORM API and the learner’s data. Behaviour comes from the player: activities (quiz, numeric, decision, results) are headless — the player owns scoring, saved state, SCORM interactions, focus and announcements; a fork provides the markup through hooks (data-t-…) checked by the validator and by axe. Generic primitives (click events, disclosure, tabs, labels) cover the rest; code that needs more is a free object.
  • Rendered at build time. Templates are parsed with parse5 (MIT, Node only, not shipped in packages) and rendered by the compiler: the player stays small, the output is deterministic, and the validator analyses exactly what will run.
  • Fallback to native blocks, always. Declared or generated from the props, checked for dropped text. It is what makes components safe for exports, translation, editors that do not know them, and detach.
  • Forks remember their parent; a fork of an activity keeps its learner-state key. Components are not scored by themselves (activities sit in their slots).
  • Pinned copies from libraries, upgraded explicitly; local edits of a library copy are reported. Breaking changes migrate: migrations.mjs steps run on every instance, all or nothing, and elabora.lock.json records the version instances were written for. Revisit if: agents need a behaviour the player lacks (add it as a new headless behaviour rather than opening templates to code), or build-time rendering blocks a feature that needs props changed by events (then render the same templates in the player).