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;

  1. Title
  2. Wysiwyg text
  3. Call to Action buttons
  4. 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.