@substrate-system/dialog
v0.0.34
Published
Modal dialog window
Downloads
867
Readme
dialog
Modal/dialog window.
See smashingmagazine.com article and nathansmith/cta-modal.
Install
npm i -S @substrate-system/dialogUse
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.jsThen 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') // closeWhile closed, the component sets the hidden attribute on the
<modal-window> element, so the host renders nothing. The attribute is
removed when the modal opens, and added back once the close animation
has finished.
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>noclick
When present, clicking the backdrop does not close the modal. 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()Classes
The <modal-window> element gets a class reflecting its state: open
while the modal is open, and closed otherwise.
modal-window.open {
/* styles for the open state */
}Events
Follow the pattern in web-component.
Events are namespaced with the prefix modal-window:. Listen for them by
calling ModalWindow.event('name').
close
import { ModalWindow } from '@substrate-system/dialog'
const el = document.querySelector('modal-window')
el.addEventListener(ModalWindow.event('close'), () => {
console.log('closed')
})open
import { ModalWindow } from '@substrate-system/dialog'
const el = document.querySelector('modal-window')
el.addEventListener(ModalWindow.event('open'), () => {
console.log('is open')
})Accessibility
Things handled by this library:
role="dialog"andaria-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
Credits
Thanks @nathansmith and Smashing Magazine for publishing this originally.
