Micromodal
Tiny, dependency-free JavaScript library for accessible, WAI-ARIA compliant modal dialogs.
Repository Health
Technical Analysis
MicroModal is a tiny, dependency-free JavaScript library focused on making modal dialogs accessible by default. Rather than shipping a full UI framework, it standardizes the behavior modals need to be usable with a keyboard and screen reader — toggling ARIA attributes, trapping and restoring focus, and closing on overlay click or Escape — while leaving all markup and styling to the consumer.
Modals are declared entirely in HTML via data-micromodal-trigger/data-micromodal-close attributes, and a single MicroModal.init() call wires up the triggers found in the DOM. Nested and layered modals are supported out of the box, and the library ships as UMD, ES module, and minified CDN builds for drop-in use with or without a build step.
What You Get
- Automatic ARIA attribute toggling (aria-hidden) on modal open and close
- A focus trap that keeps keyboard tabbing inside the currently open modal
- Focus restoration to the previously active element after the modal closes
- Built-in close-on-overlay-click and close-on-Escape behavior
- Support for nested and layered modals with independent focus trapping
- UMD, ES module, and minified CDN builds for any project setup
Common Use Cases
- Adding an accessible confirmation or alert dialog to a static or server-rendered site
- Building a lightweight signup or newsletter modal without pulling in a UI framework
- Retrofitting accessibility (ARIA plus focus trap) onto an existing custom modal
- Layering multiple modals, such as a confirmation on top of a form modal, on one page
- Progressively enhancing plain HTML modals via a CDN script tag with no build step
Under The Hood
Architecture
MicroModal ships as a single-file IIFE module (src/index.js) exposing one public object (init, initModal, config, show, close, closeAll, removeModal) backed by an internal Modal class. An allModals registry maps target ids to Modal instances, and an activeModals array tracks the open-modal stack so nested modals get correct focus trapping and a single shared keydown handler defers to whichever modal is topmost. The design is flat and cohesive rather than layered — appropriate for its scope — but every public method funnels through the same Modal class, so a change to its constructor or lifecycle methods would ripple through the whole public API.
Tech Stack
The library itself has zero runtime dependencies (no dependencies entry in package.json). Builds run through Rollup (rollup.config.js) with @rollup/plugin-babel, @rollup/plugin-eslint, @rollup/plugin-json, and rollup-plugin-terser, transpiled via Babel (@babel/preset-env) against a .browserslistrc target, and output as UMD (dist/micromodal.js), ESM (dist/micromodal.es.js), and a minified CDN bundle (dist/micromodal.min.js) per the main/module/cdn fields in package.json. Local development runs Rollup in watch mode alongside a static file server for the docs/ demo site.
Code Quality
Testing is done end-to-end with Cypress (tests/specs/*.spec.js), covering basic open/close, layered/nested modals, programmatic show/close, and animation-await behavior — real interaction coverage rather than unit tests of internals. There is no static typing (plain ES6+, no TypeScript or JSDoc type annotations). Error handling favors defensive guards (null and class-list checks, an opt-in debug mode with console warnings) over thrown exceptions. Naming is clear and consistent, ESLint enforces the standard style guide, and a GitHub Actions workflow builds and runs the Cypress suite on every push and pull request.
API Design
The public API favors declarative HTML over imperative wiring: consumers add data-micromodal-trigger/data-micromodal-close attributes and call a single MicroModal.init(), with no manual DOM references or component tree required for the common case. Accessibility guarantees — focus trap, ARIA state, focus restoration — apply automatically without any consumer code, and show/close/config provide an escape hatch for programmatic control when needed. Getting started requires minimal boilerplate: markup, CSS, one script tag, and one init call.