Skip to content

Commit 24be5db

Browse files
committed
Use shadow DOM instead of an iframe to encapsulate CSS
Fix #161 The problem: the widget uses an iframe to encapsulate its CSS and prevent leaking it to the projects using the widget (and vice versa). But since the iframe has a `src` attribute with the value `about:blank`, password managers do not work properly (they refuse to set a password without a corresponding url). The solution: as suggested by this comment #161 (comment), this commit replaces the iframe by a shadow DOM element, which still encapsulates the modal CSS, but lets password managers do their thing (confirmed on Firefox 84.0.2).
1 parent 6fadf3b commit 24be5db

File tree

2 files changed

+24
-56
lines changed

2 files changed

+24
-56
lines changed

src/components/modal.css

+3-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
align-items: center;
5454
z-index: 99999;
5555
}
56+
.modalContainer[aria-hidden="true"] {
57+
display: none;
58+
}
5659

5760
.modalContainer::before {
5861
content: "";
@@ -253,7 +256,6 @@
253256
.btnClose::before {
254257
content: "×";
255258
font-size: 25px;
256-
line-height: 9px;
257259
}
258260

259261
.btnClose:hover,

src/netlify-identity.js

+21-55
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,6 @@ const netlifyIdentity = {
7474
store
7575
};
7676

77-
let queuedIframeStyle = null;
78-
function setStyle(el, css) {
79-
let style = "";
80-
for (const key in css) {
81-
style += `${key}: ${css[key]}; `;
82-
}
83-
if (el) {
84-
el.setAttribute("style", style);
85-
} else {
86-
queuedIframeStyle = style;
87-
}
88-
}
89-
9077
const localHosts = {
9178
localhost: true,
9279
"127.0.0.1": true,
@@ -112,28 +99,12 @@ function instantiateGotrue(APIUrl) {
11299
}
113100

114101
let root;
115-
let iframe;
116-
const iframeStyle = {
117-
position: "fixed",
118-
top: 0,
119-
left: 0,
120-
border: "none",
121-
width: "100%",
122-
height: "100%",
123-
overflow: "visible",
124-
background: "transparent",
125-
display: "none",
126-
"z-index": 99
127-
};
102+
let shadow;
128103

129104
observe(store.modal, "isOpen", () => {
130105
if (!store.settings) {
131106
store.loadSettings();
132107
}
133-
setStyle(iframe, {
134-
...iframeStyle,
135-
display: store.modal.isOpen ? "block !important" : "none"
136-
});
137108
if (store.modal.isOpen) {
138109
trigger("open", store.modal.page);
139110
} else {
@@ -249,34 +220,29 @@ function init(options = {}) {
249220
store.init(instantiateGotrue(APIUrl));
250221
store.modal.logo = logo;
251222
store.setNamePlaceholder(namePlaceholder);
252-
iframe = document.createElement("iframe");
253-
iframe.id = "netlify-identity-widget";
254-
iframe.title = "Netlify identity widget";
255-
iframe.onload = () => {
256-
const styles = iframe.contentDocument.createElement("style");
257-
styles.innerHTML = modalCSS.toString();
258-
iframe.contentDocument.head.appendChild(styles);
259-
root = render(
260-
<Provider store={store}>
261-
<App />
262-
</Provider>,
263-
iframe.contentDocument.body,
264-
root
265-
);
266-
runRoutes();
267-
};
268-
setStyle(iframe, iframeStyle);
269-
iframe.src = "about:blank";
223+
224+
const wrapper = document.createElement("div");
225+
226+
root = render(
227+
<Provider store={store}>
228+
<App />
229+
</Provider>,
230+
wrapper,
231+
root
232+
);
233+
runRoutes();
234+
270235
const container = options.container
271236
? document.querySelector(options.container)
272237
: document.body;
273-
container.appendChild(iframe);
274-
/* There's a certain case where we might have called setStyle before the iframe was ready.
275-
Make sure we take the last style and apply it */
276-
if (queuedIframeStyle) {
277-
iframe.setAttribute("style", queuedIframeStyle);
278-
queuedIframeStyle = null;
279-
}
238+
const shadowRoot = document.createElement("div");
239+
container.appendChild(shadowRoot);
240+
shadow = shadowRoot.attachShadow({ mode: "open" });
241+
242+
const styles = document.createElement("style");
243+
styles.textContent = modalCSS.toString();
244+
shadow.appendChild(styles);
245+
shadow.appendChild(wrapper);
280246
}
281247

282248
export default netlifyIdentity;

0 commit comments

Comments
 (0)