Scripting
Vanilla JavaScript architecture, web component patterns, auto-initialization, and module dependencies.
Scripts in @zazz-ui/core live alongside their components: shared runtime in src/base/ and component logic in src/primitives/<name>/. Authored in TypeScript, they compile to readable .js files that run as native ES modules in modern browsers without a required bundler.
index.js imports all component scripts so pages can load behaviors with a single <script type="module" src="./zazz/index.js"> tag.
Principles
- Vanilla JS: Shipped
.jsfiles are standard ES modules. External dependencies (signals polyfill, Embla) resolve through the page's import map. - HTML-driven: Components read configuration from data attributes (
data-carousel-*,data-reveal-*). Custom element props aredata-*attributes too (e.g.data-label-show/data-label-hideon<ui-password>), not bare attributes. - Progressive enhancement: Components fall back to native HTML behaviors if JavaScript is disabled.
File template
"use strict";
/**
* @fileoverview Module title.
* @description What this module does.
*/
import { Dependency } from "./dependency.js";
// Implementation
if (typeof window !== "undefined" && typeof document !== "undefined") {
// Auto-init
}
if (typeof window !== "undefined") {
window.MyExport = MyExport;
}
export { MyExport };Module exports
Scripts expose an API on window and provide named exports for ES module consumers:
| File | Global | Exports |
|---|---|---|
utils.js | window.Utils | { parseValue, parseDataAttributes } |
signals.js | window.Signals | { state, computed, effect } |
reveal.js | window.Reveal | Reveal class |
zazz-element.js | (none) | ZazzElement base, defineZazzElement, refresh registry |
dialog-lifecycle.js | (none) | Emits zazz:dialog-open / zazz:dialog-close on every <dialog> |
embla.js | window.EmblaInit | { init, initRoot, addDotBtnsAndClickHandlers } |
toaster.js | window.Toaster | Toaster imperative API |
navigation.js | None | Side-effect only |
Web component scripts (carousel.js, lightbox.js, password-group.js, tabs.js, toaster.js) register custom elements automatically.
Auto-initialization
if (typeof window !== "undefined" && typeof document !== "undefined") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
}Initializers are idempotent and check element state (e.g. data-carousel-init) before binding.
Dependencies
The module graph resolves loading order automatically via index.js:
| Script | Imports | Notes |
|---|---|---|
utils.js | None | Exposes window.Utils |
signals.js | signal-polyfill | Reactive state (window.Signals) |
reveal.js | None | Standalone viewport observer |
dialog-lifecycle.js | None | Owns <dialog> visibility; components subscribe |
embla.js | utils.js, Embla packages | Embla packages resolve through the import map |
carousel.js | embla.js | <ui-carousel> custom element |
lightbox.js | carousel.js | <ui-lightbox> custom element |
password-group.js | signals.js | <ui-password> custom element |
tabs.js | None | <ui-tabs> custom element |
toaster.js | signals.js | <ui-toaster> custom element |
navigation.js | zazz-element.js | Page transitions; refreshes swapped-in content |
Ensure the import map appears in the DOM before index.js.