Skip to content

Safe-area-inset CSS variables

The platform exposes four CSS variables on every iframe's :root:

VariableDefaultNotes
--poe-safe-area-inset-topenv(safe-area-inset-top)Notch / status bar clearance
--poe-safe-area-inset-bottomenv(safe-area-inset-bottom)Home indicator clearance
--poe-safe-area-inset-leftenv(safe-area-inset-left)Landscape rounded-corner clearance (left)
--poe-safe-area-inset-rightenv(safe-area-inset-right)Landscape rounded-corner clearance (right)

Use these in place of raw env(safe-area-inset-*) whenever you pad against the viewport edge. The kernel injects the defaults automatically. When the parent app overlays UI on top of the iframe (e.g. a top bar, bottom tab bar, split-view divider), it overrides the relevant variables so the child does not double-pad behind the parent's chrome.

Consuming the variables

Always include an env(...) fallback for non-platform hosts (third-party embedders, dev servers):

css
.bottom-bar {
  padding-bottom: var(
    --poe-safe-area-inset-bottom,
    env(safe-area-inset-bottom)
  );
}

.top-header {
  padding-top: var(--poe-safe-area-inset-top, env(safe-area-inset-top));
}

/* Inside a tailwind arbitrary value */
class="bottom-[calc(1rem+var(--poe-safe-area-inset-bottom,env(safe-area-inset-bottom)))]"

Wrap with max(..., 0px) only when you already wrapped the bare env(...) value that way — var(...) may evaluate to a negative computed value in pathological browser bugs and max(.., 0px) guards against that. Most apps just need the bare var(...).

Declaring overrides for a child app

If your app nests another app via <poe-app> and draws chrome on top of its iframe, declare the corresponding insets so the child does not pad behind your chrome.

html
<!-- Initial value at mount: pass as JSON in an HTML attribute -->
<poe-app
  type-id="chat"
  instance-id="123"
  safe-area-insets='{"top":48,"bottom":56}'
></poe-app>
ts
// Update at runtime via the JS property — beats the HTML attribute.
const el = document.querySelector<HTMLElement>("poe-app")!;
(el as any).safeAreaInsets = { top: 48, bottom: 56 };
// Or use the convenience setter exported from the SDK:
import { setChildSafeAreaInsets } from "poe-apps-sdk/v1/client.js";
setChildSafeAreaInsets(el, { top: 48, bottom: 56 });
// Clear the override (child resumes device env() defaults):
setChildSafeAreaInsets(el, null);

Each side is a CSS px length (number). Sides omitted from the object fall through to the device env(safe-area-inset-<side>) default — partial overrides are supported. Values are validated at the platform boundary: non-finite, negative, or values above 2000px are dropped.

Runtime updates are applied without remounting the child iframe — they flow as a poe:safe-area-insets postMessage to the child's contentWindow, which the child SDK applies inline. Use this for chrome that changes during the session (sidebar open/close, keyboard transitions, orientation flips).

What not to do

  • padding-bottom: env(safe-area-inset-bottom); — ignores parent-app overlays.
  • ❌ Setting per-side 0 and the others omitted to "force" no inset — omitted sides already fall through to the device default. Set 0 only when you specifically want to override that side to zero.
  • ❌ Posting poe:safe-area-insets messages directly from app code — use the <poe-app> setter or setChildSafeAreaInsets. The element knows which iframe contentWindow to target.