gobj-ui 5.4 · C_YUI_SHELL · ROUTING

Getting back

A section of cards is the list → detail pattern: a grid where each card enters one element. The question is not how you get in, it is how you get back — and there is only one decision to make: where the reader's position lives. Everything else follows from it.

in the url Deep-linkable, survives a reload and a shared link, and the browser's Back button works without writing any code. This is the default.
in memory A drill-down that floats above a resting view and disappears with it. Not deep-linkable — and it should not be.

That axis, and no other, is the one that decides. It is settled by the Litmus in ROUTING.md §3: is this a position ("what am I looking at?") or something transient floating above one? No side channel — localStorage, a flag in priv — gets to answer on the url's behalf where the user is.

A note on names

stack, back and path are not three ways to navigate. They are the three values of nav_mode, an attr of C_YUI_NODE since gobj-ui 5.4.0, and all three live inside the url: they pick how the way back is drawn in a node tree — stacked strips, a single , or one breadcrumb line — not where the position lives.

"stack" in particular means stacked strips in the url, and has nothing to do with the C_YUI_PAGER's in-memory stack, which is precisely the mechanism that is not in the url.

1 · in the url

The route is the truth

The shell's canonical model — intent → url → view.

#/reports/sales/q3 declared route subpath: the view owns it C_YUI_NAV layout: cards the view EV_ROUTE_CHANGED

Every card is a real <a href="#/reports/sales">. The click does not touch the view: it changes the hash, and the shell's hashchange mounts the destination. Back and Forward walk the same path, so they are correct by construction — and the link can be shared, bookmarked and opened in a new tab.

  • The grid is a C_YUI_NAV with layout:"cards", which the shell synthesises from submenu.index on a level-1 menu item.
  • On mobile that same index adds a backbar (show_on:"<tablet") instead of repeating the tab strip; turn it off with index:{backbar:false}. It is still a link to a fixed destination, not a history pop.
  • The view owns its deep levels through the subpath: the shell resolves down to the nearest declared ancestor and hands over the tail. An empty subpath means "its home".
// a human chose it → push (the default) yui_shell_navigate(shell, "/reports/sales") // code decided it (redirect, normalise) → replace yui_shell_navigate(shell, r, {replace: true})
2 · in the url

The node tree

C_YUI_NODE — one declared route, free depth underneath.

/admin/treedb/db/data 1 declared route all of this is subpath admin treedb db link → the view

A shell submenu paints two levels. When a section has more, the node tree solves it the other way round: it declares one route and everything below arrives as subpath, at whatever depth is needed. Each node paints its own share of the way back and hands its child the tail of the url, already trimmed.

  • A link node is where the structure ends: from there the subpath belongs to the view, which receives it as EV_ROUTE_CHANGED {route, base, subpath} — the same contract the shell gave it, so the view cannot tell who mounted it.
  • Which is why, to talk to the shell from inside a tree, you resolve it with yui_shell_of(gobj): the parent is no longer the shell, it is the node.
  • chrome_depth caps how many strips are painted; the deepest declaration on the path wins.
// the whole tree reads the mode from its ROOT yui_node_set_nav_mode(root, "path") yui_node_nav_mode(node) // → "stack" | "back" | "path"

The three shapes of one way back

nav_mode does not change where the position lives — it stays in the url. It changes how it is drawn. It is a filter applied when the renders are asked for, never a rewrite of what the app declared; that is what makes going back to "stack" an exact restore, branch by branch. It belongs to the root: set on a middle node it is refused with a log.

nav_modeWhat it drawsChrome stripsEquivalent declaration
stack One strip per ancestorthe default whatever each branch declares — (it is what was declared)
back The tip's parent only, as ← parent 1 chrome: {layout:"backbar"} on every branch
path The whole trail on one line (breadcrumb) 0 path: {layout:"breadcrumb"} on the root
3 · in memory

The page stack

C_YUI_PAGER — a transient drill-down.

page · root page · list page · form ▸ top EV_PUSH_PAGE EV_POP_PAGE none of this is in the url

The pager keeps a LIFO priv.stack in memory: EV_PUSH_PAGE pushes a panel and EV_POP_PAGE takes it off. Its navigates the stack and nothing else; popping past the root emits EV_PAGER_EXIT so the host can close (governed by the back_on_root attr).

  • It is not a route, and must not be: it floats inside a C_YUI_WINDOW, a modal, or inline. Not deep-linkable, and it does not survive a reload.
  • No Confirm/Cancel chrome: the content saves itself (a C_YUI_FORM, for instance), with an optional discard per page.
  • Container-agnostic: the host mounts its $container wherever it wants and listens for EV_PAGE_SHOWN {id, depth}.
EV_PUSH_PAGE { id, title, content, discardable? } EV_POP_PAGE / EV_BACK // at the root → EV_PAGER_EXIT EV_REPLACE_PAGE · EV_DISCARD_PAGE

How to choose

Two questions settle it. The third only comes up when there is real depth.

1

Does it float above the view and vanish with it?

Yes → C_YUI_PAGER. Keep it out of the url: a deep-linkable overlay is the rare exception, not the rule.

2

Is it a position — must it survive a reload, a shared link, and Back?

Yes → the url. Give it a segment and make arriving a push. This is the default case.

3

And does it go more than two levels below the section?

C_YUI_NODE: one declared route, the rest as subpath. That is where you pick nav_mode, which is presentation — the position stays in the url under all three.

The three, side by side

DimensionThe urlThe node treeThe stack
Where the position lives url urlone route + subpath memorypriv.stack LIFO
Deep link · reload · share YesYesNo
Browser Back Full history Full historyinto the tree as well Not involvedits ← is internal
Component C_YUI_NAVcards · backbar · breadcrumb C_YUI_NODE C_YUI_PAGER
Depth As declared, route by route Free, under a single route Free, and none of it is a position
Nature (Litmus) PositionPositionTransient