# Navigation & Mega Menus

## Navigation & Mega Menus

**Files:** `src/config/menu.<language>.json` · **Skill level:** Beginner

The header menu is built from one file per language. You write the links, and the theme handles the dropdown panel, the flyouts, the mobile menu, and keyboard support. For the header layouts and the header button, see [Header](/margo/customization/header/). For the footer link columns and a shorter guide to the menu files, see [Menus](/margo/customization/menus/).

### How The Menu Behaves
On large screens:

- All dropdowns and mega menus share one panel. Moving from one menu to the next, the panel changes width, height, and position in about 300ms, and the content slides in the direction you moved.
- Opening a menu from closed animates it in without that change of shape.
- A nested item opens a flyout beside its list, as deep as your data goes. A flyout that would run off the screen opens on the other side.
- Moving the mouse diagonally from a link to its flyout doesn't close it.

Below 1024 px, the menu button opens a menu card over the page. **Work**, **Services**, and **Pages** open and close inside it with a height animation, the card ends with the header button, your email with a copy button, the social links, and the light/dark and language buttons, and focus stays inside the card while it is open.

Visitors who turn on **reduce motion** in their device settings get a simple fade instead. On Arabic pages everything mirrors: see [RTL & Arabic](/margo/features/rtl-arabic/).

### The Menu File
There is one file for each language: `src/config/menu.en.json`, `menu.fr.json`, and `menu.ar.json`. The header menu is the `headerPrimary` list. The same file also holds the `footer` link columns (see [Menus](/margo/customization/menus/)).

Items show in the order of the file. There are three kinds of item.

**1. A plain link**

```json
{ "enable": true, "name": "About", "url": "/about/" }
```

**2. A dropdown.** Set `"hasChildren": true` and list the links in `children`. A child can have its own `"hasChildren": true` and `children`, which opens a flyout. You can keep nesting, because the depth follows your data. The shipped **Work** menu goes three levels deep: case studies by discipline, then the projects:

```json
{
  "enable": true,
  "name": "Work",
  "hasChildren": true,
  "children": [
    { "enable": true, "name": "All projects", "url": "/projects/" },
    { "enable": true, "name": "Archive", "url": "/archive/" },
    {
      "enable": true,
      "name": "Case studies",
      "hasChildren": true,
      "children": [
        {
          "enable": true,
          "name": "Branding",
          "hasChildren": true,
          "children": [
            { "enable": true, "name": "Marrow Kiln", "url": "/projects/ceramic-forms/" },
            { "enable": true, "name": "Ravel & Hue", "url": "/projects/colour-fan/" }
          ]
        }
      ]
    },
    {
      "enable": true,
      "name": "Photo series",
      "hasChildren": true,
      "children": [
        { "enable": true, "name": "North Atlantic", "url": "/projects/north-atlantic/" }
      ]
    }
  ]
}
```

An item with children opens its menu and has no link of its own. To link to the page it stands for, add it as the first child, as "All projects" does above.

**3. A mega menu.** Set `"hasMegaMenu": true` and list groups of links in `menus`. Groups sit side by side. You can add a `cta` card beside them and a `bar` strip along the bottom of the panel. This is the shipped **Services** menu, shortened:

```json
{
  "enable": true,
  "name": "Services",
  "hasMegaMenu": true,
  "menus": [
    {
      "enable": true,
      "name": "",
      "children": [
        { "enable": true, "name": "Brand identity", "url": "/services/brand-identity/", "description": "Marks, type, and colour" },
        { "enable": true, "name": "Web design", "url": "/services/web-design/", "description": "Calm, fast websites" }
      ]
    }
  ],
  "cta": {
    "enable": true,
    "project": "ceramic-forms",
    "eyebrow": "",
    "image": "",
    "title": "",
    "button": { "enable": false, "label": "", "url": "" }
  },
  "bar": {
    "enable": true,
    "links": [
      { "enable": true, "name": "Pricing", "url": "/pricing/" },
      { "enable": true, "name": "FAQ", "url": "/faq/" }
    ]
  }
}
```

**Pages** is the other mega menu. It has three groups (Homes, Pages, More), no card, and a strip with the site search and a link to `/elements/`:

