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.
<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. with every standard component |
Once, at DOM readiness; an extension script loaded after it starts its own elements |
| Core Browser | window. 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. |
Registers the component’s required dependencies and creates the instance |
Component. |
Returns the existing instance or null |
use |
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 |
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 |
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:
<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-partrole. - Events bubble.
- Before events can be canceled; a canceled one still bubbles.
- An accepted change dispatches one completed event;
init,destroyand no-op calls dispatch none. - State methods return
truewhen the change completes andfalsewhen 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. |
Load core/pelatform-ui-core. before the component scripts |
found 0., or did not start: |
Load every file from one release |
needs the Modal script: |
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.