Usage
Setup #
One deferred script. It starts by itself once the page is parsed: the markup is enough.
<script src="js/iroom.min.js" defer></script>
To pass options, keep it from starting with data-manual and start it yourself:
<script src="js/iroom.min.js" data-manual defer></script>
<script type="module">
Iroom.start({ messages: { close: 'Fermer' } });
</script>
With a bundler, import the ES module: it starts nothing and touches no DOM on import, so it also loads on a server.
import { start } from 'iroom';
start();
Usage
Lifecycle #
One listener per event type, on the document, for every component: the elements added later work too.
Iroom.start(options?)adds the listeners, sets up the page and watches the markup inserted later (a MutationObserver). A second call does nothing.
Iroom.stop()removes every listener (one AbortController), the observers and the elements created by the components
Iroom.init(root?)sets up a part of the page by hand, when observe is off; safe to call twice
Iroom.isStarted(), Iroom.versionthe state and the version of the framework
Usage
Options #
Option Default Use
observetruesets up the markup inserted after the start
scrolledFar600pixels of scroll before data-scrolled="far" (the back to top button)
themeKey'iroom-theme'the localStorage key of the saved theme
messagesEnglish the texts for the screen readers: imageViewer, close, previousImage, nextImage
The large breakpoint of the navbar is not an option: it is read from the --ir-breakpoint-l token of the CSS, so the two never disagree.
Usage
Events #
Every change is announced by an event that bubbles. The cancelable ones can stop the change with preventDefault().
Event On Detail
ir:dropdown-open cancelable , ir:dropdown-close.dropdowntoggle, menu
ir:navbar-toggle cancelable the toggle toggle, menu, open
ir:tab-change cancelable .tabstab, panel, index
ir:lightbox-open cancelable , ir:lightbox-change, ir:lightbox-closethe link, the dialog link, index
ir:filter-change cancelable [data-filter-group]value
ir:dismiss cancelable the alert
ir:password-togglethe field input, visible
ir:theme-change<html>theme, dark
document.addEventListener('ir:tab-change', (event) => {
if (formIsDirty) {
event.preventDefault(); // the tab stays
}
});
Usage
Functions #
To drive the components from your code.
openDropdown(toggle, focus?), closeDropdown(restoreFocus?)focus: 'first' or 'last' item
toggleNavbar(toggle, open?), closeNavbars()the menus of the navbars
selectTab(tab, focus?)a tab and its panel
openLightbox(link)a [data-lightbox] link and its gallery
applyFilter(group, value)the items of a [data-filter-group]
dismiss(element)removes an element after a fade
setTheme(theme, persist?), getTheme(), isDark()'light', 'dark' or 'auto'
Components
Overview #
Twelve components. Their markup is on the components and CSS pages.
.dropdown-togglearia-expanded; a click outside, Esc , Tab or a choice closes; arrows, Home , End
.navbar-togglethe menu named by aria-controls; Esc , a link, a click outside or a large screen closes it
.tabsroles and ARIA attributes, arrows, Home , End
commandfor, closedbybrought to the browsers that lack them
[data-lightbox]a modal dialog, a gallery, arrows, swipe
[data-filter]filters with view transitions
[data-dismiss]removes its alert after a fade
[data-password-toggle]shows or hides a password
[data-theme-toggle]light or dark, saved
data-scrolled, [data-scrollspy]the scroll state, the current section
.parallaxthe progress of the visible sections, turned into a translate by the CSS
Components
Lightbox #
A link to the large image: without JavaScript it opens the image, with it a modal dialog. The same data-lightbox makes a gallery.
← and → navigate, Esc or a click on the background closes; a click with Ctrl still opens the image in a new tab.
Components
Filters #
Buttons with data-filter, items with data-category (several are allowed), in a data-filter-group.
All
Web
Print
Video
web
print
video
web, print
video
print
Components
Theme #
A data-theme-toggle button switches between light and dark, saves the choice and sets aria-pressed; .toggle-on and .toggle-off swap its content.
Dark theme
Light theme
Components
Scroll and scrollspy #
<html> gets data-scrolled="near" once the page scrolls, "far" after 600 pixels: the sticky navbar gets a shadow, the transparent one becomes solid, .back-to-top shows up.
In a data-scrollspy element, the link of the section crossing the reading line gets aria-current="true": the menu of this page. An IntersectionObserver does the work, nothing is measured on scroll.
<nav data-scrollspy>
<a href="#setup">Setup</a>
<a href="#events">Events</a>
</nav>
<a href="#top" class="back-to-top" aria-label="Back to top">…</a>
Going further
Your own component #
The components of the framework are built the same way: register yours, it gets the same lifecycle.
iroom.devinthehood.comgister({
name: 'copy',
// once per start: the signal removes the listener on stop()
listen({ signal }) {
document.addEventListener('click', (event) => {
const button = event.target.closest('[data-copy]');
if (button) navigator.clipboard.writeText(button.dataset.copy);
}, { signal });
},
// for the page, then for every inserted part: must be safe to call twice
setup(root) {
root.querySelectorAll('[data-copy]').forEach((button) => button.setAttribute('title', 'Copy'));
},
});
Going further
Build, types and tests #
Strict TypeScript in ts/, one file per component, bundled by esbuild.
js/iroom.jsthe script, readable, for the debugging
js/iroom.min.js and its source mapminified, 6 kB gzipped
js/iroom.esm.js and its source mapthe ES module, without side effect
js/types/the declarations, the events typed for addEventListener
npm run build:js # type checking, then the files above
npm run check # Biome (lint, format) and Vitest (the tests of every component)