Find Element Call's root by attribute, not by being the body

Six selectors named `body` directly — the gradient backdrop and the
platform font overrides in index.css, and the iOS adjustments in
AppBar.module.css and Modal.module.css — so they only applied when Element
Call owned the page. A host mounting it into a container would have got an
interface decorated correctly and styled incorrectly, with nothing to show
that anything was wrong.

Mark the root element with data-element-call-root and match on that
instead. Scoping this way keeps the selectors more specific than they were,
rather than less: widening them to a bare [data-platform=…] would have
dropped specificity from (0,1,1) to (0,1,0) and changed which rules win.

The platform attribute moves with them, from the initializer's write onto
document.body to a layout effect on the root, alongside the theme — so it
still lands before anything is painted.

No visual change while Element Call owns the page: the root is the body,
which now carries the attribute, so every rewritten selector matches the
element it always did.
This commit is contained in:
Valere
2026-09-03 12:55:52 +02:00
parent 1bfb018d90
commit 856e055d30
7 changed files with 40 additions and 19 deletions
+9
View File
@@ -9,6 +9,7 @@ import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { useUrlParams } from "./UrlParams";
import { useRootElement } from "./RootElementContext";
import { platform } from "./Platform";
import { useHostBridge } from "./HostBridge";
export const useTheme = (): void => {
@@ -28,6 +29,14 @@ export const useTheme = (): void => {
return (): void => subscription.unsubscribe();
}, [hostBridge]);
// Mark the element as Element Call's root and record the platform on it, so
// that the stylesheets can find both without naming `body`. A layout effect,
// like the theme below, so that it lands before anything is painted.
useLayoutEffect(() => {
rootElement.setAttribute("data-element-call-root", "");
rootElement.setAttribute("data-platform", platform);
}, [rootElement]);
useLayoutEffect(() => {
// If no theme has been explicitly requested we default to dark
const theme = requestedTheme?.includes("light") ? "light" : "dark";