Facia Docs
For AI Open Facia
Build on Facia Beta

The frame bridge

Your page runs in an <iframe> inside Facia. The bridge lets it get the token and theme, and tell Facia when it's ready, when to open the chat, and when to close.

#The frame

html
<iframe sandbox="allow-scripts allow-forms allow-same-origin"
        allow="fullscreen" referrerpolicy="no-referrer" src="https://your-app…/?facia_token=…">
  • No popups and no top-level navigation. Open links inside your page, or use Facia.openChat().
  • Fullscreen is allowed (for games).
  • Facia shows a loading skeleton over the business's background until you call ready().
  • The frame stays alive while the client switches tabs and comes back (up to 3 apps at once), so a game or form doesn't restart.
  • Facia accepts messages only from your frame's window and your page's origin, and posts only to your origin.

#facia-app.js

html
<script src="https://facia.chat/app/facia-app.js"></script>
APIWhat it does
Facia.inFaciatrue when the page is inside Facia
Facia.ready()Removes Facia's loading skeleton. Call it once your content is drawn
Facia.token()The latest token (posted by Facia, or ?facia_token= from the address)
Facia.onToken(fn)Called with each new token, and right away if there is one
Facia.onTheme(fn)Called with { scheme, vars, radius, style } now and on every theme change
Facia.applyTheme(t)Writes the theme's CSS variables onto <html> and sets color-scheme, data-facia-scheme, data-facia-radius and the style attributes
Facia.onInsets(fn)Called with { top, bottom, left, right } safe-area insets in px
Facia.openChat()Opens the client's support chat with the business
Facia.close()Goes back to where the client came from
Facia.resize(h)Accepted and ignored: the frame always fills the tab

The usual start:

js
Facia.onTheme((t) => Facia.applyTheme(t));
Facia.onInsets((i) => (document.body.style.paddingBottom = i.bottom + "px"));
const token = Facia.token();
// …draw…
Facia.ready();

#Theme variables

applyTheme sets these on <html>, so your CSS can use var(--accent):

VariableUse for
--bgPage background
--panel, --surface, --elevPanels, cards, raised surfaces
--hover, --pressHover and pressed states
--borderLines and outlines
--text, --text-2, --text-3Main, secondary and faint text
--accent, --accent-2, --accent-fg, --accent-softPrimary actions, a second accent, text on accent, soft accent backgrounds
--success, --warn, --dangerGood, careful and bad states
--chat-bgThe chat background

radius is soft or sharp. Style for it with [data-facia-radius="sharp"]. scheme is light or dark.

#Matching the style

The theme also says which style the client is in: style: { id, base, outline, grid }. id is classic, futuristic or custom:<id>, and base is classic or future. In Futuristic, and in custom styles built on it, Facia draws everything as lit outlines on a faint grid. These extra variables carry it:

VariableUse for
--fx-deepThe style's background
--fx-surfaceCards
--fx-lineLines and outlines
--fx-gridThe background grid's lines
--gold-500, --win-500Prizes and wins

applyTheme sets data-facia-style and data-facia-base on <html>, plus data-facia-outline and data-facia-grid when they apply. A few rules make your page match:

css
html[data-facia-outline] .card { background: transparent; box-shadow: inset 0 0 0 1px var(--fx-line); }
html[data-facia-outline] .primary { background: transparent; color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
html[data-facia-grid] body {
  background-image: linear-gradient(var(--fx-grid) 1px, transparent 1px), linear-gradient(90deg, var(--fx-grid) 1px, transparent 1px);
  background-size: 30px 30px;
}

In Classic none of these attributes are set, so these rules change nothing.

#Making it feel native

  • Paint --bg from the first frame (html { background: var(--bg) } and a sensible fallback) to avoid a white flash.
  • The frame fills the tab and owns the scroll. Don't add outer scrollbars.
  • Keep the first load small. A plain or Phaser page is instant, while a Unity WebGL build (10–30 MB) shows the skeleton for a while on mobile data.
  • Use --accent for your main action and neutral surfaces for the rest. Gold means VIP in Facia, so avoid it unless you mean VIP.
  • Show the client's alias. Never ask for their name.

#Raw messages

If you'd rather not load the script, speak postMessage directly:

DirectiontypePayload
page → Faciafacia:ready–
page → Faciafacia:openChat–
page → Faciafacia:close–
page → Faciafacia:resize{ height } (ignored)
Facia → pagefacia:token{ token }
Facia → pagefacia:theme{ scheme, vars: { "--bg": "…", … }, radius, style }
Facia → pagefacia:insets{ top, bottom, left, right }
js
const send = (type, extra) => parent.postMessage({ type, ...extra }, "*");
addEventListener("message", (e) => {
  if (e.source !== parent) return;
  if (e.data?.type === "facia:token") useToken(e.data.token);
  if (e.data?.type === "facia:theme") applyVars(e.data.vars);
});
send("facia:ready");

Facia sends facia:token, facia:theme and facia:insets right after facia:ready, and sends facia:theme again whenever the theme changes.

#Testing outside Facia

Open your page directly in a browser. Facia.inFacia is false, there's no theme, and Facia.token() reads ?facia_token= if you add one. For a real token, register the app with a http://localhost address and open it from inside your business.

View as Markdown