```json
"bar": {
  "enable": true,
  "links": [
    { "enable": true, "name": "Search", "action": "search", "shortcut": "⌘K" },
    { "enable": true, "name": "Every section layout", "url": "/elements/" }
  ]
}
```

### The Project Card
`cta` shows one project as a card beside the links: its cover, its title, and its year, linking to the project page.

- `project` is the file name of a project in `src/content/projects/<language>/`, without `.md`, such as `"ceramic-forms"`.
- Leave `project` empty to use the first project with `featured: true`. The demo marks `ceramic-forms` as featured.
- `image`, `title`, and `eyebrow` replace the project's cover, title, and year when you fill them in. `button.url` replaces the link when `button.enable` is `true`.
- If `project` matches no file, the card shows only when you fill in `title` and `button.url` yourself.

The card doesn't show `description` or `items`. They are kept so the menu files have the same shape as in other themes.

### The Bottom Strip
`bar` is a strip along the bottom of a mega menu. Its `links` are quick links, shown in a row. A link with `"action": "search"` instead of a `url` is a button that opens the site search, and `shortcut` is the key hint shown beside it on large screens. The search button shows only while `[settings.search] enable` is `true` (see [Search](/margo/customization/search/)).

`bar` also takes `text` (a short line) and `label` with `url` (one link before the quick links). The demo leaves them empty.

### Menu Fields
| Field         | Used on                  | What it does                                                                                    |
| ------------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
| `enable`      | Everything               | `false` hides the item. An item without `enable` shows                                          |
| `name`        | Everything               | The text shown. On a mega menu group it is the small heading, and can be empty                  |
| `url`         | Links                    | `/pricing/` for your own pages (the language prefix is added for you), or a full `https://` address |
| `hasChildren` | Dropdowns, flyouts       | `true` when the item has `children`                                                             |
| `children`    | Dropdowns, mega groups   | The links inside. Items in a dropdown can have their own `children`                             |
| `hasMegaMenu` | Top-level items          | `true` when the item opens a mega menu                                                          |
| `menus`       | Mega menus               | The groups of links. Each group has `enable`, `name`, and `children`                            |
| `description` | Dropdown and mega links  | A short line under the link, on large screens                                                   |
| `cta`         | Mega menus               | The project card: `enable`, `project`, `eyebrow`, `image`, `title`, and `button`                |
| `bar`         | Mega menus               | The bottom strip: `enable`, `links` (each with `enable`, `name`, and `url` or `action`, plus `shortcut`), and the optional `text`, `label`, and `url` |
| `badge`       | Dropdown links           | A small label or dot beside the name on large screens, with `enable`, `type`, `label`, and `color` |

No menu link has an icon, and the header has no field for one. The mega menu groups in the shipped files still have a `weight` number, kept for the standard menu shape. The header doesn't read `weight` or `icon`: to move an item, move it in the file.

<Aside type="caution">
  Change the menu file of **every language** when you add or remove a link, so
  the menus stay in step. JSON is strict: a missing comma or a comma after the
  last item stops the site from building.
</Aside>

### Keyboard Support
This works with no setup. Keep it working if you change the markup.

| Key                 | What it does                                       |
| ------------------- | -------------------------------------------------- |
| `Enter` or `Space`  | Opens the menu on the focused item                 |
| `Down` and `Up`     | Move through the links in the open list            |
| `Left` and `Right`  | Move between menus and in and out of flyouts       |
| `Home` and `End`    | Jump to the first or last link in the list         |
| `Tab`               | Moves into the panel, in reading order             |
| `Escape`            | Closes the menu and returns focus to where it was  |

On Arabic and other right-to-left pages, `Left` and `Right` swap, so they follow the reading direction. Moving focus out of the menu closes it. In the mobile panel, `Escape` closes the panel and returns focus to the button that opened it.

The menu components are in `src/layouts/components/global/header/`.

### Do It With AI
```text
Add a mega menu called "[NAME]" to the header of my Margo site. Use the menu files in src/config/,
one for each language listed in src/config/language.json (menu.en.json, menu.fr.json,
menu.ar.json). Add it to headerPrimary with "hasMegaMenu": true and [NUMBER] groups in "menus",
linking to these pages: [PAGES]. Keep the same item shape as the existing items. Translate the
names for each language. Don't change the navigation components. Then run npm run astro-check.
```