Skip to content

A custom element to create a modal, using the native dialog element under the hood.

License

Notifications You must be signed in to change notification settings

georapbox/modal-element

Repository files navigation

npm version npm license

<modal-element>

A custom element to create a modal, using the native <dialog> element under the hood.

API documentationDemo

Install

$ npm install --save @georapbox/modal-element

Usage

Script

import { ModalElement } from './node_modules/@georapbox/modal-element/dist/modal-element.js';

// Manually define the element.
ModalElement.defineCustomElement();

Alternatively, you can import the automatically defined custom element.

import './node_modules/@georapbox/modal-element/dist/modal-element-defined.js';

Markup

<modal-element>
  <h1 slot="header">Modal Title</h1>
  <p>Modal content</p>
  <div slot="footer">Modal footer</div>
</modal-element>

Style

By default, the component comes with minimal styling to remain as less opinionated as possible. However, you can style the various elements of the component using the available CSS Parts or by overriding the default CSS Custom Properties. A working example of styling the component can be found here.

API

Properties

Name Reflects Type Required Default Description
open Boolean - false Determines whether the modal is open or not.

NOTE: If you create a modal-element programmatically via document.createElement('modal-element'), you need to append it to the DOM before setting this property to true. Otherwise, you will get an error like this: Uncaught DOMException: Failed to execute 'showModal' on 'HTMLDialogElement': The element is not in a Document. This is because the native showModal method of the HTMLDialogElement requires the element to be in the DOM before it can be called.
staticBackdrop
static-backdrop
Boolean - false Determines whether the modal should be closed when the user clicks outside the modal or not.
noHeader
no-header
Boolean - false Determines whether the modal should have a header or not. Note, that if the modal has no header, the default close button will not be visible as well, therefore you probably need to provide an accessible way for users to dismiss the modal.
noAnimations
no-animations1
Boolean - false Determines whether the modal should have animations or not. Animations include the modal's entrance and exit animations, as well as the static backdrop's bounce effect when users clicks on it.
noCloseButton
no-close-button
Boolean - false Determines whether the modal should have a default close button or not. If the modal has no close button, you probably need to provide an accessible way for users to dismiss the modal.
fullscreen Boolean - false Determines whether the modal should be displayed in fullscreen or not. Note, the --me-width and --me-height CSS custom properties will be overriden if this property is set to true. Notice that the backdrop is not visible in fullscreen mode. If you still want a full-width modal with a backdrop, you can use --me-width: 100%; and --me-height: 100%; instead.
preserveOverflow
preserve-overflow
Boolean - false Determines whether the modal should preserve the document's overflow when the modal is open. If this property is set to true, the document's overflow will not be hidden when the modal is open.
placement String - center Determines the placement of the modal. Possible values are 'top-start', 'top-center', 'top-end', 'center-start', 'center', 'center-end', 'bottom-start', 'bottom-center', 'bottom-end'. Placement respects the direction of the document, eg if the document is in RTL mode, the placement will be mirrored accordingly. Note, that the placement is not applied when the modal is in fullscreen mode and it might not be visible in some cases when the modal is too large for the viewport, eg in small viewports.
closeLabel
close-label
String - Close The label for the default close button that resides in the modal's header. It is used as the aria-label attribute of the close button. If user provides text content for the close button using the close slot, this property is ignored and the aria-label attribute is removed.

All of the above properties reflect their values as HTML attributes to keep the element's DOM representation in sync with its JavaScript state.

1 A note for animations: The entry/exit animations of the modal are implemented using the @starting-style CSS at-rule, which is a relatively new feature and currently only supported in Chromium-based browsers. Here is an interesting article for more information about the feature. As a fallback, the modal will be rendered without animations in browsers that do not support this feature. Also, keep in mind that if the user has reduced motion enabled in their operating system, the animations will be disabled as well, even if they are supported by the browser.

Slots

Name Description
(default/unnamed) The modal's main content.
header The modal's header content, usually a title.
footer The modals' footer content. Usually used for buttons or other actions.
close The content of the close button that appears in the modal's header.

CSS Parts

Name Description
base The componen'ts base wrapper. In this case this is the native <dialog> element.
panel The modal's panel element where the all the content is rendered.
header The modal's header element. It wraps the modal's title and close button.
title The modal's title wrapper element.
close The default close button rendered in the modal's header.
body The modal's body element, where all main content is rendered.
footer The modal's footer element.

CSS Custom Properties

