# Advanced Menu Widget

> Navigation menu widget with 8 visual skin variants — top-level horizontal/vertical menu plus dropdown submenu styling. Only Pro widget that uses Elementor's skin system.

**Class file:** `includes/Elements/Advanced_Menu.php` (363 lines)
**Slug:** `advanced-menu` (widget id `eael-advanced-menu`)
**Public docs:** <https://essential-addons.com/elementor/docs/ea-advanced-menu/>
**Pro-shared:** Pro-only. No Lite counterpart.

## Overview

Renders a configurable navigation menu sourced from a WordPress nav-menu (`wp_get_nav_menu_items`). Top-level items expand into dropdowns; submenu depth and trigger (hover/click) are user-configurable. The widget itself is a thin shell — most controls and rendering live in **8 skin classes** under `includes/Skins/Skin_Default.php` through `Skin_Seven.php`. The skin system is the distinguishing architectural choice — each skin has its own controls and render branch, while the parent widget owns shared concerns (which menu, dropdown behaviour).

## Pro vs Lite

Pro-only. Lite has no menu widget; no upsell teaser exists for this widget.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Advanced_Menu.php` | Widget shell — `get_name`, `register_skins`, shared controls |
| `includes/Skins/Skin_Default.php` | Default skin — controls + render branch |
| `includes/Skins/Skin_One.php` ... `Skin_Seven.php` | Seven additional skin variants |
| `src/css/view/advanced-menu.scss` → `assets/front-end/css/view/advanced-menu.min.css` | All-skin styles |
| `src/js/view/advanced-menu.js` → `assets/front-end/js/view/advanced-menu.min.js` | Dropdown interaction (hover/click trigger, mobile toggle) |
| `config.php` entry `'advanced-menu'` | Single self CSS + self JS dep |

## Architecture

- **Skin-based composition** — see [`_patterns.md § Skin registration`](_patterns.md#skin-registration). Eight skins registered in `register_skins()` (lines 75–84). The widget id used in skin hook registration is `eael-advanced-menu` (NOT `advanced-menu`) — skins hook `elementor/element/eael-advanced-menu/{section-id}/before_section_end`.
- **`has_widget_inner_wrapper` opt-out** — line 64: `return ! Helper::eael_e_optimized_markup()`. When EA's "Optimize markup" site setting is on, Pro suppresses the extra Elementor wrapper div. See [`_patterns.md § Optimized markup`](_patterns.md#optimized-markup).
- **Skin id persistence** — each skin's `get_id()` returns a kebab-case string (`'skin-default'`, `'skin-one'`, ..., `'skin-seven'`). These ids are persisted in `_elementor_data`. Renaming any skin id breaks every user's saved menu — see [`skins-system.md` rule](../../.claude/rules/skins-system.md).
- **Shared controls in parent, skin-specific in skins** — the parent widget likely owns "Source menu" / "Submenu depth" / "Mobile breakpoint" while each skin owns its own colour / typography / hover-state controls. Verify before refactoring.

## Render Output

```html
<nav class="eael-advanced-menu eael-advanced-menu--{skin-id}">
  <ul class="eael-advanced-menu__list eael-advanced-menu__list--top">
    <li class="eael-advanced-menu__item [?] eael-advanced-menu__item--has-children">
      <a href="...">Top-level Item</a>
      [?] <ul class="eael-advanced-menu__list eael-advanced-menu__list--sub">
        <li class="eael-advanced-menu__item">
          <a href="...">Submenu Item</a>
        </li>
      </ul>
    </li>
  </ul>
</nav>
```

Conditional `[?]`:

- `--has-children` modifier when item has submenu
- Sub `<ul>` rendered only when current item has submenu items

Each skin can re-template — verify per-skin `render()` for variations.

## Controls Reference (parent widget)

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| `eael_advanced_menu_menu` | Content → General | SELECT | Pick WordPress nav menu |
| `eael_advanced_menu_layout` | Content → General | SELECT | Horizontal / vertical |
| `eael_advanced_menu_breakpoint` | Content → General | NUMBER | Mobile breakpoint (px) |
| `eael_advanced_menu_trigger` | Content → Settings | CHOOSE | `hover` / `click` for dropdowns |

Per-skin controls are skin-namespaced — e.g. `eael_advanced_menu_skin_one_*` — to avoid collisions when switching skins. Verify the namespacing convention is consistent across all 8 skins (today's risk: drift over time).

## Conditional Dependencies

```text
eael_advanced_menu_layout = 'horizontal'
  └── triggers responsive controls for mobile breakpoint behaviour

