Feature Request: A shared #bricks-builder-iframe-wrapper panel registry (BRX_Common)
To: the Bricks Builder team
From: Alan Blair (BRXProd)
Status: Proposal / Request for Comment
Scope: Builder editor only — no impact on the front end or saved output
TL;DR
There is currently no official, collision-free way for a plugin to dock a UI panel above or below the Bricks preview iframe. Every plugin that wants this invents its own method, and those methods fight each other.
We propose Bricks ship a tiny, official panel registry on a shared window.BRX_Common namespace. A plugin registers its panel element; Bricks inserts it as a sibling of #bricks-builder-iframe-wrapper, observes every registered panel + the iframe for height changes, and keeps the iframe sized to fill the remaining space. One authoritative writer, one source of truth, every plugin compatible by construction.
A complete, drop-in reference implementation is included (brx-common-panel-registry.js). Bricks could ship exactly this, or use it as a starting point.
The problem
A growing number of Bricks add-ons want to place persistent UI in the editor outside the right-hand settings panel — most naturally as a strip below the preview canvas (a CSS editor, a console, a query inspector, a layout bar, etc.). The builder gives us no hook for this, so each plugin improvises. Today there are at least two incompatible patterns in the wild:
-
Shrink the iframe (our approach, Bricks Productivity). We measure our panel’s height and set
#bricks-builder-iframe-wrapper { height: calc(100% - <panelHeight>px) }via an inline style, then keep it in sync with aResizeObserver. The panel sits in normal flow as a sibling of the wrapper, below it. -
Absolute-position the panel + pad the iframe content (Code2Bricks’s approach). The panel is
position: absoluteover the bottom of the canvas, and padding is added to the bottom of the iframe’s content document so nothing hides behind it.
Both work in isolation. Neither composes.
Why it breaks
Because there is no shared owner of the iframe wrapper’s height, the patterns collide the moment two of them are active — or even when one of them meets Bricks’ own chrome:
-
Two “shrink the iframe” plugins both write
#bricks-builder-iframe-wrapper { height: ... }inline. Whoever writes last wins; the other plugin’s panel is either clipped or floats over the canvas. -
“Shrink” + “absolute” double-book the same vertical space: the iframe is shortened and an absolute panel overlaps it, leaving a dead gap or a double-height reservation.
-
“Shrink” vs. Bricks’ / another add-on’s own injected bar. This is not hypothetical. Advanced Themer injects a
#brxcResponsiveBar(~66 px) as an in-flow sibling above the iframe wrapper and compensates with its own CSS rule on the wrapper. Our inlineheightoverrides AT’s rule, sobar + iframe + ourPaneloverflows theoverflow: hidden#bricks-previewcontainer and our panel is clipped off-screen. We can patch around it by measuring sibling heights ourselves — but every plugin re-inventing that arithmetic is exactly the fragility this proposal exists to remove.
The result is a poor experience: users who run two such plugins hit layout bugs that no single plugin author can fix, because the conflict is structural.
What we’re asking for
A small, official panel registry owned by Bricks and exposed on a shared namespace, so that every plugin reserves space the same way:
window.BRX_Common.panels.register(element, { position })insertselementas a sibling of#bricks-builder-iframe-wrapper(above it forposition: 'top', below it forposition: 'bottom'), starts observing it, and keeps the wrapper sized tocalc(100% - Σ registered panel heights).
A single ResizeObserver watches every registered panel; a single MutationObserver catches panels being added/removed. Any height change — a panel resizing, collapsing, expanding, mounting, or unmounting — triggers one coalesced recalculation that writes the wrapper height once. There is exactly one writer of that height, so there is nothing left to fight over.
API surface (proposed)
// Register a panel. Bricks inserts it as a sibling of the iframe wrapper and
// starts reserving space for it. Returns a handle.
const handle = window.BRX_Common.panels.register(myEl, {
position: 'bottom', // 'bottom' (default) | 'top'
id: 'my-plugin-css', // optional stable id; auto-generated if omitted
});
// Stop reserving space and release observers. Bricks recalculates.
handle.unregister();
// or: window.BRX_Common.panels.unregister('my-plugin-css');
// Force a recalculation (rarely needed — observers handle it automatically).
window.BRX_Common.panels.recalc();
// Inspect what's currently registered (debugging / introspection).
window.BRX_Common.panels.list();
// → [{ id, el, position, height }]
Behaviour specification
-
Insertion.
register()places the element as a direct sibling of#bricks-builder-iframe-wrapperinside its parent (#bricks-preview) — before the wrapper fortop, after it forbottom. If the caller already placed the element, its existing position is respected. -
Single height authority. Only the registry writes
#bricks-builder-iframe-wrapper’s height. Plugins never touch it directly. -
Reactive recalculation. A change in any registered panel’s height, or the set of registered panels, recomputes
height: calc(100% - Σ in-flow panel heights)(or100%when the sum is 0). -
In-flow only. Panels that are
display: none,position: absolute, orposition: fixedcontribute 0 — they don’t consume layout space, so they don’t shrink the iframe. (A collapsed panel that’s still in flow naturally reports its collapsed height viaoffsetHeight.) -
Coalesced writes. Observer bursts are batched into one write per animation frame to avoid observer → resize → observer feedback loops and layout thrash.
-
Idempotent bootstrap. The shim no-ops if a registry already exists, so multiple plugins shipping the same bootstrap (until Bricks ships it natively) never clobber each other — first one wins, and Bricks’ own version always wins if present.
Two ways Bricks could implement it
Option A — Flexbox (preferred; no JS height math at all)
If #bricks-preview were display: flex; flex-direction: column and #bricks-builder-iframe-wrapper were flex: 1 1 auto, then registered panels simply take their natural height and the wrapper fills whatever remains — no calc(), no ResizeObserver, no inline height writes. Panels just need a stable order (e.g. CSS order, or DOM position: top panels before the wrapper, bottom panels after). This is the cleanest end-state and eliminates the whole class of “who owns the height” bugs.
The registry API stays identical; only the internal mechanism changes. We suspect this may touch existing builder layout assumptions, so we’re not assuming it’s free — but it’s the ideal and worth evaluating.
Option B — JS calc() (drop-in compatible today)
The reference implementation below uses the calc(100% - Σpx) + ResizeObserver approach because it’s compatible with the builder’s current layout right now and matches what existing plugins already do — it just makes them do it through one shared owner instead of each on their own. Bricks could ship this verbatim today and migrate to Option A later without changing the public API.
Backwards compatibility & migration
-
Additive and opt-in. Nothing changes for plugins that don’t call the registry. The wrapper keeps its current behaviour until something registers.
-
Graceful for existing plugins. A plugin using the old “shrink the iframe” method can switch by replacing its inline-height write + private observer with a single
BRX_Common.panels.register(el, { position: 'bottom' })call and deleting its bespoke arithmetic. -
Community bridge until Bricks ships it. The reference IIFE is written to be idempotent (
if (window.BRX_Common?.panels) return;). Plugins can bundle it today so they cooperate with each other now, and the moment Bricks ships an officialBRX_Common.panels, the bundled copies stand down automatically.
Why BRX_Common?
A neutral, Bricks-owned namespace signals “shared contract between Bricks and third parties,” distinct from any one plugin’s globals. We propose window.BRX_Common as the home for this and future cross-plugin editor contracts (panel registry today; potentially toolbar slots, canvas overlays, etc. later). The exact name is Bricks’ call — the important part is that it’s official and singular.
Reference implementation
A complete, dependency-free IIFE is provided alongside this document: brx-common-panel-registry.js. It implements Option B (the drop-in-compatible path), is idempotent, batches writes per frame, counts only in-flow panels, and exposes the full register / unregister / recalc / list API described above. It is offered for the Bricks team to ship as-is, adapt, or use as a conversation starter.
I just realised I cannot attach a file. Let me kow if you want an example script. We’d be glad to collaborate on refining this into something the whole Bricks add-on ecosystem can build on.