That’s actually not real windows but regular HTML elements that are absolutely (or sometimes fixed) positioned on the page; a basic example might look like this:
<a href="#" data-open-modal>click here</a>
<div class="modal-overlay">
<div class="modal-container">
<div class="modal-header">
Lorem ipsum
<a href="#" class="modal-close">×</a>
</div>
<div class="modal-content">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatem repellat, modi totam. Recusandae a culpa dolor, placeat veritatis sed qui dignissimos, non optio provident voluptatem, sunt dolores. Perspiciatis, esse natus.
</div>
</div>
</div>
And the CSS to make it look like a window:
.modal-overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, .5);
}
.modal-container {
flex-basis: 50%;
padding: 1rem;
background-color: #fff;
border-radius: 3px;
}
.modal-header {
display: flex;
font-weight: bold;
}
.modal-close {
margin-left: auto;
color: inherit;
text-decoration: none;
margin-top: -.5rem;
font-size: 2rem;
}
Of course it should be hidden initially though… so mutatis mutandis:
.modal-overlay {
display: none;
/* ... */
}
.modal-overlay.active {
display: flex;
}
You then need some JS to show it when clicking a certain link, and hide it when clicking the close button or the overlay:
var openModal = document.querySelector('[data-open-modal]')
var closeModal = document.querySelector('.modal-close')
var modalOverlay = document.querySelector('.modal-overlay')
var handleOpenModal = function (event) {
event.preventDefault()
modalOverlay.classList.add('active')
}
var handleCloseModal = function (event) {
if (event.target !== event.currentTarget) {
// Only hide the modal when clicking elements the handler
// got actually attached to, not others from which the
// event bubbles up to the current target (e.g. from the
// modal content)
return;
}
event.preventDefault()
modalOverlay.classList.remove('active')
}
openModal.addEventListener('click', handleOpenModal)
closeModal.addEventListener('click', handleCloseModal)
modalOverlay.addEventListener('click', handleCloseModal)
The animation part can be done with a CSS transition: opacity
(say); however transitions won’t work when simultaneously changing the display
property, and conversely we have to wait until the fading out animation finished until we can change it back to display: none
. The solution is splitting that in two different classes (again mutatis mutandis):
.modal-overlay {
display: none;
opacity: 0;
transition: opacity .2s ease;
}
.modal-overlay.active {
display: flex;
}
.modal-overlay.visible {
opacity: 1;
}
And the JS needs to be adjusted to add / remove the corresponding classes at the appropriate times:
var handleShowModal = function (event) {
modalOverlay.classList.add('visible')
}
var handleHideModal = function (event) {
modalOverlay.classList.remove('active')
}
var handleOpenModal = function (event) {
event.preventDefault()
modalOverlay.classList.add('active')
// Wait a tick to kickoff the animation
window.setTimeout(handleShowModal)
}
var handleCloseModal = function (event) {
if (event.target !== event.currentTarget) {
return;
}
event.preventDefault()
modalOverlay.classList.remove('visible')
// Hide the modal when the fading out is finished
modalOverlay.addEventListener(
'transitionend',
handleHideModal,
{ once: true, passive: true }
)
}
Here’s a fiddle putting it all together.