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

@substrate-system/dialog

v0.0.21

Published

Modal dialog window

Readme

dialog

tests types module semantic versioning Common Changelog install size GZip size license

Modal/dialog window.

See smashingmagazine.com article and nathansmith/cta-modal.

Demo

See substrate-system.github.io/modal.

Install

npm i -S @substrate-system/dialog

Use

FOUCE

[!NOTE] You should prevent the flash of undefined custom elements, because the modal content should be hidden from the user until it is opened. See abeautifulsite.net.

This is how I do it in the example page:

import { ModalWindow } from '@substrate-system/dialog'

(async () => {
    await Promise.race([
        // Load all custom elements
        Promise.allSettled([
            customElements.whenDefined(ModalWindow.TAG),  // modal-window
        ]),
        // Resolve after two seconds
        new Promise(resolve => setTimeout(resolve, 2000))
    ])

    // Remove the class, showing the page content
    document.body.classList.remove('reduce-fouce')
})()

And the HTML has a class reduce-fouce:

<body class="reduce-fouce">

The CSS:

body {
    &.reduce-fouce {
        opacity: 0;
    }
}

CSS

Import

If you are using a bundler:

import '@substrate-system/dialog/css'

Or via CSS imports, for example with lightningcss:

@import url("@substrate-system/dialog/css");

CSS Variables

Customize the CSS with some variables.

:root {
    /* Overlay */
    --modal-overlay-z-index: 100000;
    --modal-overlay-background-color: rgba(0, 0, 0, 0.5);
    --modal-overlay-padding-top: 20px;
    --modal-overlay-padding-left: 20px;
    --modal-overlay-padding-right: 20px;
    --modal-overlay-padding-bottom: 20px;

    /* Dialog */
    --modal-dialog-background-color: #fff;
    --modal-dialog-border-radius: 0;
    --modal-dialog-box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.5);
    --modal-dialog-padding: 1rem;
    --modal-dialog-width: 500px;

    /* Close button */
    --modal-close-color: #fff;
    --modal-close-background-color: #000;
    --modal-close-border-radius: 50%;
    --modal-close-box-shadow: 0 0 0 1px #fff;
    --modal-close-display: block;
    --modal-close-font-family: 'Arial', sans-serif;
    --modal-close-font-size: 23px;
    --modal-close-line-height: 26px;
    --modal-close-width: 26px;

    /* Close button hover/focus */
    --modal-close-color-hover: #000;
    --modal-close-background-color-hover: #fff;
    --modal-close-box-shadow-hover: 0 0 0 1px #000;
}

Bundler

Just import. This calls the global function window.customElements.define.

import '@substrate-system/dialog'

Then use the tag in HTML:

<button id="open-modal" type="button">Open modal</button>

<modal-window id="my-modal">
    <h2>Modal Title</h2>
    <p>This is the modal content.</p>
    <p>Click the X, press Escape, or click outside to close.</p>
</modal-window>

Open/close via JavaScript:

const modal = document.getElementById('my-modal')

document.getElementById('open-modal').addEventListener('click', () => {
    modal.open()
})

HTML only

First copy the file to a location accessible to your web server.

cp ./node_modules/@substrate-system/dialog/dist/index.min.js ./public/dialog.js

Then link to the file in HTML

<body>
    <p>...content...</p>
    <script type="module" src="/dialog.js"></script>
</body>

API

Attributes

active

Controls whether the modal is open. Set to "true" to open, "false" or remove the attribute to close.

modal.setAttribute('active', 'true')   // open
modal.setAttribute('active', 'false')  // close
modal.removeAttribute('active')        // close

closable

Set to "false" to prevent the modal from being closed via the close button, Escape key, or clicking outside. You must close it programmatically. Defaults to true.

<modal-window closable="false">
    <h2>Unclosable Modal</h2>
    <p>This modal cannot be closed with the X button, Escape key, or clicking outside.</p>
    <button id="close-btn" type="button">Close this modal</button>
</modal-window>
document.getElementById('close-btn').addEventListener('click', () => {
    modal.close()
})

no-icon

Hides the close button icon. Useful when you want to provide your own close UI.

<modal-window no-icon>
    <header>
        <button type="button" id="cancel">Cancel</button>
        <h3>Edit profile</h3>
        <button type="button" id="save">Save</button>
    </header>
    <div>...form content...</div>
</modal-window>

animated

Controls whether open/close animations are used. Set to "false" to disable. Defaults to true. Animations also respect prefers-reduced-motion.

<modal-window animated="false">
    <p>No animation</p>
</modal-window>

static

When set to "true", clicking outside the modal does not close it. The Escape key and close button still work (unless closable="false").

<modal-window static="true">
    <p>Click outside won't close this</p>
</modal-window>

noclick

When present, clicking the backdrop does not close the modal. Unlike static, this is a boolean attribute (no value needed). The Escape key and close button still work.

<modal-window noclick>
    <p>Clicking the backdrop won't close this</p>
</modal-window>

close

Sets the title/aria-label for the close button. Defaults to "Close".

<modal-window close="Dismiss">
    <p>Close button will have title "Dismiss"</p>
</modal-window>

Methods

open()

Opens the modal and focuses it.

const modal = document.querySelector('modal-window')
modal.open()

close()

Closes the modal and returns focus to the previously focused element.

modal.close()

Accessibility

Things handled by this library:

  • role="dialog" and aria-modal="true" on the dialog
  • Focus trapping (Tab cycles within modal)
  • Escape key closes the modal (when closable)
  • Focus returns to the trigger element on close
  • Close button has aria-label
  • Respects prefers-reduced-motion

Things You Need To Do

Include a heading

The component extracts text from the first heading (h1-h6) to use as the dialog's aria-label. Always include a descriptive heading:

<!-- Good: heading text becomes the aria-label -->
<modal-window>
    <h2>Edit Profile</h2>
    <p>Update your information below.</p>
</modal-window>

<!-- Avoid: no heading results in aria-label="modal" -->
<modal-window>
    <p>Some content without a heading...</p>
</modal-window>

Adding a description

For modals with important supplementary text (like warnings), you can add aria-describedby. This library will handle an aria-describedby attribute correctly, meaning that it will be forwarded to the correct element.

<modal-window aria-describedby="delete-warning" id="confirm-delete">
    <h2>Delete Account</h2>
    <p id="delete-warning">
        This action cannot be undone.
    </p>
    <button type="button">Cancel</button>
    <button type="button">Delete</button>
</modal-window>

Example

See ./example.

Credits

Thanks @nathansmith and Smashing Magazine for publishing this originally.