npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

senangwebs-modals

v1.2.0

Published

Lightweight JavaScript library that enables easy creation and customization of modal dialogs using HTML data attributes.

Readme

SenangWebs Modals (SWM)

A lightweight, dependency-free modal library for plain HTML and JavaScript. Configure dialogs with data attributes or create them through the JavaScript API.

License: MIT

Version 1.2.0 includes the production-readiness changes documented here.

Installation

npm install senangwebs-modals

Import JavaScript and CSS explicitly in a bundler:

import SWM from 'senangwebs-modals';
import 'senangwebs-modals/css';

SWM.createModal({ title: 'Welcome', content: '<p>Trusted application markup.</p>' });

Named methods and TypeScript declarations are included in version 1.2.0:

import { createModal, openModal, init, destroy } from 'senangwebs-modals';

CommonJS remains supported:

const SWM = require('senangwebs-modals');

For plain HTML, load matching, pinned CSS and JavaScript versions:

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/swm.css">
<script src="https://unpkg.com/[email protected]/dist/swm.js" defer></script>

The UMD bundle exposes window.SWM. Existing dist/swm.js and dist/swm.css import paths remain supported. The native module is dist/swm.mjs; it requires a separate stylesheet link when used directly in a browser.

HTML definitions

<div data-swm id="myModal" data-swm-title="Default title">
  <button type="button" data-swm-btn data-swm-title="Custom Modal"
    data-swm-position="center" data-swm-bg-color="#3498db"
    data-swm-bg-opacity="0.7" data-swm-bg-blur="3"
    data-swm-z-index="9999" data-swm-footer="Custom Footer">
    Open Modal
  </button>
  <div data-swm-body>
    <p>This is trusted modal content.</p>
  </div>
</div>

Configuration may live on the container or trigger. A present trigger attribute overrides the container value, including an empty attribute. Nested definitions use their own triggers and bodies.

const handle = SWM.openModal('#myModal');
handle?.closeModal();

openModal() reads the definition directly and returns a handle. It does not dispatch a click or run other trigger listeners. Invalid selectors, missing definitions, and missing bodies log an error and return undefined.

A trigger button is optional:

<div data-swm id="autoModal" data-swm-title="Auto Modal">
  <div data-swm-body><p>Open this definition through SWM.openModal().</p></div>
</div>

JavaScript API

const { modalOverlay, modal, closeModal } = SWM.createModal({
  title: 'Programmatic Modal',
  content: '<p>Trusted application markup.</p>',
  footer: '<button type="button">Continue</button>',
  position: 'top right',
  bgColor: '#3498db',
  bgOpacity: 0.7,
  bgBlur: 3,
  zIndex: 9999
});

closeModal();

createModal() accepts omitted options. Both opening methods return { modalOverlay, modal, closeModal }. closeModal() is idempotent; removal finishes after the close animation, with cancellation and timeout fallbacks. If there is no animation, including reduced-motion mode, removal is synchronous.

Modals also close through the close button, overlay click, or Escape. Escape and overlay dismissal affect the topmost dialog. New dialogs stack above existing dialogs, even when a lower z-index is requested; at the browser's maximum z-index, later DOM order determines stacking.

| JavaScript property | HTML attribute | Default / behavior | |---|---|---| | title | data-swm-title | Empty; always rendered as text | | content | [data-swm-body] contents | Empty; trusted HTML by default | | footer | data-swm-footer | Empty; trusted HTML by default | | position | data-swm-position | center | | bgColor | data-swm-bg-color | #000000; three/six digit hex | | bgOpacity | data-swm-bg-opacity | 0.5; clamped to 0–1 | | bgBlur | data-swm-bg-blur | 0; nonnegative pixels | | zIndex | data-swm-z-index | 1000; bounded integer | | contentMode | data-swm-content-mode | html or text; default html | | Declarative only | data-swm-content-strategy | copy or move; default copy |

Numeric options accept numbers or numeric strings. Empty, non-finite, malformed, and unsupported values fall back to defaults; negative blur falls back to zero. Positions are center, top, bottom, left, right, top left, top right, bottom left, and bottom right.

