<modal-element>
A custom element to create a modal, using the native <dialog> element under the hood.
Examples
Below are examples of different configurations and use cases of the <modal-element>
component.
Table of contents
- Default configuration
- Static backdrop
- Without header
- Without title
- Without default close button
- Fullscreen modal
- Without footer
- Without animations
- Scrolling long content
- Custom styling
- Truncate long title
- Custom backdrop
- Custom transition (enter/leave)
- Prevent modal from closing
- Modal placement
- Preserve body overflow
- Nested modals
- Interactive demo
Note: Open the browser's console to see the events fired by the modal element.
Default configuration
This is a modal with the default configurations. It will close by clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Default configuration
This is a modal with the default configuration.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Default configuration</h2>
<p>This is a modal with the default configuration.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Static backdrop
By adding the static-backdrop
attribute, the modal will not close by clicking on the backdrop.
The modal will still close by clicking the "X" button, or pressing the Esc key.
Static backdrop
This is a modal with static backdrop. Clicking on the backdrop will not close the modal.
Close the modal by either clicking the "X" button, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element static-backdrop>
<h2 slot="header">Static backdrop</h2>
<p>This is a modal with static backdrop.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Without header
By adding the no-header
attribute, the modal will not have a header. Note that this will also remove the default close button, therefore you need to provide a way to close the modal.
Without header
This is a modal without header.
Close the modal by either clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element no-header>
<h2 slot="header">Without header</h2>
<p>This is a modal without header.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Without title
If you don't need a title, you can omit it by not providing any content for the header
slot.
This is a modal without title. It will still have a close button though.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<p>This is a modal without title. It will still have a close button though.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Without default close button
By adding the no-close-button
attribute, the modal will not have a default close button.
Without close button
This is a modal without default close button.
Close the modal by either clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element no-close-button>
<h2 slot="header">Without close button</h2>
<p>This is a modal without default close button.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Fullscreen modal
By adding the fullscreen
attribute, the modal will be displayed in fullscreen mode.
Fullscreen modal
This is a fullscreen modal.
Note, that --me-width
and --me-height
CSS Custom Properties are overriden when the modal is in fullscreen mode.
Close the modal by either clicking the "X" button, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element fullscreen>
<h2 slot="header">Fullscreen modal</h2>
<p>This is a fullscreen modal.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Without footer
If you don't need a footer, you can omit it by not providing any content for the footer
slot.
Without footer
This is a modal without footer.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Without footer</h2>
<p>This is a modal without footer.</p>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Without animations
By adding the no-animations
attribute, the modal will not use animations on open and close.
It also removes the backdrop click animation if the static-backdrop
attribute is present.
Without animations
This is a modal that does not use animations on open and close or backdrop click.
Close the modal by either clicking the "X" button, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element no-animations static-backdrop>
<h2 slot="header">Without animations</h2>
<p>This is a modal that does not use animations on open and close or backdrop click.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Scrolling long content
By design, the modal's height will not exceed that of the viewport. If the content is longer than the viewport, the body will scroll while the header and footer will remain fixed and visible.
Scrolling long content
This is a modal with really long content. The header and footer will remain fixed, while the body will scroll.
Scroll down and see for yourself! 👇
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<style>
.long-content {
min-height: 150vh; /* Really long body */
}
</style>
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Scrolling long content</h2>
<div class="long-content">
<p>
This is a modal with really long content.
The header and footer will remain fixed, while the body will scroll.
</p>
<p>Scroll down and see for yourself! 👇</p>
</div>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Custom styling
The modal can be styled and customized as you wish. The following example uses Water.css for styling.
Custom styling
This is a modal with custom styling.
You can style the various elements of the component using the available CSS Parts or by overriding the default CSS Custom Properties.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<style>
modal-element {
--me-width: 35rem;
--me-border-width: 1px;
--me-border-color: var(--border);
--me-border-radius: 0.375rem;
--me-box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.25);
--me-header-background-color: var(--background-alt);
--me-body-background-color: var(--background-body);
--me-footer-background-color: var(--background-alt);
--me-backdrop-background: rgba(0, 0, 0, 0.7);
}
modal-element::part(header) {
border-bottom: 1px solid var(--border);
}
modal-element::part(footer) {
border-top: 1px solid var(--border);
}
modal-element::part(close) {
background-color: var(--button-base);
border: 1px solid var(--border);
border-radius: 0.25rem;
color: var(--text-bright);
transition: background-color 0.2s ease;
}
modal-element::part(close):hover {
background-color: var(--button-hover);
}
</style>
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Custom styling</h2>
<p>This is a modal with custom styling.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Truncate long title
By default, the modal's title will not be truncated if it's too long. You can override this behavior by using some CSS.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Beatae quis eveniet dicta saepe rem impedit odio quisquam ducimus eaque animi laborum quod, iusto autem, nobis cumque, voluptate repellat quo ab?
This is a modal with a really long title.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<style>
modal-element::part(title) {
/* https://dfmcphee.com/flex-items-and-min-width-0 */
min-width: 0;
}
modal-element [slot="header"] {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Lorem ipsum dolor sit amet consectetur adipisicing elit. Beatae quis eveniet dicta saepe rem impedit odio quisquam ducimus eaque animi laborum quod, iusto autem, nobis cumque, voluptate repellat quo ab?</h2>
<p>This is a modal with a really long title.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Custom backdrop
You can customize the backdrop using CSS Custom Properties.
It's me, Mario!
...and I'm in a custom backdrop!
If you are wondering how I got here, I used the --me-backdrop-background
CSS Custom Property.
If you don't see me though, it's probably because of this bug but fortunately it's already being worked on.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<style>
--me-backdrop-background: #0060ad url('./assets/super-mario.jpg') no-repeat bottom center / 90%;
</style>
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">It's me, Mario!</h2>
<p>...and I'm in a custom backdrop!</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Custom transition (enter/leave)
You can customize the modal's enter/leave transition using CSS.
Note, that browser support is limited to those that suport the
@starting-style
CSS at-rule. Also, the prefers-reduced-motion
media query is respected by default
and animations are disabled when the user has enabled the "reduce motion" option in their OS settings.
Custom transition
This is a modal with custom transitions.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<style>
@media (prefers-reduced-motion: no-preference) {
modal-element:not([no-animations])::part(base) {
transition:
transform 0.35s,
opacity 0.35s,
display 0.35s allow-discrete,
overlay 0.35s allow-discrete;
}
/* IS-OPEN STATE */
modal-element[open]:not([no-animations])::part(base) {
transform: translateY(0);
opacity: 1;
}
/* EXIT STATE */
modal-element:not([no-animations])::part(base) {
transform: translateY(-2rem);
opacity: 0;
}
/* BEFORE-OPEN STATE */
@starting-style {
modal-element[open]:not([no-animations])::part(base) {
transform: translateY(-2rem);
opacity: 0;
}
}
}
</style>
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Custom transition</h2>
<p>This is a modal with custom transitions.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Prevent modal from closing
You can prevent the modal from closing by listening to the me-request-close
event and
calling event.preventDefault()
in the event handler.
Prevent from closing
This modal will not close when clicking the "X" button, but it will close when clicking on the backdrop or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element>
<h2 slot="header">Prevent from closing</h2>
<p>
This modal will not close when clicking the "X" button,
but it will close when clicking on the backdrop
or pressing the <kbd>Esc</kbd> key.
</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
modalElement.addEventListener('me-request-close', evt => {
if (evt.detail.reason === 'close-button') {
evt.preventDefault();
}
});
</script>
Modal placement
By default, the modal is centered on the screen both vertically and horizontally.
You can change the placement of the modal by using the placement
attribute.
Modal placement
This is a modal with custom placement.
placement="center"
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element placement="top-start">
<h2 slot="header">Modal placement</h2>
<p>This is a modal with custom placement.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<form name="modal-placement-form">
<label for="placement-select">Placement</label>
<select name="placement" id="placement-select">
<option value="top-start">Top Start</option>
<option value="top-center">Top Center</option>
<option value="top-end">Top End</option>
<option value="center-start">Center Start</option>
<option value="center">Center</option>
<option value="center-end">Center End</option>
<option value="bottom-start">Bottom Start</option>
<option value="bottom-center">Bottom Center</option>
<option value="bottom-end">Bottom End</option>
</select>
</form>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
const placementForm = document.forms['modal-placement-form'];
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
placementForm.addEventListener('change', evt => {
modalElement.setAttribute('placement', evt.target.value);
});
</script>
Preserve body overflow
By default, the modal will set the overflow
property of the document's body to hidden
when the modal is open.
This is to prevent the body from scrolling when the modal is open.
If you want to preserve the body overflow, you can add the preserve-overflow
attribute to the modal.
Preserve body overflow
This is a modal that preserves the body overflow. Try scrolling the document while the modal is open.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-modal-button">Open Modal</button>
<modal-element preserve-overflow>
<h2 slot="header">Preserve body overflow</h2>
<p>This is a modal that preserves the body overflow. Try scrolling the document while the modal is open.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openModalButton = document.getElementById('open-modal-button');
const modalElement = document.querySelector('modal-element');
openModalButton.addEventListener('click', () => {
modalElement.open = true;
});
</script>
Nested modals
This example demonstrates how to open a modal on top of another modal. Keep in mind though that this is considered a bad practice in terms of user experience and should be avoided if possible.
First modal
This is a modal from which you can open another modal.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Second modal
This is a modal that was opened on top of another modal.
Close the modal by either clicking the "X" button, clicking on the backdrop, or pressing the Esc key.
Source code
<button type="button" id="open-first-modal-button">Open Modal</button>
<modal-element id="first-modal">
<h2 slot="header">First modal</h2>
<p>This is a modal from which you can open another modal.</p>
<div slot="footer">
<button type="button" id="open-second-modal-button">Open another modal</button>
<button type="button" data-me-close>Close</button>
</div>
</modal-element>
<modal-element id="second-modal" placement="top-center" preserve-overflow>
<h2 slot="header">Second modal</h2>
<p>This is a modal that was opened on top of another modal.</p>
<button slot="footer" type="button" data-me-close>Close</button>
</modal-element>
<script>
const openFirstModalButton = document.getElementById('open-first-modal-button');
const openSecondModalButton = document.getElementById('open-second-modal-button');
const firstModalElement = document.getElementById('first-modal');
const secondModalElement = document.getElementById('second-modal');
openFirstModalButton.addEventListener('click', () => {
firstModalElement.open = true;
});
openSecondModalButton.addEventListener('click', () => {
secondModalElement.open = true;
});
</script>
Interactive demo
The following example is an interactive demo of the component. You can play around with the various options and see how they affect the modal.
Interactive demo
This is an interactive demo of the component.
License
Licensed under the The MIT License (MIT)