diff --git a/datasette/static/app.css b/datasette/static/app.css
index 4a4b0d5c..3b0546e3 100644
--- a/datasette/static/app.css
+++ b/datasette/static/app.css
@@ -63,7 +63,7 @@ em {
}
/* end reset */
-/* Modal CSS variables (shared by web components via Shadow DOM) */
+/* Shared modal CSS variables */
:root {
--modal-backdrop-bg: rgba(0, 0, 0, 0.5);
--modal-backdrop-blur: blur(4px);
diff --git a/datasette/static/modal.css b/datasette/static/modal.css
index 8590adae..aeec561f 100644
--- a/datasette/static/modal.css
+++ b/datasette/static/modal.css
@@ -1,4 +1,4 @@
-/* Shared by light-DOM dialogs and dialogs inside existing shadow roots. */
+/* Shared modal styles. */
datasette-modal {
display: contents;
}
diff --git a/datasette/static/modal.js b/datasette/static/modal.js
index 36e7a7b4..b252af21 100644
--- a/datasette/static/modal.js
+++ b/datasette/static/modal.js
@@ -1,8 +1,5 @@
-// Shared modal shell. Content stays in the caller's DOM, including plugin
-// controls and their form/ARIA relationships. The native dialog owns modality.
+// Shared lifecycle for native modal dialogs.
(() => {
- const stylesheet = document.currentScript.dataset.stylesheet;
-
class DatasetteModal extends HTMLElement {
constructor() {
super();
@@ -39,18 +36,6 @@
const dialog = this.dialog;
if (!dialog) return;
dialog.classList.add("datasette-modal");
- // The same CSS is used in the document and in existing web components.
- const root = this.getRootNode();
- if (
- root instanceof ShadowRoot &&
- !root.querySelector("link[data-datasette-modal]")
- ) {
- const link = document.createElement("link");
- link.rel = "stylesheet";
- link.href = stylesheet;
- link.dataset.datasetteModal = "";
- root.prepend(link);
- }
this._listeners?.abort();
this._listeners = new AbortController();
const options = { signal: this._listeners.signal };
@@ -86,11 +71,7 @@
(event) => {
if (event.key !== "Escape" || event.defaultPrevented) return;
// A nested native dialog or plugin picker gets first refusal.
- if (
- event.composedPath().find((node) => node.localName === "dialog") !==
- dialog
- )
- return;
+ if (event.target.closest("dialog") !== dialog) return;
event.preventDefault();
if (this.busy || this._escapeCleanup || this._escapeTimer !== null)
return;
@@ -158,10 +139,7 @@
const dialog = this.dialog;
if (!dialog.open) {
this._clearPendingClose();
- let active = this.ownerDocument.activeElement;
- while (active?.shadowRoot?.activeElement)
- active = active.shadowRoot.activeElement;
- this._trigger = trigger || active;
+ this._trigger = trigger || this.ownerDocument.activeElement;
this._restoreFocus = true;
dialog.showModal();
}
diff --git a/datasette/templates/base.html b/datasette/templates/base.html
index 43911ee3..b11d14f5 100644
--- a/datasette/templates/base.html
+++ b/datasette/templates/base.html
@@ -9,7 +9,7 @@
{% endfor %}
-
+
{% for url in extra_js_urls %}
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 35d6443c..57643f64 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -139,11 +139,11 @@ Modal dialogs
Core dialogs use the same ```` component available to plugins. See :ref:`javascript_plugins_modals` for examples, lifecycle methods, dismissal guards and shared styles.
-The implementation lives in ``datasette/static/modal.js`` and ``datasette/static/modal.css``. The wrapper keeps each native ``