When building web applications, you need a modal at some point. A popup asking for confirmation before a critical action, a dialog showing validation errors or maybe a login form.
In this post you will learn to create one utilizing vue-slots
and some scss.
Before continuing this post i would recommend you to read this article about structuring your vue js application. The scss-variables used in our modal-component assume that you have your project setup this way.
The template
<template>
<div class="modal">
<div class="modal__bg" @click="$emit('close')" />
<div class="modal__content">
<div class="modal__close">
<button @click="$emit('close')">&times;</button>
</div>
<slot />
</div>
</div>
</template>
The template of this component is really minimalistic. A root div with class modal
with two children modal__bg
and modal__content
. The modal__content
contains a slot and a close-button. The <slot />
is where your component's content will be placed.
If you use the modal-component this way:
<modal>
<h1>Modal heading</h1>
<p>With some model content</p>
</modal>
it will be parsed as:
<div class="modal">
...
<div class="modal__content">
...
<h1>Modal heading</h1>
<p>With some model content</p>
</div>
</div>
The $emit('close')
is here to emit an event to the parent component so that it knows when to hide the component. (Assuming that we show and hide the component through a v-if statement).
Styling
<style lang="scss">
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: auto;
height: auto;
z-index: 1000;
&__bg {
background-color: rgba($black, 0.25);
bottom: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1;
position: fixed;
z-index: 1;
}
&__content {
background-color: $white;
color: $black;
border-radius: $border-radius;
display: flex;
flex-direction: column;
width: 95vw;
height: 95%;
max-height: 95vh;
max-width: 800px;
padding: $spacing * 4;
position: relative;
opacity: 1;
z-index: 2;
}
&__close {
position: absolute;
right: $spacing * 2;
top: $spacing * 2;
z-index: 3;
}
}
</style>
The root-element is going to be a full-window element with position: fixed
applied, so that it always floats over every content on the screen. A high enough z-index is required here.
display:flex
with align-items: center
and justify-content:center
makes sure our modal__content
is always centered on the screen.
width: 95vw;
height: 95%;
max-height: 95vh;
max-width: 800px;
For the styling of the model__content
, use width, height, max-height and max-width to make the model responsive. What this does is make the content 800px wide at maximum. When the screen is smaller then 800px, it will show the content at 95% width. If this does not makes sense, please let me now in the comments below.
Behind the modal__content
we put a modal__bg
which fades out our background a little bit. We also make this one fixed to make it a full-window element. Give this element a z-index which is 1 less then the modal__content
so that it is always behind the content element.
At last you are going to need a close button. Position the button with position:absolute;
to the top right of the window. Because this the place where will expect it.
Full component
<template>
<div class="modal">
<div class="modal__bg" @click="$emit('close')" />
<div class="modal__content">
<div class="modal__close">
<button @click="$emit('close')">&times;</button>
</div>
<slot />
</div>
</div>
</template>
<style lang="scss">
.modal {
align-items: center;
justify-content: center;
display: flex;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: auto;
height: auto;
z-index: 1000;
&__bg {
background-color: rgba($black, 0.5);
bottom: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1;
position: fixed;
z-index: 2;
}
&__content {
background-color: $white;
color: $black;
border-radius: $border-radius;
display: flex;
flex-direction: column;
width: 95vw;
height: 95%;
max-height: 95vh;
max-width: 800px;
padding: $spacing * 4;
position: relative;
opacity: 1;
z-index: 2;
}
&__close {
position: absolute;
right: $spacing * 2;
top: $spacing * 2;
z-index: 3;
}
}
</style>
You now have built a simple modal component. You can always expand your component if you need more buttons for example.