Get started
JavaScript modules
What each optional script adds, how to load only the one you need, and what a page does without them.
Every LUZ page works without JavaScript. The scripts add behaviour on top: copying, highlighting, a cycling color mode button, players, tabs and sorting a table. The catalog adds its own: search, the cookie banner, the time of day for the color mode, a timeline and the scroll spy of the sidebar.
Load all of it, or one module
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.js"></script>
import { initTabs } from "@luz-ds/core/tabs";
initTabs();
luz.jsstarts every module when the page loads.- Importing a single module does nothing until you call its
initfunction. midi.jsandtable-sort.jsare chunks of their own:luz.jsloads each one on demand, and only on a page that needs it.theme-mode.jsis another:luz.jsloads it only on a page with a<luz-theme-mode>. See Color mode.- The site search is in
@luz-ds/catalog: see Search. So is the cookie banner, a module of its own thatcatalog.jsdoes not include: see Cookie consent. The time of day for the color mode is a third module of the same kind,theme-time.js, loaded bycatalog.jsonly on a page with a<luz-theme-time>: see Color mode by time of day. - The timeline and the sidebar's scroll spy are in
@luz-ds/catalogtoo, astimeline.jsandsidebar.js.catalog.jsloads the first on a page with a<luz-timeline>and the second on a page with a.luz-sidebar, or a list markeddata-luz-spy. Import them by name for a timeline or a sidebar added later:@luz-ds/catalog/timelineand@luz-ds/catalog/sidebar. - A page that loads only core has no scroll spy. The sidebar drawer is a native popover and works without a script.
- The timeline has a small dictionary of its own for its four buttons (
enandesonly), thendata-luz-label-*and the nearestlang. A language added withregisterLocale()does not reach it: callinitTimeline(document, t)with thetof core.
The modules
All 13 JavaScript modules of core
| Module | What it does | Key exports |
|---|---|---|
luz.js | Entry: re-exports every module and initializes them all on load | Everything below |
theme.js | Switching to a look of your own; the color mode is theme-mode.js's | applyTheme |
theme-mode.js | The <luz-theme-mode> element, in a chunk of its own, loaded on demand | None: importing it defines the element |
theme-boot.js | A classic script for the <head>: the color mode before the first paint | None |
clipboard.js | Copy with visual feedback | copyToClipboard, initCopyButtons |
modal.js | Fallback for the command and commandfor attributes in browsers without them | initModals, supportsCommands |
highlight.js | Dependency-free syntax highlight for code blocks | highlightBlock, initHighlight |
player.js | Video, audio and MIDI players with one UI | mountPlayer, mountMidiPlayer, initPlayers |
tabs.js | Tab activation and keyboard support | activateTab, initTabs |
i18n.js | Text the scripts write, in the language of the nearest lang: English in the entry, other languages fetched when a page uses them | t, getLang, registerLocale, loadLocales |
utils.js | Pure helpers | formatTime, escapeHtml, calculateProgress |
table-sort.js | Sorts a table in the browser: a heading marked data-luz-sort becomes a button. Loaded on demand, only on a page that has one | initTableSort(root, t) |
midi.js | MIDI parser and Web Audio scheduler, loaded on demand | MidiPlayer |
Languages and the strings scripts write
English is in the entry. Every other language LUZ ships is a small file, fetched at start for a page whose <html lang>, or any lang outside a link or an option, names it; a page in English makes no request.
With the individual modules, call loadLocales() yourself, and await loadLocales() before initHighlight() and initPlayers(). A file that cannot load is one console warning, and English.
For a language LUZ does not ship, set data-luz-label-* once on <html> or <body>, or call registerLocale() before the scripts start.
To check the file, put an import map with an integrity section for it before the first module script, on the pages in that language only. A modulepreload link with integrity is not a guard in Safari or Firefox: use it as a performance hint.
A language for LUZ is a pull request: the JSON file packages/core/src/i18n/<code>.json, one line in the files map of i18n.js and a budget row. The steps are in AGENTS.md.
Without JavaScript
What each behaviour falls back to when the script does not run:
| Component | Without JavaScript |
|---|---|
Links, and every panel stacked | |
The four radios still change the mode; Auto follows the system, and the choice is not remembered | |
No bar and no moments are shown; core's radios still change the mode, and no time of day is applied | |
Hidden; the text stays selectable | |
The browser's own media controls; MIDI shows its fallback text | |
Opens where the browser supports command; elsewhere the open button does nothing | |
Plain text, no colours | |
A heading with a link sorts on the server; a plain heading is not sortable. Rows are never hidden | |
An ordered list with a line and a dot per item; every item is visible | |
Works, drawer included; the current link does not follow the scroll | |
Table of contents and other data-luz-spy lists | Links to the sections; no link is marked as the one in view |