Frame views: any interface code
Build a Block interface with React or another framework, SVG, canvas, drag and timers, run in a guarded opaque frame with StillMade theming and shared editing.
A frame view ("runtime": "frame" in the view) is an interface written with ordinary web code: React, Svelte, Vue or Preact bundles, SVG, canvas and WebGL, pointer capture and drag, timers, loops, recursion, classes and Web Workers. Use it when the reviewed subset of standard views is too small for the interface you want.
Folder layout
my-block/
stillmade.block.json
src/run.js or another runtime (recipe, module, capability)
src/view.html markup, for example <div id="root"></div>
src/view.css styles; use StillMade's theme variables
src/view.js ONE classic script: bundle with --format=iife
src/view.config.json {"runtime": "frame"}
tests/fixtures.jsonsrc/view.config.json may also set "appearance" and "files" (assets, see below). Bundle npm dependencies into src/view.js as an IIFE, for example:
esbuild src/view.jsx --bundle --format=iife --minify --jsx=automatic \
--define:process.env.NODE_ENV='"production"' --outfile=src/view.jsLimits: markup and each stylesheet up to 256 KiB, view.js up to 4 MiB, and up to 32 asset files with 8 MiB in total.
The StillMade object
Frame views get the same StillMade object as standard views: input and onInput, run, useOutput, getShared, onShared, updateShared, bindShared, connectDocument, previewImage, previewMedia, render and the rest of the interface bridge. In addition:
| Member | What it does |
|---|---|
StillMade.asset(path) | A blob: URL for one of the view's asset files, for <img>, new FontFace() or <video>. |
StillMade.assetBytes(path) | A copy of an asset file's bytes as an ArrayBuffer. |
Shared state works as in every view: keep each independently edited value in its own field (for example point0, point1) so two people editing different values never conflict.
Theming
The theme variables from StillMade's design system are always defined: var(--bg), var(--bg-elev-1), var(--fg), var(--fg-muted), var(--accent), var(--border-tok), var(--divider), var(--r-3) and the rest. Colors written with them follow Light, Dark and White automatically. Frame views are not required to use them; the appearance check reports hard-coded colors as advice instead of refusing the view.
Assets
Images, fonts, JSON, audio and video the view needs ship as files named by SHA-256, like module files:
{"runtime": "frame", "files": {"schemaVersion": 1, "files": {
"fonts/inter.woff2": {"sha256": "…", "bytes": 48256, "mediaType": "font/woff2"}
}}}stillmade-block pack fills in the digests and sizes. StillMade checks every file against its SHA-256 before the view starts.
What the frame does not allow
The view runs in an opaque-origin frame with its own policy:
- no network (
fetch, WebSocket and remote images, fonts or scripts fail); - no code from strings (
eval,new Function, inline event attributes) and
no extra script elements;
- no navigation, popups, downloads, forms that submit, or dialogs;
- no WebAssembly on the page (run heavy computation in the Block's runtime,
for example a module Block, and call it with StillMade.run);
- no storage or access to StillMade's page.
Workers created from blob: URLs are allowed and run off the page's thread.
Run limits
StillMade compiles the view so every function and loop checks a time budget. One task may run for 250 ms; past that it is stopped with an error, and a view that keeps the page busy most of the time, stops answering, or navigates its frame is closed with a message. Move long computation into a Worker or the Block's runtime.