Mobile & responsive
Several nodes adapt to narrow screens. All of it is opt-in on the model and degrades gracefully without JavaScript.
Tables → stacked cards
Set stackOnMobile and a wide table becomes, on a narrow screen, one card per
row with Column: value lines (the header row is hidden; each cell prints its
column name):
- Java
- JSON
UiTable.of("products", "Products")
.column(UiColumn.of("name", "Name"))
.column(UiColumn.of("price", "Price"))
.row(Map.of("id", "p1", "name", "Widget", "price", "€ 19.00"))
.stackOnMobile(true);
{ "type": "table", "id": "products", "stackOnMobile": true, "columns": [...], "rows": [...] }
Live, in a frame deliberately kept below 640px — so the media query is in
effect and you see the real thing, not a sketch of it:
The header row is gone; every cell prints its own column name.
Each <td> carries a data-label (the column label) that the CSS shows via a
::before, so it works in both the SSR and SPA output and needs no script.
Tabs → wrap or a "⋯ More" menu
Tab bars wrap onto more rows by default when they don't fit. For a
single-row bar that collapses the overflow into a dropdown instead, set
tabOverflow: MENU:
UiSection.of("main", null)
.section("overview", "Overview", overviewBody)
.section("orders", "Orders", ordersBody)
// …many tabs…
.tabOverflow(UiSection.TabOverflow.MENU);
It works out of the box: mount your page through a SuiEventBus and the tabs that
don't fit collapse into a trailing ⋯ dropdown on their own — it re-computes on
resize. Without JS the bar simply wraps, so it stays usable either way.
const bus = new SuiEventBus(renderer, root);
renderer.mount(page); // tab overflow is handled automatically
Button bars → wrap or a "⋯ More" menu
The button bar of a list's or table's header and the footer of a form or a
detail wrap the same way by default. A button's label is always one line, so
a bar that runs out of room wraps whole buttons — it never breaks "Back to the
list" over three lines inside one. For a bar that stays one row and folds the
buttons that do not fit into the same ⋯ dropdown the tabs use, set
actionsOverflow(Overflow.MENU):
UiList.of("mail", "Inbox")
.action(UiAction.secondary("back", "Back to the list"))
.action(UiAction.secondary("assistant", "Assistant").icon("sparkles"))
.action(UiAction.primary("write", "Write").icon("plus"))
.actionsOverflow(Overflow.MENU);
The buttons fold from the end, so put the one that must stay visible first.
In a header the bar shares the row with the title while it fits; on a phone
it takes a row of its own under the title, measured against the full width,
and a search box in the header (headerExtra) keeps its own row below. A
footer's links stay where they are; only its buttons and menus fold. Without
JS the bar wraps, as before.
Navigation menu
The sidebar UiMenu has its own responsive story — a RESPONSIVE mode that is
a push rail on desktop and an overlay drawer on mobile. See
menu.
As a normal consumer — no. The SuiEventBus watches its root and runs these
enhancers itself after every render, so tab overflow, menu-button popovers and the
sidebar's persisted collapse state all just work once you've mounted through a bus.
The functions stay exported only as an escape hatch for the rare case of rendering without a bus — e.g. progressively enhancing a static, server-rendered page with a sprinkle of JS but no full SPA runtime. Then you'd call the one you need once after the markup is in the DOM. If you use the bus (the usual case), you can forget they exist.