Skip to content

Preview a theme on your site

The home page shows a theme on apps we made up. Far more convincing is a theme on a page that’s actually yours, but that only works if your page takes part.

A URL box alone isn’t enough. A page inside an <iframe> can’t be styled from outside it. CSS custom properties don’t cross that boundary, the parent can’t reach into the frame’s document, and most sites refuse to be framed at all. So the framed page has to render the theme itself; the box on the home page only ever hands it one, your page does the rest.

Two messages, both checked by origin:

your page → its parent { source: "mode.place", type: "ready" }
our page → your frame { source: "mode.place", type: "theme", expression, record }

Your page says it’s listening; we send the current theme, and send a fresh one whenever the visitor picks a different look. The record is an ordinary place.mode.standard.theme, the same thing you’d fetch from someone’s account.

// The only origin we'll accept a theme from.
const HOST = "https://standard.mode.place";
// Rule 5: declare the layer order once, anywhere in your CSS.
// @layer app, author, viewer;
// And mark the scope this applies to, e.g. <body data-mode="default">.
const style = document.createElement("style");
document.head.append(style);
window.addEventListener("message", (event) => {
if (event.origin !== HOST) return;
const data = event.data;
if (data?.source !== "mode.place" || data?.type !== "theme") return;
// themeToCss is the reference renderer from "Render themes in your app".
style.textContent = fontFaces(data.record) + themeToCss(data.record, data.expression, "viewer");
document.body.dataset.mode = data.expression;
});
// Tell the host we're listening. (Only meaningful when framed.)
if (window.parent !== window) {
window.parent.postMessage({ source: "mode.place", type: "ready" }, HOST);
}

themeToCss and fontFaces are the four small functions in Render themes in your app; nothing else is needed. Your page ends up with one <style> element in the viewer layer, exactly as if a signed-in visitor had brought their own look.

Two things worth doing while you’re there:

  • Allow the frame. If you send X-Frame-Options or a frame-ancestors policy, add https://standard.mode.place to it, or the frame won’t load at all.
  • Check the origin both ways. The snippet above ignores messages from anywhere else, and only announces itself to us. Do keep both checks: a look can only ever set the standard’s known values to CSS strings, but an unchecked message channel is worth nobody’s trouble.