Skip to content

Commit 9316254

Browse files
committed
fix: Use proper native HTML <dialog> component for dialogs for better a11y
Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
1 parent 9f09d62 commit 9316254

3 files changed

Lines changed: 52 additions & 66 deletions

File tree

assets/js/event-detail-modal.js

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,42 @@
11
import DOMPurify from "dompurify";
22
import { proxyImageUrl } from "./events.js";
33

4-
const backdropClass = "pf-v6-c-backdrop";
5-
64
const modalTemplate = document.createElement("template");
75
modalTemplate.innerHTML = `
8-
<div class="${backdropClass}" style="display: none;">
9-
<div class="pf-v6-l-bullseye">
10-
<div class="pf-v6-c-modal-box pf-m-md" role="dialog" aria-modal="true"
11-
aria-labelledby="event-detail-title" aria-describedby="event-detail-body">
12-
<div class="pf-v6-c-modal-box__close">
13-
<button class="pf-v6-c-button pf-m-plain" type="button" aria-label="Close" data-slot="close">
14-
<span class="pf-v6-c-button__icon">
15-
<i class="fas fa-times" aria-hidden="true"></i>
16-
</span>
17-
</button>
6+
<dialog class="pf-v6-c-modal-box pf-m-md" closedby="any"
7+
aria-labelledby="event-detail-title" aria-describedby="event-detail-body">
8+
<form method="dialog" class="pf-v6-c-modal-box__close">
9+
<button class="pf-v6-c-button pf-m-plain" type="submit" aria-label="Close" autofocus>
10+
<span class="pf-v6-c-button__icon">
11+
<i class="fas fa-times" aria-hidden="true"></i>
12+
</span>
13+
</button>
14+
</form>
15+
<header class="pf-v6-c-modal-box__header">
16+
<div class="pf-v6-c-modal-box__header-main">
17+
<h1 class="pf-v6-c-modal-box__title" id="event-detail-title" data-slot="title"></h1>
1818
</div>
19-
<header class="pf-v6-c-modal-box__header">
20-
<div class="pf-v6-c-modal-box__header-main">
21-
<h1 class="pf-v6-c-modal-box__title" id="event-detail-title" data-slot="title"></h1>
22-
</div>
23-
</header>
24-
<div class="pf-v6-c-modal-box__body" id="event-detail-body" tabindex="0" data-slot="body">
25-
<div data-slot="content">
26-
<div class="pf-v6-c-skeleton pf-v6-u-mb-md" style="--pf-v6-c-skeleton--Height: 10rem; border-radius: var(--pf-t--global--border--radius--medium);"></div>
27-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
28-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50 pf-v6-u-mb-sm"></div>
29-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
30-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50 pf-v6-u-mb-md"></div>
31-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-v6-u-mb-sm"></div>
32-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
33-
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50"></div>
34-
</div>
19+
</header>
20+
<div class="pf-v6-c-modal-box__body" id="event-detail-body" data-slot="body">
21+
<div data-slot="content">
22+
<div class="pf-v6-c-skeleton pf-v6-u-mb-md" style="--pf-v6-c-skeleton--Height: 10rem; border-radius: var(--pf-t--global--border--radius--medium);"></div>
23+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
24+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50 pf-v6-u-mb-sm"></div>
25+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
26+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50 pf-v6-u-mb-md"></div>
27+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-v6-u-mb-sm"></div>
28+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-75 pf-v6-u-mb-sm"></div>
29+
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50"></div>
3530
</div>
36-
<footer class="pf-v6-c-modal-box__footer">
37-
<a data-slot="rsvp" target="_blank" rel="noopener"
38-
class="pf-v6-c-button pf-m-primary">RSVP on Luma</a>
39-
<button data-slot="cancel" type="button"
40-
class="pf-v6-c-button pf-m-link">Close</button>
41-
</footer>
4231
</div>
43-
</div>
44-
</div>`;
32+
<footer class="pf-v6-c-modal-box__footer">
33+
<a data-slot="rsvp" target="_blank" rel="noopener"
34+
class="pf-v6-c-button pf-m-primary">RSVP on Luma</a>
35+
<form method="dialog" style="display: inline;">
36+
<button type="submit" class="pf-v6-c-button pf-m-link">Close</button>
37+
</form>
38+
</footer>
39+
</dialog>`;
4540

4641
const detailTemplate = document.createElement("template");
4742
detailTemplate.innerHTML = `
@@ -79,25 +74,13 @@ customElements.define(
7974
class extends HTMLElement {
8075
connectedCallback() {
8176
this.append(modalTemplate.content.cloneNode(true));
82-
this._backdrop = this.querySelector(`.${backdropClass}`);
77+
this._dialog = this.querySelector("dialog");
8378
this._$ = (s) => this.querySelector(`[data-slot="${s}"]`);
84-
85-
this._$("close").addEventListener("click", () => this.close());
86-
this._$("cancel").addEventListener("click", () => this.close());
87-
this._backdrop.addEventListener("click", (e) => {
88-
if (e.target === this._backdrop) this.close();
89-
});
90-
this._keyHandler = (e) => {
91-
if (e.key === "Escape") this.close();
92-
};
9379
}
9480

9581
async open(eventApiId, api) {
96-
this._backdrop.style.display = "";
97-
document.documentElement.style.overflow = "hidden";
98-
document.addEventListener("keydown", this._keyHandler);
99-
100-
this._$("title").innerHTML = '<div class="pf-v6-c-skeleton pf-m-text-lg pf-m-width-50"></div>';
82+
this._$("title").innerHTML =
83+
'<div class="pf-v6-c-skeleton pf-m-text-lg pf-m-width-50"></div>';
10184
const content = this._$("content");
10285
content.innerHTML = `
10386
<div class="pf-v6-c-skeleton pf-v6-u-mb-md" style="--pf-v6-c-skeleton--Height: 10rem; border-radius: var(--pf-t--global--border--radius--medium);"></div>
@@ -110,6 +93,8 @@ customElements.define(
11093
<div class="pf-v6-c-skeleton pf-m-text-sm pf-m-width-50"></div>`;
11194
this._$("rsvp").style.display = "none";
11295

96+
this._dialog.showModal();
97+
11398
try {
11499
const data = await fetch(
115100
`${api}/events/detail?event_api_id=${encodeURIComponent(eventApiId)}`,
@@ -154,17 +139,17 @@ customElements.define(
154139
$("time").textContent = timeText;
155140

156141
if (data.location) {
157-
const venueText = data.full_address
142+
$("venue").textContent = data.full_address
158143
? `${data.location}${data.full_address}`
159144
: data.location;
160-
$("venue").textContent = venueText;
161145

162146
if (data.map_url) {
163-
const mapLink = document.createElement("a");
164-
mapLink.href = data.map_url;
165-
mapLink.target = "_blank";
166-
mapLink.rel = "noopener";
167-
mapLink.className = "pf-v6-c-button pf-m-link pf-m-inline pf-m-small";
147+
const mapLink = Object.assign(document.createElement("a"), {
148+
href: data.map_url,
149+
target: "_blank",
150+
rel: "noopener",
151+
className: "pf-v6-c-button pf-m-link pf-m-inline pf-m-small",
152+
});
168153
mapLink.innerHTML =
169154
'View on map <span class="pf-v6-c-button__icon pf-m-end"><icon-external></icon-external></span>';
170155
$("map-link").append(mapLink);
@@ -201,11 +186,5 @@ customElements.define(
201186
"<p>Could not load event details. Please try again later.</p>";
202187
}
203188
}
204-
205-
close() {
206-
this._backdrop.style.display = "none";
207-
document.documentElement.style.overflow = "";
208-
document.removeEventListener("keydown", this._keyHandler);
209-
}
210189
},
211190
);

assets/scss/main.scss

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ $font-path: "../../webfonts";
1212
@import "@fortawesome/fontawesome-free/scss/solid";
1313
@import "@fortawesome/fontawesome-free/scss/brands";
1414

15-
// Customization here
15+
// By default this is a `<div>`, but we want to use the native `<dialog>` component instead
16+
dialog.pf-v6-c-modal-box:not([open]) {
17+
display: none;
18+
}
19+
20+
dialog[open].pf-v6-c-modal-box::backdrop {
21+
background-color: var(--pf-t--global--background--color--backdrop--default);
22+
}

layouts/baseof.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@
107107
</div>
108108
{{ end }}
109109

110-
<event-detail-modal></event-detail-modal>
111110
</div>
111+
<event-detail-modal></event-detail-modal>
112112

113113
{{ with resources.Get "js/main.js" }}
114114
{{ $opts := dict

0 commit comments

Comments
 (0)