How the view layer adapts the master-detail composition to different viewport sizes without per-screen layout code.
Responsive behavior in STRVCT is not a separate rendering mode or set of breakpoints. The same master-detail composition that produces a multi-column Miller view on a wide desktop produces a one-column stack-with-breadcrumbs on a phone, because the stack view measures the available width and hides columns that would not fit. The model, the node graph, the tile classes, and the view hierarchy are identical in both cases; only which columns are currently visible changes.
This is the behavior described in the framework's guiding paper as automatic collapsing — when the viewport is too narrow to display the full chain of master-detail views, earlier columns collapse and a breadcrumb bar tracks the navigation path. It falls out of the same primitives used for navigation at any width; there is no separate mobile code path in the view hierarchy.
Two orthogonal pieces of code produce the responsive effect. Understanding them separately makes the overall behavior easy to predict.
SvStackView.compactNavAsNeeded() walks the stack view chain from outermost (leftmost) to innermost (rightmost), summing the target widths of all vertical nav views in the chain against the top-level viewport width (topViewWidth()). For each stack view in order:
uncollapse()d — its DOM element is visible.collapse()d — its DOM element is hidden via display: none and it no longer contributes to layout.Because the walk is ordered and cumulative, it is the earliest columns (closest to the root of the navigation) that disappear first as width shrinks. The current (deepest) column the user is navigated into stays visible as long as possible. Horizontal nav views (e.g., top menus) are skipped in the sum — they are assumed to span the full width and are not subject to the same packing rule.
The chain is re-evaluated on window resize, orientation change, and whenever the stack view structure changes (push, pop, navigation).
Compaction decides which columns are visible. A second method, SvNavView.updateWidthForWindow(), decides how wide the visible columns render.
For any vertical nav view:
targetWidth() >= windowWidth, the column is given width: 100% — it cannot achieve its target, so it takes what it can.windowWidth < targetWidth * 2, the column is given width: 100%. This is the "fill the slack" case: the viewport is wide enough to show one column but not two, so the one visible column stretches to fill rather than leaving dead space to its right.targetWidth().The 2× threshold is chosen so that the transition between "one column fills the viewport" and "two columns sit side-by-side" happens at a natural breakpoint — the first viewport width where a second column could actually fit.
Two node-level hooks let the domain model influence the responsive layout without overriding views:
nodeMinTileWidth() — Returned by a node to declare a minimum width its column should render at. Used by SvNavView.targetWidth() as a floor against the default target. Useful for nodes whose tiles contain content that does not reflow below a certain width (e.g., a wide property inspector).nodeFillsRemainingWidth() — When true, the nav view will skip its fixed target width and stretch to fill available space when it is the last visible column, even outside the 2× stretch case. Useful for terminal views like document editors or chat logs that benefit from arbitrary extra width.Both are queried at layout time, so a node can change its answer dynamically and the stack view will re-evaluate on the next compaction pass.
When columns are collapsed by the compaction pass, navigation back up the chain is provided by a breadcrumb bar rendered outside the stack itself. The breadcrumb view observes the navigation path and renders a selectable entry for each level — tapping an entry navigates the stack back to that level, at which point the deeper columns are popped and compaction re-runs against the shorter chain.
This keeps the stack view's job narrow: it is only responsible for displaying and packing columns. Back-navigation affordances, gestures (swipe-to-pop), and path visualization are concerns of the surrounding views.
STRVCT exposes two different pieces of information about the current environment, and they are not interchangeable:
SvWebBrowserWindow.shared().width() returns the current viewport width in CSS pixels. This is what the compaction and stretch logic consult. It updates on window resize and orientation change. It is the right signal for any layout decision.SvWebBrowserWindow.shared().isOnMobile() returns whether the user-agent string matches a known mobile-device substring (iPhone, Android, etc.). It is device detection, not viewport detection — a narrow desktop browser window is not "mobile," and a tablet in landscape may report mobile even at desktop-class widths.Layout code should prefer viewport width. isOnMobile() is appropriate for gating behavior that is genuinely device-specific — touch-only gestures, iOS/Android platform quirks, permission flows — not for choosing column widths.
A few things developers often expect to find in a responsive system are deliberately not provided by the framework:
SvNavView and SvStackView classes render at every width.targetWidth * 2 comparison for the last-column stretch, which is derived from the column's own target width, not a global constant.Applications that need true breakpoint behavior (e.g., a node that renders differently below a certain width) can observe window resize notifications and change their view or tile class from nodeViewClass() accordingly — the framework will tear down and reconstruct the view on the next sync pass.
docs/Views/Auto-Generated Views — How nav views, tiles, and stack views are discovered and instantiated from model nodes.docs/Views/Custom Views — Overriding view classes for domain-specific presentation.docs/Naked Objects §4.3 — The design rationale for automatic collapsing as a consequence of composable master-detail primitives.docs/Events and Gestures — Swipe gestures (including back-swipe) and touch handling.