segfault labs

Styling

A module renders in its own iframe, so none of the overlay's CSS leaks in. Instead, the host gives you a base stylesheet and a set of theme variables so your module matches the overlay out of the box — and updates live when the user changes theme.

What you get automatically

When a surface mounts, the SDK injects a small base stylesheet so a plain module looks integrated with no effort:

html, body { margin: 0; background: transparent; }
html { overflow: hidden; }     /* windows use overflow: auto instead */
* { box-sizing: border-box; }
body {
  font-family: var(--ps-font, system-ui, -apple-system, sans-serif);
  color: var(--ps-foreground, #e6e6e6);
  font-size: 13px;
  line-height: 1.4;
}
a { color: var(--ps-accent, #3d9dc0); }
/* themed, thin scrollbars are styled too */

So a module that writes plain text already has the right font, colour, and a transparent background that lets the overlay's material show through.

Theme variables

The host resolves the current theme to these CSS custom properties on your iframe root and re-pushes them whenever the theme or opacity changes — so just use them in your CSS and your module re-themes itself.

VariableExample valueUse for
--ps-backgroundhsl(220 6% 9%)the app background colour
--ps-foregroundhsl(0 0% 92%)primary text
--ps-panelhsl(220 6% 13%)a raised surface / card background
--ps-panel-2hsl(220 6% 17%)a secondary / nested surface
--ps-accenthsl(196 52% 50%)accent — links, highlights, primary buttons
--ps-accent-2hsl(262 52% 60%)a secondary accent (gradients, variety)
--ps-mutedhsl(220 6% 60%)muted / secondary text
--ps-overlay-opacity0.92the painted-surface translucency (a unitless number)
--ps-fonta font-family stackthe overlay UI font

Colours arrive as ready-to-use hsl(...) strings — drop them straight into any property:

.card {
  background: var(--ps-panel);
  color: var(--ps-foreground);
  border: 1px solid color-mix(in srgb, var(--ps-foreground) 12%, transparent);
  border-radius: 10px;
  padding: 10px 12px;
}
.card a, .card .accent { color: var(--ps-accent); }
.card small { color: var(--ps-muted); }

color-scheme

The host also sets color-scheme (light or dark) on your <body>, so native form controls and scrollbars render in the right scheme. (It's intentionally on <body>, not the root — putting it on the root would paint a solid canvas and defeat the transparent background.)

Reacting in code

You rarely need to: the SDK applies the variables for you, so CSS that uses var(--ps-*) just works. If you compute styles in JS (e.g. drawing to a <canvas>), read them from ps.theme and subscribe to changes:

function paint() {
  const accent = ps.theme['--ps-accent'] ?? '#3d9dc0'
  // ...draw with `accent`...
}
paint()
ps.onThemeChange(paint) // fires on theme / opacity change

Sizing

You don't set a surface's size directly — the host does, based on kind:

  • Widgets and settings auto-size to their content. The SDK runs a ResizeObserver on <body> and reports the content size, so the surface grows/shrinks to fit. Lay out your content naturally; avoid fixed 100vh/100vw heights.
  • Windows fill the host window body, which the user can resize. Use height: 100% and let your content scroll (windows get overflow: auto).

Typing and focus

When a text field inside your iframe gains focus (input, textarea, or contenteditable), the SDK tells the host so the click-through overlay accepts keystrokes — you get working text entry for free, no extra code.

Tips

  • Keep backgrounds transparent or translucent (var(--ps-panel) over the overlay material reads best); a fully opaque fill looks heavy floating over a game.
  • Use color-mix(in srgb, var(--ps-foreground) N%, transparent) for subtle borders/dividers that adapt to the theme instead of hard-coding greys.
  • Respect the user's setting to keep motion subtle — avoid large, constant animations in a widget that sits over gameplay.