Menus — Reference
The Menus surface is the navigation plane for SGEN. It owns every menu a site renders — primary header, footer, mobile drawer, sidebar, in-content navigation strips, plus any custom menus a theme or page references by slug. Anything a visitor uses to navigate the site traces back to a record managed here.
This page is a reference for platform engineers and integrators who need to understand the surface before scripting against it, extending the supported link types, or scoping a theme integration. Customer-facing menu-building walkthroughs live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Menus live under the Menus module in SG-Admin. The module renders three primary views — the menu list, the per-menu editor with drag-reorder canvas, and the menu-location assignment view — and exposes a write surface for menu CRUD, item add and remove, drag-reorder persistence, nested-item creation, location assignment, and per-device visibility rules.
A menu is two things stored together: a definition (the ordered tree of items, each with a label, link, and metadata) and a set of location bindings (which theme regions render this menu — header, footer, mobile, sidebar, custom slot). The two are edited together in the menu editor but the bindings are persisted separately so a single menu can render in multiple locations.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Menus
- URL prefix:
/sg-admin/menus/ - View templates:
application/views/Admin/Menus/
markup written into page content is sanitized at render time — the classes that drive navigation styling are stripped, leaving plain anchor tags. The constraint exists to keep navigation in a structured store the platform can extend later (search, analytics, accessibility audits) rather than scattered across page bodies.Menu items support four link types. Internal items reference an SGEN page, post, custom-object, or store record by id; the rendered URL resolves at render time so a slug change updates the menu automatically. External items carry an absolute URL. Anchor items reference a within-page id and render as a same-page jump link. Action items reference a platform behavior (open the cart, open the search modal, log out the visitor) and render as a button or anchor with the appropriate handler.
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Menus → Primary Header [Save] [Duplicate] │├──────────────────────────────────────────────────────────────────────┤│ Items ││ ────────────────────────────────────────────────────────────────────││ ⋮⋮ Home Internal → page:1 ││ ⋮⋮ Features Internal → page:14 ││ ⋮⋮ Solutions (group, no link) ││ ⋮⋮ For agencies Internal → page:23 ││ ⋮⋮ For in-house teams Internal → page:24 ││ ⋮⋮ For e-commerce Internal → page:25 ││ ⋮⋮ Pricing Internal → page:7 ││ ⋮⋮ Docs External → docs.sgen.com ││ ⋮⋮ Sign in Action → auth.signin ││ ││ Locations: ☑ header ☐ footer ☐ mobile ☐ sidebar ││ [+ Add item] [+ Add group] │└──────────────────────────────────────────────────────────────────────┘Actions
The Menus surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List menus
Returns the menus on the site, paginated, with name, item count, assigned locations, and updated-at columns. Supports column sort, free-text filter, and assignment-state filter (assigned to any location, unassigned).
Create menu
Opens the menu editor with an empty item list. The operator names the menu, adds items, optionally arranges them into groups (parents that have no link of their own but contain sub-items), and binds the menu to one or more locations. On submit the menu persists and the location bindings activate.
Edit menu
Loads an existing menu into the editor. The same operations available on create are available on edit. Submit replaces the stored item tree and updates the location bindings.
Reorder items
Drag-reorder persists as a re-numbering of the items' sort-order field. The operation is idempotent and supports nested drags — an item dragged into a group becomes a child; an item dragged out of a group becomes a top-level sibling. The platform enforces a maximum nesting depth of three levels; an item dragged into a deeper position returns to the maximum depth.
Add item
Adds an item to a menu. The form prompts for the link type and then for the type-specific fields — page picker for internal, URL field for external, anchor-id field for anchor, action-id picker for action. Each item also carries a label override (defaults to the linked record's title), a per-device visibility flag set (visible on desktop, tablet, mobile), and an optional CSS class for theme styling.
Remove item
Removes an item from a menu. Removing a parent that has children removes all descendants; the surface confirms before applying. A removed item is not recoverable from the surface — re-add manually.
Assign menu to location
Binds a menu to a theme location. Each theme exposes a fixed set of locations (header, footer, mobile drawer, sidebar, plus custom slots the theme defines). Multiple menus can be bound to multiple locations in any combination — one menu can render in two locations, and one location can be unassigned (renders nothing). A location with no assigned menu is the controlled way to render no navigation; the alternative — assigning an empty menu — is also supported.
Per-device visibility
Each item carries three flags — visible on desktop, visible on tablet, visible on mobile. The flags default to all-on. An item with all three flags off is retained in the editor but does not render anywhere. The flags are independent of the location binding — a menu can render on mobile while individual items within it are hidden on mobile.
Duplicate menu
Creates a new menu with a copy of the item tree and a copy-suffix in the name. Location bindings do not copy; the duplicate is unassigned by default.
Delete menu
Removes the menu record. The surface confirms before applying. Removing a menu that is bound to a location automatically unbinds the location — the location reverts to no menu and renders nothing until reassigned.
List by location
A read-only view that pivots the data — for each theme location, the surface lists the menu (if any) currently bound. Useful when triaging "the footer is wrong" without knowing which menu drives the footer.
Data model
A menu record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
name | string | Display name. Unique. |
slug | string | URL-safe identifier. Used by themes that reference a menu by slug. |
description | string | Optional. For operator orientation. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on save. |
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. |
menu_id | integer | Foreign key to the menu. |
parent_id | integer | Foreign key to the parent item, or null for top level. |
sort_order | integer | Within-parent ordering. |
link_type | enum | internal, external, anchor, action. |
link_target | string | Type-dependent — record reference, URL, anchor id, or action id. |
label | string | Display label. Overrides the linked record's title when set. |
label_secondary | string | Optional. Sub-label used by themes that render a label and a description. |
target_attribute | enum | _self, _blank. Default _self. |
visible_desktop | boolean | Per-device visibility flag. |
visible_tablet | boolean | Per-device visibility flag. |
visible_mobile | boolean | Per-device visibility flag. |
css_class | string | Optional class for theme styling. |
icon | string | Optional. Theme-specific icon identifier. |
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. |
menu_id | integer | Foreign key to the menu. |
location_slug | string | Theme-defined location identifier — header, footer, mobile, sidebar, plus custom slots. |
assigned_at | timestamp | When the binding was created. |
Internal-link resolution: when an item's link_type is internal, the link_target carries a typed reference such as page:14 or post:217. The URL is resolved at render time by looking up the linked record's current slug. Slug changes on the linked record propagate to the menu automatically — no menu edit required.
Action items: the action link type references platform behaviors by identifier. The supported set includes auth.signin, auth.signout, cart.open, cart.checkout, search.open, language.switcher. Custom action identifiers are not supported in v1 — extension requires a platform-side action handler.
PRIMARY HEADER (menu_id = 1, locations = [header])├── Home link_type = internal, page:1├── Features link_type = internal, page:14├── Solutions link_type = none (group parent)│ ├── For agencies link_type = internal, page:23│ ├── For in-house teams link_type = internal, page:24│ └── For e-commerce link_type = internal, page:25├── Pricing link_type = internal, page:7├── Docs link_type = external, https://docs.sgen.com└── Sign in link_type = action, auth.signinPER-DEVICE FLAGS apply at item level, not menu level."Sign in" can be visible_mobile = false even thoughthe parent menu is bound to a mobile location.Permissions
Access to the Menus surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Menus surface.
Layer 2 — per-action capability. Within SG-Admin, each Menus action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List menus | ✔ | ✔ | ✔ |
| Create menu | ✔ | ✔ | — |
| Edit menu (items, labels, visibility) | ✔ | ✔ | — |
| Reorder items | ✔ | ✔ | — |
| Add item | ✔ | ✔ | — |
| Remove item | ✔ | ✔ | — |
| Duplicate menu | ✔ | ✔ | — |
| Delete menu | ✔ | — | — |
| Assign to location | ✔ | — | — |
| Unbind from location | ✔ | — | — |
| List by location | ✔ | ✔ | ✔ |
Link-target permission cascade. An item that links to a page record (internal link, page:n) does not enforce per-record visibility — the menu item resolves the URL even if the linking operator lacks read access on the page. The constraint is intentional: menus are public-surface data, and the menu editor surface is gated by Editor capability, not per-record capability.
Audit. Every menu create, edit (item add or remove, reorder, label change), duplicate, delete, location assignment, and unbind emits an Activity Log entry. The log records the acting operator, the menu identifier, and the change shape (item-level diff for edits). The audit volume is intentionally light — drag-reorder operations coalesce into a single entry per save, not one per drag.
Theme override. Themes can reference a menu by slug or by location. The location-binding mechanism is the canonical path; the slug path is a fallback for themes that need a stable identifier across deploys. Both paths pass the same permission checks at the surface level.
THEME LOCATIONS (defined by the active theme)┌──────────────────────────────────────────────┐│ header ← Primary Header (id=1)││ footer ← Site Footer (id=2)││ mobile ← Mobile Drawer (id=3)││ sidebar ← (unassigned — no nav) ││ utility ← Account & Help (id=4)│└──────────────────────────────────────────────┘One menu can be bound to multiple locations.One location holds exactly one menu (or none).Bindings are independent of the menu records.DELETING menu id=2 (Site Footer)├── location 'footer' → automatically unbinds└── footer location renders nothing until reassignedRelated references
- Pages — Reference. Internal menu items reference page records by id; slug changes on a page propagate to menus automatically.
- Posts — Reference. Internal menu items can reference posts the same way they reference pages.
- Custom Objects — Reference. Internal menu items can reference custom-object records.
- Settings — Reference. Owns the active-theme selection and exposes the theme's location slugs. Switching themes can change the location list — bindings to locations the new theme does not expose remain stored but render nowhere until rebound.
- Users — Reference. Per-action capability checks resolve against Users; the audit log binds the change to a user id.
- SEO — Reference. Menu items participate in the site's link graph and surface in the sitemap when the SEO module is configured to include navigation links.
/docs/menus.