Builtin conventions¶
Every pyjinhx builtin follows the same contract, so knowing one means knowing all of them.
The contract¶
idis optional. Omit it and pyjinhx generatespjx-<n>. Pass one when you need a stable hook (CSS, htmx targets) — and always for reactive components, whose OOB targeting requires stable identity across renders.class_nameappends your classes to the root element:PJXBadge(label="New", class_name="pill").extra_attrsrenders extra attributes (validated — values may not contain") on the root element — the carrier forhx-*,data-*,aria-*, or Alpine directives:PJXCard(body=..., extra_attrs={"hx-get": "/refresh", "hx-trigger": "every 30s"}).- All copy is props. Every user-visible string, aria-labels included, has an English default
you can replace:
PJXModal(title="Excluir?", close_label="Fechar"). - JS is headless. Builtin JavaScript never writes inline styles for state — visibility and variants are classes/attributes; computed positioning coordinates (tooltip/popover placement) are the one sanctioned inline-style use. Communication is through
pjx:*DOM events anddata-pjx-*attributes; programmatic APIs live under the singlewindow.pjxnamespace. Async-state JS follows the runtime's concurrency pattern: a ref-count per scope, release keyed to each request'sloadend(terminal on load, error, abort, and timeout), and state re-applied afterhtmx:afterSettlefor nodes a swap replaced mid-flight. - The DOM contract is API. Each builtin's documentation ends with a "DOM contract" block —
stable classes,
data-pjx-*attributes, events, state attributes. We version those like code.
Events and hooks¶
Interactive builtins fire a two-tier event vocabulary on their root element (all bubble):
pjx:<component>:before-<verb>— cancelable.event.preventDefault()aborts the action.detail = {reason, trigger}withreason ∈ {"escape","backdrop","api","trigger"}.pjx:<component>:<verb>— fired after the DOM change. Not cancelable.
Shared vocabulary: pjx:before-reveal / pjx:reveal fire on any [data-pjx-region] (PJXPanel panels,
PJXTabGroup bodies) when it is shown — PJXLazyPanel(when="reveal") builds on it; pjx:toast is the
input event PJXToastHost listens for (htmx fires it from HX-Trigger response headers).
document.getElementById("confirm-del").addEventListener("pjx:modal:before-close", (e) => {
if (hasUnsavedChanges()) e.preventDefault();
});
Declarative attributes¶
| attribute | effect |
|---|---|
data-pjx-open="<id>" |
click opens that PJXModal / PJXDrawer |
data-pjx-close |
click closes the nearest enclosing dismissible |
data-pjx-toggle="<id>" |
click toggles that PJXPopover / PJXDropdown menu |
data-pjx-confirm-danger |
hx-confirm on this element gets a danger-styled OK button |
data-pjx-confirm-ok, data-pjx-confirm-cancel |
per-trigger confirm-dialog label overrides |
data-pjx-loader |
requests from this subtree show the PJXPageLoader |
data-pjx-region |
marks a show/hide region (emits pjx:reveal) |
data-pjx-autoshow |
PJXNotification auto-shows when this attribute is present on mount |
The window.pjx namespace¶
pjx.modal · pjx.drawer · pjx.popover · pjx.notification · pjx.loader.region (region busy-state) ·
pjx.loader.page (page navigation) · pjx.confirm · pjx.prompt · pjx.toast. Open/close/show/hide
functions return false when a before-* hook canceled the action.
Theming¶
Builtins read --pjx-* tokens that default to your app-level semantic tokens. Re-skin globally in
one block:
or per-context: .settings-pane { --pjx-card-bg: var(--surface-alt); }.