Skip to content

ADR 0046: The supported viewport floor is 1280x720; above it an operator surface may never require a horizontal scroll, while vertical scrolling is normal, and width reductions key on the pane through container queries rather than on the window

Status: Accepted Date: 2026-08-10 Issue: #1351

Context

Operator stations are built on panel PCs. A 9" panel runs a kiosk browser at 1280x720 with no chrome, and the sizes in the field are 1280x800, 1280x720 and 1024x600 — not the 1920x1080 desktop monitor every gateway surface was designed against.

Until #1351 the gateway had no responsive breakpoints anywhere. The only media queries in the whole stylesheet tree were prefers-reduced-motion and print, and the one width-driven reduction that existed — the audit trail's column shedding from #1055 — was written for a single table and never generalised. Every other surface was narrow-correct only by accident, and there was no stated floor to be correct against.

The accidents were real. A panel-width survey found four tables whose content-driven intrinsic width exceeded the pane they sat in, and in the worst case the overflow escaped the table entirely: Alarm Response is a nine-column table with a 1175px floor against a 972px pane, its region did not scroll, so the page's own scroller took the overflow and the Ack buttons landed 87px past the right edge of the screen. Reaching the most time-critical control in the product meant dragging the pane sideways. The hmi-alarms-active screenshot spec had already been overridden to a 1600px viewport with a comment explaining that the Actions column clips at 1280 — the capture corpus had framed around the defect rather than reporting it.

The vertical axis behaves differently, and a decision on it already exists. The inline equipment faceplate measures 735px expanded, which cannot fit a 720px viewport at any scroll position. That was reviewed on 2026-07-09 for the equipment-faceplate-control clip, and the 940px "everything visible" cut was rejected in favour of the 720 cut precisely because it "looked like nobody's real screen"; the clip scrolls to the sections it reads, the way an operator drives the page. Measured against a seeded stack at 1280x720, every faceplate control — the mode buttons, Run/Stop, the setpoint field, Trend, all three Tag Write buttons — is brought fully on screen by the dashboard pane's 661px of vertical scroll. Nothing is clipped and nothing is unreachable.

So the two axes are not the same problem. A horizontal scroll on an operator surface is a control the operator cannot see and does not know to look for. A vertical scroll is how every page on every screen has worked for thirty years.

Decision

The supported viewport floor for the gateway's operator surfaces — /hmi, /system and /data — is 1280x720 CSS pixels with no browser chrome.

Three rules follow.

  1. At or above the floor, no operator surface may require a horizontal scroll to reach a control or read a value. This is a defect, not a degradation, and it is gated: test/ui/panel-viewport.spec.ts drives Alarm Response at 1280x720 and fails when #mainContent or the table's own box has anything to scroll sideways, or when any element's right edge lands past the viewport.

  2. Vertical scrolling is supported and expected. Content taller than the viewport is normal; a surface is correct when every control is reachable by scrolling its pane, not when everything fits at once. The inline faceplate is the worked example and needs no change.

  3. Below the floor, behaviour is best-effort and ungated. The reductions that exist keep working (Alarm Response holds down to a 513px pane, the audit trail to ~560px, the Data alarm log to 627px), and under those floors the table's own scrollbar takes over. A 1024x600 panel therefore works; it is not something we test or promise.

Width-driven reductions key on the pane, through a container query on the element that wraps the content — never on the viewport through a media query. What constrains a table is the box it sits in, not the window: the System app's sidebar leaves a 1280 viewport 972px while the HMI's leaves it 1040, a 928px marketing capture leaves 620px, and collapsing a sidebar does not change a table's own floor. A viewport media query would be measuring the wrong thing and would fire at the wrong moment in two of the three shells.

A reduction sheds from the low-priority end and states which column may never go. For Alarm Response that column is Actions: acknowledge is the column that must never be the one that scrolls away, so the ladder takes Type, then Message, then Source and Age, and the irreducible row is the alarm, its severity, its two ISA-18.2 state badges and the buttons that act on them.

Height is not a breakpoint axis. No rule here keys on viewport height, and none should be added without superseding this ADR. A max-height media query would re-lay-out 51 committed clip and screenshot specs shot at 720 and 800 — including the faceplate clip whose framing was chosen deliberately — for a fit nobody asked for.

Alternatives Considered

A viewport media query per shell. Simpler to read, and wrong: the same viewport gives three different pane widths across /hmi, /system and /data, so each shell would need its own set of numbers and every future chrome change would silently invalidate them. Container queries ask the question the layout actually depends on. This also follows the precedent #1055 set for the audit trail rather than opening a second mechanism beside it.

Keep the local scrollbar and call the columns reachable. This is what #1045 did for the audit trail, and #1055 overturned it: reachable by dragging is not readable, and for Ack specifically it is not usable either. A control an operator has to discover by scrolling is a control they will not press during an alarm flood.

Make the faceplate fit 720. Rejected. The founder review that chose the 720 cut over the 940 one already settled that a real operator screen scrolls, every control is reachable today, and forcing the fit would mean either shrinking chrome (the setpoint row misses the fold by 3px, so the gain is cosmetic and fragile) or a two-column faceplate under a height-keyed media query, which restyles the whole committed capture corpus.

Declare 1024x600 the floor. Rejected as a promise we would not keep. Nothing tests it, the fixed chrome alone (topbar plus sidebar) eats most of a 1024 width, and stating a floor we do not gate is worse than stating a narrower one we do. Panels at that size still work as well as the reductions allow.

No stated floor; fix surfaces as they are reported. This is the status quo the issue was filed against, and it produced a screenshot spec that documented the bug in a comment and shot around it. A floor is what makes "does this fit?" answerable without a person at a panel.

Consequences

  • Four surfaces gained a column-priority reduction under this rule: Alarm Response (.alarm-table-wrap, both shells), Data > Alarm History (.alarmlog-table-wrap), and the HMI alarm-history overlay (.hmi-ah-table-wrap). The audit trail's #1055 ladder is unchanged and is retroactively an instance of this ADR.
  • Above a 1200px pane, Alarm Response renders exactly as before; every committed capture of it is framed wider than that, so none moves.
  • The hmi-alarms-active / hmi-alarms-all / hmi-alarm-state-matrix specs may drop their 1600px viewport override and return to the 1280 base whenever they are next re-captured — the override is now a framing choice, not a workaround.
  • data-alarm-history (both the screenshot and the clip) currently shows a table that overflows its pane, with the Batch column off-frame entirely. Both want a re-capture against this change; the clip's re-shoot needs founder approval like any other.
  • Machine-token columns break mid-word when a pane squeezes them (Unit/chromatogr / aphy-1). That is the trade #1055 accepted for the audit trail's Resource column, applied consistently here.
  • Adding a column to any of these tables now means placing it on the ladder. A column with no priority assigned is a column that will reintroduce the overflow at panel width.
  • Nothing keys on viewport height, so no clip or screenshot spec shot at 720 or 800 changes for a reason this ADR introduced.