Mobile Navigation
How VitNode's menus behave on phones - a floating bottom bar of up to three links you pick in the AdminCP plus a Menu tab for the rest, and a side drawer behind the user button for the account.
On a phone, the desktop header's navigation has nowhere to go. So below the
sm breakpoint (640px) VitNode splits it in two: your most-used links move to a
floating bottom bar pinned to the bottom of the screen - the kind your thumb
already knows from native apps - and everything else lives in a side drawer
behind the user button at the top.
Both are managed in the AdminCP, no code or config required.
Two menus, one screen
Open AdminCP → System → Navigation. It has two tabs, named after where the menu sits rather than the device, because a tablet is happy with the header:
| Tab | Where visitors see it | Holds |
|---|---|---|
| Header | In the header on tablets and desktops, and in the side drawer on phones | Any number of items, with dropdowns one level deep |
| Bottom bar | Pinned to the bottom of the screen, on phones only | Up to three links, no dropdowns, plus a Menu tab |
Each tab works the same way: Add link opens the same picker (a page one of your plugins provides, or a custom link), rows drag to reorder, and the row menu has Edit, Move up, Move down and Remove. A page can sit in both menus at once - Search in the header and in the bottom bar is a perfectly normal choice.
Once the bottom bar holds three links, Add link switches off until you remove one.
The bottom bar
Each link is an icon above a short label. The current page is tinted and sits
on a highlight that slides between tabs. A tab counts as current when the URL is
its address or anything below it, so /blog/my-first-post keeps a Blog tab lit
(/ only matches the home page itself).
The last tab is always Menu. It opens a drawer from the bottom with the rest of the Header menu - everything the bar does not already show, dropdowns included. On a page that lives in there, the Menu tab is the one lit. If the bar already covers every header link, the Menu tab steps aside.
An empty bottom bar is not drawn at all. That is the default on a fresh install, so phones start with just the header and its side drawer until you add a link.
Give every link an icon and a short label
At 320px each of the four tabs gets about 72px, which fits one word like "Discover" or "Settings" - longer labels are cut off with an ellipsis. A link without an icon shows the first letter of its label instead. Titles are per item, so the header can say "Community highlights" while the bottom bar says "Discover".
The side drawer
The user button stays at the top of the header, where it always was. On a phone it shows the member's avatar (or a person icon for guests), and a tap slides a drawer in from the side with:
- the member's name and email, linking to their profile - or Login and Register buttons for guests,
- the full Header menu, dropdowns included - only while the bottom bar is empty, since otherwise its Menu tab already has it,
- the account links from the desktop user menu (profile, files, settings, AdminCP for admins),
- theme and language switches,
- Log out.
Swipe it away, tap outside it, press Esc or hit the close button. Desktop is untouched: the same button still opens the usual dropdown there.
For developers
- API. Both menus live in
core_navigation; alocationcolumn says which (headerorbottom_bar). The admin routes underadmin/navigationtake an optionallocationon/createand/reorderand default to the header, so older clients keep working. The bottom bar refuses a parent (400) and a fourth link (409). - Public data. The middleware response carries the bottom bar next to the
header menu as a flat
bottomBararray, cached separately and expired together with it. - Events.
navigation.reorderedincludeslocation. See Built-in events. - Room at the bottom. While the bar is on screen, the page gets extra bottom padding so it never covers your footer.
- Notches and home indicators. The bar sits above the iPhone home indicator
via
env(safe-area-inset-bottom), which needsviewport-fit=coverin your viewport meta tag. New apps have it; in an older app, update the viewport entry insrc/routes/__root.tsx:
{ content: 'width=device-width, initial-scale=1, viewport-fit=cover', name: 'viewport' },- Translations. The drawer's own words - "Menu", "Navigation", "Account" -
live under
core.global.mobile_nav; the AdminCP tab labels underadmin.navigation.tabs. See Languages & Localization to translate them.