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.
The conversation
Section titled “The conversation”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.
Your side
Section titled “Your side”// 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-Optionsor aframe-ancestorspolicy, addhttps://standard.mode.placeto 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.
Next steps
Section titled “Next steps”- Render themes in your app: the renderer this uses, and how to do the same thing with real accounts instead of a frame.
- The Mode Contract, Rule 5: why the theme goes in the viewer layer.