JustFlows

Documentation is English-only for now. The rest of the site follows your language.

Menus

Admin → Menus is a visual designer: a drag-and-drop item tree, layout and mega-menu presets, per-item styling, visibility rules, and a live preview. The primary menu cannot be deleted.

7 min read

Admin → Menus is a full designer, not a flat link list. CRUD is at /api/menus for administrators and editors. Items can be a custom URL or any CMS content type — pages, posts, products, shop pages, and custom types — and they nest. Saving stores the content-type slug; public menus resolve those links like pages. The primary menu is undeletable.

Public rendering reads menus through jf-cache (menus: prefix) and the `navigation.items` filter, which now runs *after* the host resolves a menu (visibility rules already applied), so appended items sit alongside the author's.

The designer

  • A drag-and-drop item tree with indent / outdent and undo / redo.
  • A live `?preview=1` iframe of the rendered menu.
  • One-click design presets — a layout plus activation, breakpoint, and alignment an author picks instead of configuring from scratch.
  • Edits autosave to a draft_items / draft_design working copy that only the preview path reads; Publish promotes it and clears the draft.

Layout and design contract

Each menu carries a layout / design contract in a design column (migration 0024_menu_designer). A NULL design renders the built-in defaults, so every existing menu is unchanged.

  • Layouthorizontal, vertical, dropdown, multi-level-dropdown, mega, footer, drawer.
  • Activation — hover or click.
  • Alignment and a per-menu mobile breakpoint with a collapse pattern (dropdown, accordion, drawer-right, drawer-left, fullscreen) and enter / exit motion.
  • Depth and items-per-level caps.

Per-item options

A menu item gains a style preset and per-button styling (background / text / border colours validated as safe CSS values, radius, size, full-width), an icon or image (validated as safe asset URLs), a badge, a description line, title text, and extra rel tokens (nofollow, sponsored, ugc, externalnoopener noreferrer is always added for target="_blank").

A mega layout's top-level items hold multi-column regions whose content is authored as blocks, sanitized on write against a fixed safe-block allowlist (no core.html / core.code / core.embed) and rendered through the same block pipeline as page content.

Visibility rules

An item can be shown or hidden by visitor auth state, role, locale, or a plugin-provided condition. The checks are enforced server-side and fail closed — an unknown or deactivated condition hides the item. A menu that uses any auth / role / condition rule bypasses the shared public cache so every request resolves against the real session; the cacheability test itself is cached and invalidated on every menu save, so menus without rules cost nothing extra. Device targeting (desktop / tablet / mobile) is presentation-only, applied with the shared data-jf-devices CSS primitive now emitted into /theme.css for every theme.

Theme front-end

The designer ships a partials/nav-menu.ejs renderer, /js/site-nav.js (desktop flyouts, an off-canvas mobile drawer, keyboard navigation, prefers-reduced-motion), and the .jf-nav styles in the default theme's global.css.

Extending menus

Plugins and themes contribute one-click starting points with the `menu.design.presets` filter (static MenuDesignSeed data, no build(); ids are "<pluginId>:<slug>") and answer an item-level custom condition with `menu.visibility.evaluate`. Append link-shaped items with `navigation.items`. See Hooks and docs/HOOKS.md → Contributing a menu design preset in the CE repository.

Filter navigation in a plugin
ts
ctx.hooks.filter("navigation.items", (items, { location }) => {
  if (location !== "primary") return items;
  return items.filter((item) => item.url !== "/hidden");
});