Need a simple POPUP? I use this dialog everywhere now
Sometimes a client will ask for a popup. Then I always ask what do you want to do with it?
Basically it comes down to this simple use case;
- Title
- Wysiwyg text
- Call to Action buttons
- Settings, like when to show popup.
So for the first three it's just content in your CMS or whatever. But the setting I always use the 'days' before popping it in your face again.
Let's dive in code. I do use 1 npm package: dialog-polyfill
Liquid Example for data
This popup is already present on page in html dialog
form. And it contains some settings you can alter in your prefered way.
{%- if site.popups != blank -%}
{%- assign popup = site.popups.first -%}
<dialog data-days-delay="{{ popup.popup_days_delay | default: 1 }}"
data-popup-delay="1000"
data-id="pop_id_{{ popup.id }}"
{% if popup.image != blank %}class="has-popup-image"{% endif %}
{% if popup.link != blank %}data-link="{{ popup.link }}"{% endif %}>
<form method="dialog">
<button type="submit">
<span>Shut up!</span>
<svg class="svg-close"><use href="#svg_close"></use></svg>
</button>
</form>
{% if popup.image != blank %}
...
{% endif %}
<div class="popup-content">
<h3 class="title">{{ popup.title }}</h3>
{{ popup.text }}
{% if popup.buttons != blank %}
<div class="buttons">
{%- for b in popup.buttons -%}
{%- include 'includes/modules/button', button: b -%}
{%- endfor -%}
</div>
{% endif %}
</div>
</dialog>
{%- endif -%}
So this renders the popup on page. Then with javascript we make this pop! :)
import dialogPolyfill from "dialog-polyfill";
async function initDialogPopups(): Promise<void> {
const dialogs = document.querySelectorAll("dialog");
// Add guard for length, if no dialogs are found, return.
if (dialogs.length === 0) {
return;
}
dialogs.forEach((dialog: HTMLDialogElement) => {
// Use polyfill to make dialog work in all browsers.
dialogPolyfill.registerDialog(dialog);
// Every wrapper has a delay; Get it from data-popup-delay attribute.
const delay =
dialog.dataset.popupDelay != null
? parseInt(dialog.dataset.popupDelay)
: 0;
const daysDelay =
dialog.dataset.daysDelay != null ? parseInt(dialog.dataset.daysDelay) : 1;
// Popup id is written to Localstorage as to only show popup once.
const popupId = dialog.dataset.id!;
// Check if popup has been shown before.
// Also little cheatcode if site setting contains 1337 the popup always shows.
if (!(daysDelay === 1337) && localStorage.getItem(popupId) != null) {
const storageValue = localStorage.getItem(popupId)!;
const parsed = parseInt(storageValue);
const now = new Date().getTime();
if (parsed > now) {
return;
}
}
dialog.addEventListener("close", () => {
popupHandler(popupId);
});
setTimeout(() => {
dialog.showModal();
}, delay);
// Handle confirmed and cancelled events.
function popupHandler(id: string): void {
const dateThen = daysDelay * 24 * 60 * 60 * 1000; // 1 day
localStorage.setItem(id, String(dateThen + new Date().getTime()));
}
});
}
export { initDialogPopups };
Also a bit of SCSS to make it stylishly pop! :)
dialog::backdrop,
dialog + .backdrop { /* polyfill */
background-color: rgb(0,0,0);
opacity: 0.8;
}
dialog {
container-name: element;
container-type: inline-size;
border: none;
padding: 3rem;
border-radius: 2rem;
width: 100%;
display: grid;
grid-template-areas: "close" "content";
max-width: 50rem;
// This contains 'the' submit which closes the popup.
form {
z-index: 2;
grid-area: close;
justify-self: end;
align-self: start;
}
.svg-close {
width: 2.4rem;
height: 2.4rem;
flex: 0 0 auto;
fill: currentColor;
}
button[type="submit"],
input[type="submit"] {
color: black;
font-size: 80%;
appearance: none;
background: none;
border: none;
font-family: inherit;
display: flex;
align-items: center;
gap: .5rem;
}
&.has-popup-image {
button[type="submit"],
input[type="submit"] {
color: white;
}
}
button[type="submit"] {
cursor: pointer;
span {
transition: opacity .2s;
opacity: 0;
}
&:focus,
&:hover {
span {
opacity: 1;
}
}
}
.popup-content {
grid-area: content;
margin: 0;
display: grid;
grid-template-areas: "image" "text" "buttons";
}
.title {
margin: 0 0 2rem;
position: relative;
color: #bada55;
display: flex;
flex-direction: column;
}
.popup-image {
grid-area: close;
margin-inline: 0 -3rem;
margin-block: -3rem 3rem;
img {
width: 100%;
aspect-ratio: 1/0.6;
}
}
.button-group {
grid-area: buttons;
margin-block-start: 2.4rem;
}
&[open] {
animation: fadein .2s ease-in forwards;
}
}
@keyframes fadein{
0%{
opacity:0;
}
100%{
opacity:1;
}
}
And with this you have a super fancy and simple 'native' popup.
I've hacked the code onto this page. Although it's js and css now. And some tweaks because I am not using the polyfill. But still good to see it working.