Breakpoint Layout New in 1.0.4
Breakpoint is the layout mode for a screen that should not merely reflow on a phone — it should be a different screen.
Flex adapts one layout: components wrap, resize and re-order, but it is the same set of widgets in the same relationships. Breakpoint replaces one subtree with another. At desktop width the operator sees the trend, the alarm table and the full control strip; at phone width they see three numbers and an acknowledge button. Not the same screen, smaller — a screen you drew for the phone.
That is also its cost: each tier you author is a screen you maintain. Reach for it when the wide and narrow versions genuinely differ, and for everything else use flex.
Tiers
A breakpoint container has a ladder of tiers, ordered smallest first. By default it is the
project's own ladder — the same mobile / tablet / desktop vocabulary the shell, the grid
layouts and the device toolbar already use:
| Tier | Applies at |
|---|---|
mobile | below 600 px |
tablet | 600 – 995 px |
desktop | 996 px and above |
Those two switch widths come from the project (Project Properties → Breakpoints), so changing
them moves the shell, every responsive grid and every breakpoint view together. A container does
not keep its own copy of the ladder, which is why the two can never drift apart.
Each tier has exactly one panel. A panel has no geometry of its own — it fills the container — and the container renders exactly one of them at a time.
A tier is chosen from the container's own measured box, never window.innerWidth. That is what
makes breakpoint containers nest: one inside a narrow side panel switches to its narrow tier while
the page around it is still at desktop.
Owning and mirroring
A panel either owns its subtree or mirrors another tier's.
A mirroring tier stores nothing of its own; it renders the panel it points at. Crossing into it is not a re-render at all — the same subtree stays mounted — so mirroring is free at runtime as well as in authoring effort.
A new three-tier container is seeded like this:
desktop owns its subtree ← what you see first, and draw on
tablet mirrors desktop
mobile owns its subtree ← the purpose-built narrow screen
Two authored screens, not three. That is the shape you almost always want: the phone is genuinely different, and the tablet is a slightly cramped desktop. A two-tier ladder seeds two owning panels and no mirror.
To give a mirroring tier its own layout, click the → mirrors Desktop row in the layer tree — it is a button, and it hands that panel its own empty subtree. Or set mirrors to None (own layout) in the Position panel.
Its tooltip states the thing worth knowing before you click: editing here edits Desktop, and giving this tier its own layout is a separate screen to maintain.
A mirror follows exactly one hop: a legal target is a panel that does not itself mirror. The dropdown is built from the live siblings, so it can only ever offer a legal target — which is why a mirror cycle is not something you can express, rather than something that is checked for afterwards.
Resize the canvas to tablet width and you will often see the desktop screen, because the seeded tablet tier mirrors it. Everything about that is correct, and it reads as a fault.
The layer tree is where the ladder is legible — each tier with the width band it serves, and which one mirrors which:
Mobile < 600px
Tablet 600–995px → mirrors Desktop
Desktop ≥ 996px
That is deliberately the only place a tier is named. An indicator on the canvas would report the active tier while the Position panel reports the selected panel's tier, and at tablet width those legitimately disagree — the panel actually selected is the Desktop one. Two labels contradicting each other is worse than the gap one would close.
To see a different tier, resize: the device toolbar, or the canvas itself.
Authoring
Drilling into a breakpoint container and dropping a widget puts it in the panel that owns the visible tier. Resolved through the mirror: dropping while Tablet is on screen lands the widget in Desktop, which is where it will actually be seen. The same applies to paste.
Inside a panel, widgets are positioned by coordinates — a panel is a canvas, whatever the enclosing view's mode is.
A panel itself cannot be deleted or duplicated. There is one per tier and the container renders by
looking one up, so a missing tier would render nothing and a duplicate would make "which panel
serves this width" unanswerable. Remove a tier by editing the container's tiers instead, which
tells you what that would destroy.
Settings
| Setting | Default | What it does |
|---|---|---|
tiers | the project's | The ladder, smallest first. Set it only for a container that needs its own — ['small','large'] for a simple two-way swap |
followProject | on | Switch at the project's breakpoints. Turn it off only to give this container its own thresholds |
thresholds | — | Custom switch widths in px, ascending, one fewer than the number of tiers. Ignored while followProject is on |
determinant | width | Which dimension picks the tier. See the warning below before choosing height |
hysteresis | 0 | Dead-band in px around every switch width |
transition | none | none, or fade |
mountPolicy | lazy | lazy, keepAlive or eager — see below |
overflow | auto | Standard overflow handling |
padding | 0 | Inner padding, px |
Leaving tiers, followProject and thresholds unset is the normal case, and nothing is written
to the view until you actually diverge from the project.
Hysteresis
Raise it (8–24 px) if the layout flickers when the window rests near a switch width.
That flicker is real and has a specific cause: if two tiers differ in whether they scroll, the scrollbar changes the width that chose them — so the container switches, the scrollbar appears, the width drops back, and it switches again, every frame. A dead-band around each threshold ends it.
Determinant
width is almost always right. height is for a container inside a fixed-height region.
A panel's content determines the container's height, and with determinant: height that height
selects the panel. Content can therefore change the tier that chose it.
Because of that, a minimum 24 px dead-band is applied whatever you set — you cannot take it back to zero. A zero dead-band there is not a preference, it is a live oscillation risk you have no way to see from the property grid.
Transition
none by default, and that is the right choice for an HMI: an operator reading a value should never
wait on an animation. fade is opacity-only (140 ms, no movement, so it cannot shift a number
mid-read) and is for a dashboard where the change is cosmetic. It is disabled automatically under
the operator's reduced-motion setting.
Mount policy
| Policy | Behaviour | Use it for |
|---|---|---|
lazy (default) | Only the active tier is mounted. The others hold no subscriptions and run no scripts | Almost everything |
keepAlive | Mount on first activation, then hide | A half-filled form, or a chart the operator has zoomed — state a swap would otherwise lose |
eager | Mount every tier up front | Rarely |
eager multiplies this view's tag subscriptions by the number of tiersThree tiers of a subscription-heavy screen is three times the realtime traffic, permanently, for two screens nobody is looking at.
Converting a view into or out of breakpoint mode
Changing the layout mode of a breakpoint view rewrites what its root is, so QUBIQ restructures the view rather than just flipping a flag.
Into breakpoint — safe, and nothing is deleted. The root becomes a breakpoint container, the ladder is seeded, and every widget that was at the root moves into the largest owning tier. The view looks unchanged where you are standing, and tablet mirrors it.
Out of breakpoint — only one tier's widgets can survive, because the new root holds them directly and merging three screens would overlap them into nonsense. So the conversion is refused while another authored tier holds work, and it names the tiers and the widgets at risk. Confirm and it proceeds, keeping the largest owning tier.
A mirroring tier never triggers that refusal: it owns nothing, so warning about it would be noise you would learn to click through — which is how a guard stops working.
When not to use it
- The layout only needs to reflow. Use flex. One layout that adapts is one layout to maintain.
- Only a few widgets differ. Bind their
visibleproperty tosession.device.typeinstead of authoring a second screen. - It is a dashboard of tiles. A responsive grid already reflows per breakpoint without a second subtree.
- You would author three near-identical tiers. That is three screens to keep in step. Author two and mirror the middle.
Troubleshooting
| Symptom | Cause |
|---|---|
| Tablet shows the desktop screen | The tablet tier mirrors desktop — the seeded default. The layer tree says so; click that row to give the tier its own layout |
| A widget vanished after dropping it | It landed in the tier that owns the visible one. Check the owning tier's panel |
| The layout flickers near a switch width | Raise hysteresis to 8–24 px |
| The tier changes when data arrives | determinant is height, and content is changing the height. Use width, or raise the dead-band |
| A tier renders empty | Its panel owns an empty subtree — either it never had content, or a broken mirror was cleared on load |
| Switching tiers is slow | mountPolicy is eager or keepAlive on a heavy screen |
A view can arrive older, imported, hand-edited, or half-written by a crash mid-save. On load a missing tier is synthesised mirroring the nearest larger owning tier, a duplicate has its children salvaged into the panel that is kept, a broken mirror is cleared so the panel owns an empty subtree, and an orphaned tier is preserved and reported, never deleted. Authored work is not thrown away to make a document tidy.