Foundations

Core and lifecycle

The root scope, the runtime API and the init and destroy lifecycle shared by every component.

Core is the shared base of every bundle: tokens, foundation and utilities in CSS, and version, init, destroy and observe in JavaScript. Asset loading lists the Full, Core and Modular files.

Root

Components sit inside a separate [data-plf-root] element in both CSS modes. Root typography and tokens apply only inside a root; elements outside it are not reset.

Rule Behavior
Nested roots Keep their own complete defaults and theme
Component scope A component uses its nearest root and never borrows controls or targets from a nested root
One document Loads one CSS mode and one runtime format and version
No implicit roots No mutation observer runs unless you call observe, and no root is created automatically
Site content An element with data-plf-site holds the site’s own content inside a root, such as a page’s content in a dashboard layout

Preflight and the WordPress integration leave site content to the site’s styles, so a page built with Elementor or the block editor looks as it does elsewhere on the site. The site’s styles for its elements reach the components inside it too, such as Elementor’s for buttons; in a root inside it, they are Pelatform UI’s again. It takes the root’s font and text color; content that sets its own, such as Elementor’s widgets, keeps it. On a root of its own, such as <div data-plf-root data-plf-theme="light" data-plf-site>, site content keeps that root’s theme, so a page built for a light background stays light in a dark layout.

HTML
<div data-plf-root>
  <aside class="sidebar">…</aside>
  <main data-plf-site><!-- The page's own content --></main>
</div>
Format API Starts itself
Full Browser window.PLF with every standard component Once, at DOM readiness; an extension script loaded after it starts its own elements
Core Browser window.PLF with version, init, destroy and observe; component scripts register against it without a second runtime No
ESM @pelatform/ui/core exports version, init, destroy, use and observe; the root and component subpaths share one graph and registry No

Loading the same version again reuses the runtime; a different version is reported without replacing the active one.

ESM

Importing a component does not register or initialize it, read the DOM, or attach globals or listeners. DOM methods called on a server throw a clear error.

Call Does
Component.getOrCreateInstance(element) Registers the component’s required dependencies and creates the instance
Component.getInstance(element) Returns the existing instance or null
use(Modal, DataTable) Registers components and extensions, with the components they need, so init and observe start them, also in later calls
init() from @pelatform/ui Registers the standard components, then scans
init() from @pelatform/ui/core Scans only for the components registered by use or their own methods
TypeScript
import {init, use} from '@pelatform/ui/core';
import {Modal} from '@pelatform/ui/components/modal';
import {DataTable} from '@pelatform/ui/extensions/datatable';
use(Modal, DataTable);
init();

Lifecycle

init(scope = document) and destroy(scope = document) accept a Document or an Element and include the scope itself; Window, DocumentFragment and null throw TypeError. Call init after inserting content and destroy before replacing it.

Method Behavior
init Safe to repeat: listeners are not duplicated and native values and options are not reset
destroy Cleans descendants before ancestors, removes owned listeners, timers and generated DOM, and restores attributes and styles only while they still match the instance’s last write, so later consumer changes survive
observe(scope) Optional and safe to repeat: initializes components added inside the scope and destroys removed ones, until you call the function it returns

Pass a detached element to destroy directly; a document scan cannot find removed nodes. A component that fails to start releases its partial resources and reports the error without stopping the others. Each document or iframe starts its own runtime; targets never cross documents.

Dynamic content

Content added after the page loads starts with PLF.init(container), and PLF.destroy(container) cleans up content before you remove it. To have both done for you, observe the document once:

HTML
<script>
  document.addEventListener('DOMContentLoaded', () => PLF.observe(document));
</script>

Every component added later then starts and every one removed is destroyed, until you call the function observe returns; a moved element keeps its instance. This covers content from fetch and from libraries that swap HTML, such as htmx, Turbo and Astro’s client router. An update that changes elements in place instead, such as a morph, can remove the parts a component adds: destroy that content before the update and initialize it after. With ESM imports, call observe(document) after init().

Hooks and events

  • Behavior reads data-plf-* hooks and native or ARIA state, never visual classes, so both CSS modes share one runtime.
  • Generated elements carry a data-plf-part role.
  • Events bubble.
  • Before events can be canceled; a canceled one still bubbles.
  • An accepted change dispatches one completed event; init, destroy and no-op calls dispatch none.
  • State methods return true when the change completes and false when it is canceled, blocked, invalid or already in that state.
  • A call made while the same instance is still changing is blocked.
  • Core keeps no state between visits and makes no network requests: host controls, transport, routing and persistence belong to your application.

Troubleshooting

A problem logs one console line, [PLF] Name #id: message, followed by the element: click it to find the element on the page. The message names what to add or change.

Message Fix
must sit inside a separate element with data-plf-root Wrap the component in an element with data-plf-root; the root attribute cannot sit on the component itself
getOrCreateInstance needs an element, not null The selector found nothing: check the id, or call it once the markup exists
needs the data-plf-modal attribute The element passed to getOrCreateInstance is not that component’s root
A message about a name, such as the menu needs a name Give that element an accessible name: aria-label, or aria-labelledby pointing at visible text
Modal script needs Core 0.1.0 (found none) Load core/pelatform-ui-core.js before the component scripts
found 0.0.9, or did not start: Core 0.0.9 is already active Load every file from one release
needs the Modal script: load components/modal.js Load the named script as well

A component that does nothing and logs nothing either lacks its script (Sheet, Drawer, Alert Dialog and Result Dialog use the Modal script) or was inserted after init ran: see Dynamic content.


Built with Pelatform UIv0.1.0

Documentation

Getting started

Foundations

Components

Actions

Data display

Navigation

Feedback

Data input

Layout

Advanced

Integrations

Blocks

Application

Authentication

Icons