Guide
Upgrading from 0.4 to 0.5
0.5 is a breaking release. Renamed attributes and events kept working through 0.5, but some changes need updates to your build, styles, markup or server code. Work through the sections that apply to your site, then follow Upgrading from 0.5 to 0.6, which removes the deprecated names.
Imports and builds
The package is ESM only. The CommonJS build (dist/index.cjs) and the require export condition are gone, so load it with import, or with await import() from CommonJS code.
import { Parallelogram } from '@parallelogram-js/core';
import Toggle from '@parallelogram-js/core/components/Toggle';
Import paths work with or without .js. Use the normal paths rather than the old @parallelogram-js/core/dev/* paths, which 0.6.0 removes. Bundlers that resolve the development export condition, as Vite does during development, pick the development build.
The package now ships TypeScript declarations and a custom-elements.json manifest, so you can delete declarations you wrote for it yourself.
Supported browsers
The package targets Baseline 2023: Chrome and Edge 120, Firefox 121, and Safari 17.2 on macOS and iOS, or later. It uses the native <dialog> element, form-associated custom elements and constructable stylesheets, and ships modern JavaScript without transpiling it. Older browsers aren't tested or supported.
Registering components
app.components.add() tells components apart differently:
- Only a valid custom element name, with a hyphen such as
p-modal, loads a web component. Every other selector, includingformorul > li, registers an enhancement component. - An enhancement component is named by its
nameoption, or otherwise by its full selector. Names used to come from the first data attribute or class in the selector, so updatedependsOnlists andpageManager.instanceslookups. Registering the same name twice throws.
app.components
.add('[data-toggle]', () => import('@parallelogram-js/core/components/Toggle'))
.add('[data-tabs]', {
loader: () => import('@parallelogram-js/core/components/Tabs'),
dependsOn: ['[data-toggle]'],
});
Components you write should declare static selector = 'data-your-component'. Without it their attribute name comes from the class name, which minifiers change, and a warning is logged.
State attributes
Components write their state to data-<component>-state instead of the attribute they are selected by. Update styles and scripts that read state:
| Before | From 0.5 |
|---|---|
[data-<component>="open"] |
[data-<component>-state="open"] |
[data-toggle-target="open"] on a target |
[data-toggle-state="open"] |
p-modal[data-modal="open"] |
p-modal[data-modal-state="open"] |
The old attributes still receive a copy of the state until 0.6.0, so existing styles keep working in the meantime. In your own components, setState() writes the new attribute and getElementState() reads it.
Dialogs
<p-modal> and Lightbox open the native <dialog> element as a modal, so they render in the top layer, the page behind them is inert, and focus returns to the element that opened them.
z-indexno longer affects<p-modal>, because nothing on the page sits above the top layer.- The
backdroppart is gone. Style the dimmed page withp-modal::part(panel)::backdrop. - The
panelpart is now the<dialog>itself. - With
data-modal-closable="false", the modal’s owndata-modal-closebuttons now close it. Escape, the backdrop and its close button still don’t. - Setting
modal.open = truebefore the element is defined now opens it.modal.open(), theopenattribute and[data-modal]triggers work as before.
Web component events
Web component events are named <tag>:<verb> and bubble out of shadow roots, so a listener on document hears them wherever the element is. The old names are dispatched straight after, with the same detail, until 0.6.0.
| Old name | New name |
|---|---|
modal:open, modal:close |
p-modal:open, p-modal:close |
toast:show, toast:close |
p-toasts:show, p-toasts:close |
upload:success, upload:error |
p-uploader:upload-success, p-uploader:upload-error |
sequence:update |
p-uploader:sequence-update |
file:update, file:delete |
p-uploader-file:update, p-uploader-file:delete |
document.addEventListener('p-modal:close', event => {
console.log(event.detail.modal.id);
});
Dates from <p-datetime>
In date mode, <p-datetime> stores, emits and submits a plain yyyy-mm-dd date. It used to submit a UTC instant, such as 2026-09-09T14:00:00.000Z for 10 September in Sydney, so update server code that parsed instants for date fields. Datetime and time modes still use ISO instants, and date mode still accepts an instant as its value.
The element is now form-associated. It submits under its name, and range-to in range mode, without adding hidden <input> elements, so remove scripts or styles that looked for those inputs. It supports required, min, max, form reset and disabled. In range mode, read the range property instead of the deprecated isRange.
Toggle
Toggles are independent. Opening one no longer closes every other toggle on the page, and only toggles with data-toggle-capture close on an outside click. The multiple option is gone: give toggles that should close each other, such as an accordion's, a shared data-toggle-group.
<button type="button" data-toggle data-toggle-target="#refunds" data-toggle-group="faq">
Can I get a refund?
</button>
Forms
FormEnhancer uses the browser's constraint validation. Declare rules with native attributes such as required, type, minlength, min, max and pattern, which also work without JavaScript, and move to the new names:
| Before | From 0.5 |
|---|---|
data-form-validator |
data-form-enhancer |
data-validate rules |
Native validation attributes |
data-validate-message |
data-form-enhancer-message |
form-validator:* events |
form-enhancer:* events, also on the form |
The old names keep working until 0.6.0.
Images
Lazysrc hands images to the browser's own loading="lazy".
srcandsrcsetalready in the markup are left alone.data-lazysrc-src,data-lazysrc-srcsetanddata-lazysrc-sizesare copied across as soon as the image mounts, and the browser decides when they load.data-lazysrc-thresholdanddata-lazysrc-root-marginonly apply todata-lazysrc-bgbackground images.data-lazysrc-use-nativeis gone.- The default classes are
lazysrc--loading,lazysrc--loadedandlazysrc--error. Setdata-lazysrc-loaded-class="loaded", and the loading and error equivalents, to keep the old names.
Uploads
<p-uploader>no longer falls back to/upload,/update,/deleteand/sequence. Setupload-action, andupdate-action,delete-actionorsequence-actionfor each feature you want.- Fields show only when
<p-uploader-fields>declares them. <p-uploader-file>edits all of a file's fields in one dialog opened by its Edit details button. The per-field edit panels and buttons are gone.window.MockXHRis gone; usesetXHR()in tests.
Removed options
Many options and properties that did nothing were removed, such as Toggle's transitionDuration, Tabs' activeClass and Scrollhide's debounce. The Removed section of the changelog lists them all.
Deprecated in 0.5
These work in 0.5 and are removed in 0.6.0:
| Deprecated | Use instead |
|---|---|
@parallelogram-js/core/dev/* imports |
The normal paths with the development export condition |
State copies in data-<component>, data-toggle-target and data-modal |
data-<component>-state, data-toggle-state and data-modal-state |
modal:open, modal:close, toast:show and toast:close |
p-modal:open, p-modal:close, p-toasts:show and p-toasts:close |
upload:success, upload:error, sequence:update, file:update and file:delete |
The p-uploader:* and p-uploader-file:* events |
data-form-validator, data-validate and data-validate-message |
data-form-enhancer, native validation attributes and data-form-enhancer-message |
form-validator:mounted, form-validator:submit-blocked and form-validator:submit-valid |
form-enhancer:mounted, form-enhancer:submit-blocked and form-enhancer:submit-valid |
CopyToClipboard's data-copy-target and data-copy-text |
data-copytoclipboard-target and data-copytoclipboard-text |
Lightbox's data-lightbox-state-closed-class, which was never applied |
The lightbox:closed event |
<p-datetime>'s isRange |
range |
BaseComponent's _getDataAttr() and _elementsKeys() |
getAttr(), getBoolAttr(), getNumberAttr() and trackedElements() |