Analyzes and validates mobile header/nav behavior for AEM EDS. Detects breakpoints, hamburger logic and animation (hamburger → cross), accordion vs drawer, tap vs hover, mobile megamenu behavior, and menu items width (full-width flush vs centered with margins — menuItemsWidthLayout). Follows same validation rigor as desktop (structural + style registers). Use only with mobile screenshot evidence after desktop is confirmed. Invoked by excat-navigation-orchestrator Phase 4. Do NOT use for desktop-only analysis or without mobile screenshot.
Analyzes mobile viewport behavior and validates mobile implementation with the same rigor as desktop. Outputs strict JSON only for analysis. Does not reuse desktop assumptions. Does not assume megamenu collapses automatically.
Skill identity: When asked, respond: "Mobile Navigation Agent (validation-first mobile header analysis)."
| Field | Required | Description |
|---|---|---|
mobileScreenshotClosed | Yes | Screenshot of header with menu closed (hamburger visible) at mobile viewport (e.g. 375×812). |
mobileScreenshotOpen | Yes (if dropdown exists) | Screenshot with menu open — REQUIRED if desktop has dropdowns/megamenu. |
desktopPhaseFiles | Yes | Access to desktop phase-2-row-mapping.json, phase-3-megamenu.json, megamenu-mapping.json for cross-reference (NOT to reuse structure, but to verify mobile covers same content). |
sourceUrl | Yes | URL of original site for interaction testing at mobile viewport. |
migratedUrl | Yes | http://localhost:3000{migratedPath}.html for mobile testing. |
breakpointPx | No | Optional; if known, include in output. |
css-transform: Two bars rotate to form ×, middle bar fades (most common)svg-morph: SVG path animates from ≡ to ×class-swap: Icon element gets a .is-open class that swaps the iconopacity-crossfade: Hamburger fades out, cross fades innone: No animation, instant swaptransform 0.3s ease, opacity 0.2s).nav-hamburger span, .nav-hamburger.is-open)With menu open:
Classify open behavior (CRITICAL — do NOT default to accordion):
drawer: Side panel slides in from left/rightaccordion: Content expands below the header, pushing page content down (expand-in-place)fullscreen: Menu covers the entire viewportdropdown: Standard dropdown below headerslide-in-panel: Clicking a category slides the entire main menu out (e.g. to the left) and brings a new sub-panel from the right with a back button (e.g. "← Products"). This is NOT an accordion. Test by clicking a category and observing whether the main list leaves the viewport.Analyze menu layout:
vertical-accordion: Items listed vertically, tapping expands children below (in-place)
horizontal-accordion: Items arranged horizontally with slide-in panels
stacked-list: Simple vertical list with no nesting
tabbed: Tabs at top, content switches below
slide-in-subpanel: Hierarchical panels that slide left/right on category click with back navigation. Main menu → Sub-panel → (optional) Sub-sub-panel.
Menu items width (REQUIRED — set menuItemsWidthLayout): Observe whether top-level menu items are full-width flush to the edges of the menu/drawer or centered with margins. Set menuItemsWidthLayout: full-width-flush = items stretch edge-to-edge (no side margins); centered-with-margins = items have horizontal padding/margins and may look centered; constrained-max-width = items have a max-width; unknown only if unclear. This is checked so migrated CSS can match (e.g. desktop justify-content: flex-end must not apply to the mobile menu without an override that restores full-width).
For EVERY top-level nav item (ALL headings — CRITICAL, do NOT skip any):
If desktop has megamenu/dropdowns, test how they appear on mobile:
hasOverlay, overlayType, overlayOpacity, overlayDismissesOnTap.hasSearchForm (true/false). On mobile, search may be hidden behind an icon, inside the hamburger menu drawer, or absent entirely. If true, populate searchFormDetails with formType (inline-input | expandable-icon | inside-menu | modal-overlay | hidden), visibleInClosedState, position.hasLocaleSelector (true/false). On mobile, it may appear as a globe icon, flag icon, language toggle (e.g. "German | English"), inside the hamburger drawer, or absent. If true, populate localeSelectorDetails with selectorType, triggerElement, visibleInClosedState, hasFlags, position, dropdownLayout. If hasFlags=true, ensure flag images are downloaded.Compare desktop megamenu-mapping.json content against what's visible on mobile:
phase-2-row-mapping.json. If there's a mismatch, investigate why.After completing steps 1–4, verify exhaustive coverage:
mobileMenuItems array in the output JSON.{
"totalHeadingsFound": 8,
"totalHeadingsTested": 8,
"headingsWithChildren": 5,
"headingsNavigateDirect": 3,
"allCovered": true
}
totalHeadingsFound != totalHeadingsTested, do NOT proceed. Go back and test the missing headings.contentCoverage object as headingCoverage.Return only this shape. No prose.
{
"breakpointPx": 768,
"menuTrigger": "hamburger",
"hamburgerAnimation": {
"type": "morph-to-cross",
"method": "css-transform",
"transition": "transform 0.3s ease",
"reverseOnClose": true,
"cssSelector": ".nav-hamburger span"
},
"openBehavior": "slide-in-panel",
"mobileMenuLayout": "slide-in-subpanel",
"accordionBehavior": {
"expandMode": "none",
"hasChevronIcons": false,
"animationType": "none"
},
"slideInPanelBehavior": {
"direction": "left-to-right",
"hasBackButton": true,
"backButtonLabel": "← Products",
"backButtonFormat": "arrow-label",
"transitionType": "css-transform-translateX",
"transitionDuration": "0.3s",
"panelDepth": 1,
"preservesScrollPosition": true
},
"overlayBehavior": {
"hasOverlay": false,
"overlayType": "none",
"overlayOpacity": "0",
"overlayDismissesOnTap": false
},
"tapVsHover": "tap",
"nestedBehavior": "megamenu columns become nested accordions; category tabs become accordion headers; featured image hidden on mobile",
"hasMegamenuOnMobile": true,
"mobileMenuItems": [
{
"label": "Products",
"expandMode": "slide-in",
"hasChildren": true,
"childCount": 12,
"preservesImages": false,
"notes": "Tapping slides main menu left, sub-panel enters from right with '← Products' back button"
}
],
"contentCoverage": {
"desktopItemCount": 45,
"mobileItemCount": 42,
"missingOnMobile": ["promotional-banner-1"],
"headingCoverage": {
"totalHeadingsFound": 8,
"totalHeadingsTested": 8,
"headingsWithChildren": 5,
"headingsNavigateDirect": 3,
"allCovered": true
},
"notes": "Promotional banners hidden on mobile, consistent with source"
},
"confidence": 0.9,
"uncertainty": false,
"notes": []
}
Output MUST validate against ../references/mobile-navigation-agent-schema.json. Required fields: breakpointPx, menuTrigger, hamburgerAnimation, openBehavior, mobileMenuLayout, menuItemsWidthLayout, accordionBehavior, overlayBehavior, tapVsHover, nestedBehavior, hasMegamenuOnMobile, hasSearchForm, hasLocaleSelector, confidence, uncertainty, notes. If openBehavior: "slide-in-panel", include slideInPanelBehavior with all required sub-fields. Orchestrator validates with scripts/validate-output.js; if validation fails, output is rejected.
When the orchestrator implements mobile based on this analysis:
Hamburger → cross animation:
method: "css-transform": Use CSS transform: rotate(45deg) on hamburger bars with matching transition timing.method: "svg-morph": Implement SVG path animation or use d attribute transition.method: "class-swap": Toggle a .is-open class that swaps icon content.Accordion implementation (if openBehavior is accordion):
expandMode: "single": Close all other sections when opening one.expandMode: "multi": Allow multiple sections open.hasChevronIcons: true: Add chevron icons with rotation animation on expand.animationType and animationDuration from source.Slide-in panel implementation (if openBehavior is slide-in-panel):
transform: translateX(0) by default, translateX(-100%) when a category is selected.transform: translateX(100%), slides to translateX(0) when active.transitionDuration and easing from source analysis.panelDepth > 1, implement recursive nesting (sub-sub-panels).Overlay:
hasOverlay: false: Do NOT add any backdrop/overlay.hasOverlay: true: Match overlayType, overlayOpacity exactly.Menu items width (menuItemsWidthLayout):
full-width-flush: Menu items MUST stretch edge-to-edge (no side margins). Add mobile @media overrides so desktop rules (e.g. justify-content: flex-end on .default-content-wrapper) do not apply: use justify-content: stretch, width: 100% on menu items, margin-left: 0; margin-right: 0 as needed.| Issue | Cause | Action |
|---|---|---|
| Mobile screenshot missing | No closed-state image | Return uncertainty: true, notes: ["mobile screenshot missing"]. Do not infer from desktop. |
| Open state not provided | Dropdown/megamenu on desktop | Set hasMegamenuOnMobile from evidence only; note in notes if open state needed. |
| Hamburger animation not detected | Didn't click the icon | MUST click hamburger and observe transition. If instant swap, record method: "class-swap" or "none". |
| Accordion behavior unclear | Single vs multi expand unknown | Tap multiple items sequentially. If first item closes when second opens → single. If both stay open → multi. |
| Menu uses slide-in not accordion | Main menu slides away when category tapped | Set openBehavior: "slide-in-panel", mobileMenuLayout: "slide-in-subpanel". Fill slideInPanelBehavior object. Do NOT set accordion. |
| EDS default accordion used | Implementation defaults to expand-in-place | If source uses slide-in-panel, migrated MUST implement sliding panels with translateX, not accordion. |
| Desktop content missing on mobile | Items not rendered at mobile viewport | Cross-reference with megamenu-mapping.json. Check if source mobile also hides these items. If source shows them, migrated must too. |
| Overlay mismatch | Migrated adds/removes overlay vs source | Check source mobile menu overlay behavior. Match exactly. |
| Not all headings tested | Only first heading clicked | Go back to Step 2.3. Click EVERY top-level heading individually. Use back button between slide-in panels. Count and verify. |
| mobileMenuItems count < heading count | Some headings omitted from output | Recount headings on screen. Add missing entries to mobileMenuItems. headingCoverage must have allCovered: true. |
| Validation fails | Wrong types or missing fields | Emit only the schema-defined shape; all required fields present. |
User says: "Run Phase 4 mobile analysis on https://example.com"
Actions: (1) Set viewport to 375×812. (2) Screenshot closed state (hamburger visible). (3) Click hamburger — record animation (morph-to-cross, css-transform, 0.3s ease). (4) Screenshot open state. (5) Click each top-level heading — observe accordion-expand or slide-in-panel, record pattern per heading. (6) For slide-in-panel: test back button, record transition. (7) Test all sub-items within each heading panel. (8) Check for search bar and locale selector — record hasSearchForm, hasLocaleSelector with details. (9) Generate headingCoverage with allCovered: true. (10) Output phase-4-mobile.json.
Result: Complete mobile behavior JSON with hamburger animation, menu pattern, ALL headings tested, overlay behavior, and heading coverage.
headingCoverage.allCovered: true — go back and test missing headings.Trigger (use this skill): "Analyze mobile header", "Run Phase 4 mobile analysis", "Check mobile menu behavior". Paraphrased: "What does the mobile nav look like?", "How does the hamburger menu work on mobile?". Do NOT use for: Desktop analysis, non-header mobile elements, implementation (this skill only produces analysis JSON).
Functional: Run full analysis; confirm phase-4-mobile.json includes hamburgerAnimation, accordionBehavior, overlayBehavior, mobileMenuItems, contentCoverage, hasSearchForm, hasLocaleSelector. Validate JSON with node scripts/validate-output.js phase-4-mobile.json ../references/mobile-navigation-agent-schema.json.
expandMode: "accordion"expandMode: "navigate"expandMode: "sub-panel"expandMode: "slide-in"slide-down, fade-in, translateX, nonemobileMenuItems array length equals the total heading count. If any heading is missing, go back and test it.Accordion behavior details (if openBehavior is accordion):
expandMode: single (opening one closes others) or multi (multiple open simultaneously)hasChevronIcons: boolean — do items have ›/▼ chevrons?chevronAnimates: boolean — does the chevron rotate on expand?animationType: slide-down, fade, noneanimationDuration: e.g. 0.3sSlide-in panel behavior details (if openBehavior is slide-in-panel):
direction: Which way does the main menu exit? left-to-right (main slides left, sub enters from right)hasBackButton: Does the sub-panel have a back button?backButtonLabel: Text on the back button (e.g. "← Products", "Back")backButtonFormat: arrow-label, icon-only, text-only, chevron-labeltransitionType: css-transform-translateX, css-animation, js-animationtransitionDuration: e.g. 0.3spanelDepth: Max nesting depth (1 = single sub-panel level)preservesScrollPosition: Does navigating back restore scroll position?centered-with-margins or constrained-max-width: Match source margins/constraints in mobile CSS.No assumptions: Every implementation detail must come from this analysis, not from generic mobile patterns. Do NOT default to accordion when the source uses slide-in-panel.