Name Description Default
--me-width The preferred width of the modal. If this width is larger than the viewport, the modal's width will adjust to fit the viewport. 32rem
--me-height The preferred height of the modal. If this height is larger than the viewport, the modal's height will adjust to fit the viewport. fit-content
--me-border-color The modal's border color. initial
--me-border-style The modal's border style. solid
--me-border-width The modal's border width. initial
--me-border-radius The modal's border radius. 0
--me-box-shadow The modal's box shadow. none
--me-background-color The modal's background color. canvas
--me-header-spacing The amount of spacing (padding) to use for the header element. 1rem
--me-body-spacing The amount of spacing (padding) to use for the body element. 1rem
--me-footer-spacing The amount of spacing (padding) to use for the footer element. 1rem
--me-header-background-color The header's background color. transparent
--me-body-background-color The body's background color. transparent
--me-footer-background-color The footer's background color. transparent
--me-backdrop-background1 The backdrop's background shorthand property. It can be used to set the backdrop's background color, gradient, or image. rgba(0, 0, 0, 0.5)
--me-backdrop-filter1 The backdrop's filter property. It can be used to set the backdrop's filter effects like blur, brightness, contrast, etc. none

1 At the time of writing, the ::backdrop pseudo-element (which is used under the hood) does not inherit from originating element per the specification. This means that the backdrop's CSS custom properties cannot be inherited from the modal's CSS custom properties. Fortunately, this is a known issue and it is being worked on by browser vendors and the CSS working group. Currently, it's already fixed in Firefox Nightly 120+ and Chromium Canary 121+. For more details, check the browser reported bugs below:

Events

Name Description Event Detail
me-open Emitted when the modal opens. { element: HTMLElement }
me-close Emitted when the modal is dismissed in any way, either by clicking on the close button, by pressing the Esc key or by clicking outside the modal. { element: HTMLElement }
me-request-close Emitted when the modal is about to be dismissed. This event is cancellable, hence you can prevent the modal from being dismissed by calling event.preventDefault(). This can be useful when you want to prevent users from dismissing the modal until they have completed a certain task, eg submitting a form. In the event.detail object you can find the reason why the modal is about to be dismissed. You can use the reason to determine whether the modal should be dismissed or not as demonstrated in this example. Note, that this event is not emitted if the modal is dismissed by an external event, eg by calling the hide method or by setting the open property to false manually. { element: HTMLElement, reason: 'close-button' | 'escape-key' | 'backdrop-click' | 'external-invoker' }

Methods

Name Type Description Arguments
defineCustomElement Static Defines/registers the custom element with the name provided. If no name is provided, the default name is used. The method checks if the element is already defined, hence will skip trying to redefine it. elementName='modal-element'
show1 Instance Opens the modal. -
hide1 Instance Closes the modal. -

1 These methods are only available after the component has been defined. To ensure the component is defined, you can use whenDefined method of the CustomElementRegistry interface, eg customElements.whenDefined('modal-element').then(() => { /* call methods here */ });

Closing mechanisms

The modal provides some default ways for the end user to dismiss the modal. These are:

  • By clicking on the default close button in the modal's header.
  • By pressing the Esc key.
  • By clicking outside the modal if the staticBackdrop property is set to false.

These default mechanisms are enough for most use cases. However, sometimes developers want to provide extra mechanisms for modal closure. The most usual scenario is to provide a "Close" button on the modal's footer or a "Cancel" button for modals that act as confirmation prompts. To achieve this the developer would need to add the button and then add a click event listener to it to dismiss the modal. This is a common pattern and it works well. However, it can be a bit cumbersome to do this for every modal, especially if the developer is using the modal in a lot of places. Therefore, as of v1.5.0, the modal provides an automatic way to dismiss the modal by clicking on a button or a link inside the modal's content with the data-me-close attribute. This is a convenient way to dismiss the modal without the need to add a click event listener to the element.

Hopefully, in the future we are going to have more declarative ways to handle these kinds of scenarions natively with Invokers, but for now, this is a simple way to achieve the same result.

Here is an example:

<modal-element>
  <h5 slot="header">Modal Title</h5>
  <p>Modal content</p>
  <button slot="footer" type="button" data-me-close>Close</button>
</modal-element>

By just adding the data-me-close attribute to the button, the modal will be dismissed when the button is clicked.

Note: As of v1.7.0, by clicking on the custom close button, the me-request-close event will also be emitted with the reason set to 'external-invoker' and therefore the user can prevent the modal from being dismissed by calling event.preventDefault() in the event listener if needed.

Changelog

For API updates and breaking changes, check the CHANGELOG.

License

The MIT License (MIT)