eael_advanced_menu_trigger = 'click'
  └── enables click-outside-to-close handler in JS
```

Skin-specific dependencies live inside each skin class — not enumerated here.

## JavaScript Lifecycle

`src/js/view/advanced-menu.js`:

```js
var AdvancedMenuHandler = function( $scope, $ ) {
    var $menu = $scope.find( '.eael-advanced-menu' );
    if ( ! $menu.length ) return;

    var trigger = $menu.data( 'trigger' );       // 'hover' | 'click'
    var breakpoint = $menu.data( 'breakpoint' );

    // Wire up hover or click handlers
    // Mobile toggle
    // Submenu open/close
};

jQuery( window ).on( 'elementor/frontend/init', function() {
    if ( eael.elementStatusCheck( 'eaelAdvancedMenuLoad' ) ) return false;
    elementorFrontend.hooks.addAction(
        'frontend/element_ready/eael-advanced-menu.default',
        AdvancedMenuHandler
    );
} );
```

Standard `eael.elementStatusCheck` guard prevents double-init.

## Hooks & Filters

### Elementor hooks consumed (in skins)

| Hook | Skin | Purpose |
| --- | --- | --- |
| `elementor/element/eael-advanced-menu/eael_advanced_menu_section_general/before_section_end` | All skins | Inject skin-specific general controls |
| `elementor/element/eael-advanced-menu/eael_advanced_menu_section_style_menu/before_section_end` | All skins | Inject skin-specific menu styles |
| `elementor/element/eael-advanced-menu/eael_advanced_menu_section_style_dropdown/before_section_end` | All skins | Inject skin-specific dropdown styles |
| `elementor/element/eael-advanced-menu/eael_advanced_menu_section_style_top_level_item/before_section_end` | All skins | Top-level item style |
| `elementor/element/eael-advanced-menu/eael_advanced_menu_section_style_dropdown_item/before_section_end` | All skins | Submenu item style |

### Pro / EA hooks emitted

None visible. Widget is self-contained.

## Common Issues

| Symptom | Likely cause | Diagnose | Fix |
| --- | --- | --- | --- |
| Switching skins resets some controls | Skin-namespaced controls don't share state | Inspect saved `_elementor_data` — skin controls live under different keys | Expected behaviour. Document for users; cannot share value without sacrificing per-skin styling. |
| Dropdown doesn't open on mobile | `breakpoint` data attribute mismatch with CSS media query | DevTools: check `data-breakpoint` value against CSS `@media` | Sync the JS data-attribute with the SCSS breakpoint variable. |
| Submenu appears under other elements | `z-index` from theme overrides | Inspect computed z-index | Bump `z-index` on `.eael-advanced-menu__list--sub`. |
| Skin renamed in user content disappears | Skin id was renamed in code (violation of skins-system rule) | Check `_elementor_data` for `skin: 'skin-eight'` etc. | Revert the rename. Skin ids are user content. |

## Known Limitations

- **8 skins = 8 render paths to keep in sync** — adding a generic feature requires editing 8 files. Refactor target: shared base class for skins.
- **No keyboard navigation** documented — verify `aria-*` attributes and tab handling for accessibility audit
- **No mega-menu mode** — submenu rendering is single-column. Mega-menu would need a new skin or a separate widget.
- **WPML menu translation** — relies on WPML's nav-menu translation; verify `wpml_object_id` filter usage on menu IDs if WPML-touched.
- **Mobile breakpoint is per-widget**, not synced with theme breakpoints — content can desync from theme media queries.

## Cross-References

- Skins rule: [`.claude/rules/skins-system.md`](../../.claude/rules/skins-system.md)
- Shared patterns: [`_patterns.md`](_patterns.md)
- Pro-Lite bridge: [`docs/architecture/pro-lite-bridge.md`](../architecture/pro-lite-bridge.md)
