# Menus and navigation

## Where menu items come from

Create items in the site's Navigation panel. Assign a menu to `header` or `footer`, enter labels and destinations, arrange the order and save. Do not package customer menu IDs. These locations work with Loom and the application's shared header.

## Ready-to-use Loom element

```html
<gs-menu location="header" label="Main navigation" class="theme-menu"></gs-menu>
<gs-menu location="footer" label="Useful links"></gs-menu>
```

The element renders `nav`, a `ul` and anchors. `label` names the navigation for screen readers. Do not nest it inside another `nav`. It does not create pages or menu entries. In editor preview, links do not leave the builder. Public rendering applies the site prefix and custom-domain rules. An assigned empty menu stays empty; without an assignment, public rendering can fall back to page links.

The shared site header still displays the main navigation. Use this element for additional navigation in a section or footer; adding `gs-menu` does not disable that header. Avoid placing duplicate menus next to each other.

## Custom markup and styling

```html
<ul class="secondary-links">
<template gs-repeat="item in menus.footer">
  <li><a href="[[ safe_url(item.href) ]]" gs-text="item.label"></a></li>
</template>
</ul>
```

`menus.header` and `menus.footer` expose `label` and `href`. Text is escaped automatically. Do not prepend a domain: the renderer already resolves destinations. Snippets receive the `menus` context without extra arguments.

```css
.theme-menu ul { display:flex; flex-wrap:wrap; gap:1rem; padding:0; list-style:none; }
.theme-menu a:focus-visible { outline:2px solid currentColor; outline-offset:4px; }
```

## Before publication

Test empty menus, long labels, narrow screens, keyboard focus and reordering. Check an internal page, `mailto:`, `tel:` and HTTPS links. Never store `javascript:` destinations. Add the blog archive to Navigation after creating the blog page: enabling the plugin does not decide its position in the menu. Test both a custom domain and a renderer URL with a site prefix.

## Dropdowns, nested navigation and megamenus

Shared GS Loom menus support **5 levels**, 30 siblings per branch and 100 items in total per menu. `children` contains items in the same format. `menu_layout: "mega"` lays out immediate children as columns; `dropdown` renders a list. Each item supports a library icon or `image:<URL>` selected/uploaded through the file manager.

Open **Navigation** in the builder, expand an item and use **Add child item**. Every level has a label, link, icon and submenu layout. Editing the site menu enables `use_site_menu` in GS Loom settings. Paylio uses the shared header; Skeleton and Victorie retain original demo navigation until this option is enabled, preserving existing websites during updates.

```html
<gs-menu location="header" label="Menu"></gs-menu>
```

```json
{
  "label": "Explore",
  "href": "#",
  "menu_layout": "mega",
  "icon": "star",
  "children": [{
    "id": "services",
    "label": "Services",
    "href": "/services",
    "icon": "globe",
    "children": [{ "id": "design", "label": "Design", "href": "/design" }]
  }]
}
```

Submenus open on click or Enter/Space; Escape closes the current level and restores focus. When a parent has its own URL, that link appears first inside its panel, so opening a submenu does not navigate. On mobile, nested panels expand vertically. Theme tokens: `--gs-menu-background`, `--gs-menu-color`, `--gs-menu-border`, `--gs-menu-radius`, `--gs-menu-gap`; defaults inherit GS Loom/header tokens. Test long translations, touch, keyboard and desktop/tablet/mobile. Icons, URLs and layout remain language-independent.

Try the working dropdown and megamenu in [GS Loom components](../components/#live-component-examples).