HTML safety

content and footer are trusted HTML by default. Never pass user input, fetched content, or other untrusted strings directly in HTML mode. Sanitize them in your application with an appropriate HTML sanitizer, or render them literally:

SWM.createModal({
  title: 'User message',
  content: userProvidedText,
  footer: '<b>This will also remain literal text.</b>',
  contentMode: 'text'
});

data-swm-content-mode="text" copies the source body's textContent and renders footer strings literally. Text mode takes precedence over the move strategy. SWM does not include a sanitizer. Event-handler attributes and other active markup remain active in trusted HTML mode.

Preserve live content

Copy mode keeps the historical behavior: it reconstructs source markup. It does not preserve event listeners, live input values, or framework component identity, and IDs are duplicated while the source and copy coexist. Use ID-free source markup or choose move mode when those properties matter.

<div data-swm id="editor" data-swm-title="Edit profile"
  data-swm-content-strategy="move">
  <div data-swm-body>
    <label for="profileName">Name</label>
    <input id="profileName" value="Initial value">
  </div>
</div>

Move mode transfers live child nodes into the dialog, preserving identity, listeners, input state, and IDs. Close or destruction returns the dialog body's remaining nodes to the original source. Nodes added to the dialog body also return; deliberately removed nodes are not recreated. Opening the same moved definition again returns its existing handle, including while it is closing. Reopen after cleanup completes.

Moving DOM nodes can conflict with a framework's renderer. Framework-owned content should be managed through that framework's own lifecycle; copying also does not mount components automatically.

Keep associated forms inside the moved content, or use explicit HTML form attributes. Moving fields out of an external form changes their native form association.

Initialization and cleanup

Browser imports initialize delegated triggers automatically. SWM.init() is idempotent. SWM.destroy() synchronously closes all owned dialogs, restores moved content and page state, and removes document listeners. Call init() to re-enable declarative triggers. A later programmatic opening also initializes the library again.

Use one loaded SWM module/bundle per document. Lifecycle ownership is per module; avoid loading the UMD and ESM distributions together.

SSR

Importing or requiring SWM without a DOM is safe; init() and destroy() are harmless server-side. createModal() and openModal() require a browser document with a body and throw a clear error otherwise. Call rendering methods after client mount. SWM renders client-side dialogs rather than server-side HTML.

Accessibility and layout

  • Dialogs have an accessible name, role="dialog", and aria-modal="true".
  • Initial focus enters the close button. Tab and Shift+Tab stay in the active dialog; disabled/hidden controls, radio groups, and positive tabindex order are handled.
  • Background content and lower dialogs become inert, including newly added body children. Background scrolling is locked until the last dialog is removed.
  • Closing returns focus to a connected, usable invoker, the remaining dialog, or a page fallback. Closing a lower dialog does not steal focus from the upper one.
  • Closing animations retain containment until cleanup, and reduced-motion preferences disable animations.
  • Layout is bounded by the viewport, supports scrolling long bodies, and does not require a CSS reset or Tailwind.

Give every meaningful dialog a descriptive title and label your own controls. Avoid positive tabindex values. Application content still needs accessible markup; automated checks cannot establish complete accessibility.

See examples/index.html for the existing styled demo and examples/standalone.html for a dependency-free example using text and moved content.

Browser support

Target: the latest two stable versions of Chrome, Edge, Firefox, and Safari, including mobile Chrome and Safari. Native inert support is required; older browsers and Internet Explorer are not supported. Backdrop blur is decorative and falls back to the overlay color where unavailable.

Development and release checks

Use Node.js 24 for development:

npm ci
npm run test:install
npm run check
  • npm test: unit and lifecycle regression checks.
  • npm run test:browser: Playwright browser checks with automated accessibility analysis.
  • npm run test:package: tarball, SSR, exports, TypeScript, and CSS bundler checks.
  • npm run build: regenerate UMD, ESM, CSS, declarations, and source maps.
  • npm run dev: watch both JavaScript distributions and CSS.

License and support

MIT — see LICENSE.md. Report issues at the GitHub repository.