Title-Matched Mega Menu With a Full-Depth Link-List Fallback
Shopify gives a navigation item no settings of its own. This menu joins each top-level link to a theme-editor block by matching their titles, and renders a full link-list panel for every item that has no block behind it.
Shopify's link lists carry no per-link settings, so a nav item can't own theme-editor fields. This mega menu joins the two admin surfaces on the only thing both of them hold — the title a merchandiser typed. A theme-editor block whose title matches a link becomes that link's dropdown, and a link with no block behind it still opens a three-level panel built from the link lists themselves.
Fact Strip
- Client: Nudestix
- Surface: Global — the site-wide header
- Templates recorded: five, including the home page, campaign story and press pages, and the bundle collection
- Navigation depth rendered: three levels
- Complexity: High
- Attribution: Deploi-authored, inside a theme based on Dawn 6.0.2. The block types, the title-match loop and the mega-menu snippets are ours; the header section that declares them is shared with the theme's own header work.
- Status: Live, verified 2026-09-06
- Evidence: Moderate — see How We Know
The Problem
On a color-cosmetics store the menu is a merchandising surface, not a signpost. Marketing wanted every top-level navigation item to be able to become a different kind of thing — a plain list of links for one, a grid of image-and-copy cards for another, a live collection strip for a third, a multi-collection layout for the fourth — and to change which is which during a campaign.
That last clause is the whole requirement. A holiday drop reorganizes the menu. A shade launch adds a panel and retires one. If each of those is a Liquid edit, the menu moves at the speed of a deployment queue, and the merchandising calendar doesn't wait for one.
The Constraint
Shopify's link_list object carries no per-link settings. A navigation item is a title, a URL and its children, and it cannot own theme-editor fields — there is nowhere on it to hang an image, a heading or a chosen collection. Dawn's mega menu reflects that honestly: it renders the link list, and that is all it can do.
The deeper problem is that menus and blocks live in two different places in the Shopify admin. Menus are built in Navigation. Blocks are configured in the theme editor. Nothing joins a record in one to a record in the other — there is no foreign key, no picker that reaches across, no id shared between them. So a join has to be made out of something both surfaces already have, and the only thing both have is the title a merchandiser typed.
What We Built
header-mega-menu.liquid is where the join happens. It loops the menu's links, and for each link it loops section.blocks looking for one whose title setting downcases to the same string as the link title. On a match it renders that block's layout into a .megamenu-dropdown panel and sets megamenuFlag. The block decides what the panel is; the nav item decides where it appears. Panel layouts are declared as block types on the header section, so adding a panel to a nav item is adding a block in the theme editor and typing a title into it.
When no block matches, the loop falls through to a generic panel built from linklists[link.handle] and linklists[sublink.handle] — three levels of navigation assembled straight from the link lists themselves. A nav item with no block behind it still gets a usable dropdown, which turns out to be the load-bearing part of the design.
Collection-backed block types don't render inline. They delegate to their own snippets with collections[block.settings.collection] passed in, so the products in a menu panel come from a live collection rather than from a list someone has to remember to update. One branch of the navigation — the "nude world" section — is rendered by header-nude-world-menu.liquid straight from link.links, without a block at all.
The menu is also instrumented. A delegated click listener on .menu__list pushes a mega_menu_click event into window.dataLayer carrying the first- and second-level navigation names, reading the parent item from a data-main-menu-item attribute stamped onto every sub-link. Delegation is what makes it survive panels the theme renders through different snippets.
Why This Way
Title matching is not elegant, and it's the only join Shopify offers between the Navigation admin and theme-editor blocks. Everything else on the table required one of the two sides to stop being merchandiser-editable — hard-coding panels per handle in Liquid, or moving navigation out of Shopify entirely. Matching on the title keeps both sides where the people who use them already work.
The fallback is the other half of the same decision, and it's the part we'd defend first. A menu is not a component you get to ship half-configured: it renders on every page, in front of every shopper, before anyone has opened the theme editor. So the unconfigured state had to be a working dropdown rather than an empty panel, which is why the fall-through builds three levels out of linklists and not out of anything a merchandiser has to set up.
The bill lands on the images. Panel artwork goes through a hand-authored <picture> with five media-query sources instead of the theme's own image helper, because a panel's width is known at every breakpoint. Those five breakpoints are also five hand-written numbers that stop being true the day the panel layout is retuned.
Why Not an App
Mega-menu apps solve the visual half of this well. What they do with the other half is the problem: navigation moves into the app's own database. The Shopify Navigation admin stops being the source of truth, campaign nav stops living in the theme's version control, and the menu that renders on every pageview arrives with a script in front of it.
There's also a division-of-labor argument. This merchandising team already composes pages out of theme-editor blocks — that's how the product pages and campaign templates work here. A mega menu built the same way is one interface, not a second one to learn, and the panels reuse the theme's own card and collection markup rather than a vendor's.
Implementation Notes
- The join is a downcased title comparison between a block's
megamenu_titlesetting and the nav item'slink.title— a string match, not an id — with amenu_titlesetting doing the same job on the collection-backed variants. - The fallback panel reaches three levels deep using
linklists[link.handle]andlinklists[sublink.handle], so an item with no block behind it still gets a full-depth dropdown rather than a flat list. - Collection panels receive
collections[block.settings.collection]from the block, so what a shopper sees in the menu is the live collection and not a product list somebody has to remember to prune. - Sub-links whose URL is
#are given a pointer-events class, so a placeholder row used as a column heading can't be clicked into a dead end. - Each link's
aria-labelis derived from its own URL — split on/, take the last segment, swap hyphens for spaces, capitalize — with a hand-written exception for the external blog subdomain, where the slug heuristic produces nonsense. - Mega-menu images render through a
<picture>element with five media-query sources at 500 and 800 pixels, rather than the theme's stock image tag, because a menu panel's image is a known size at every breakpoint. - The nav click listener is delegated from
.menu__listand every sub-link carriesdata-main-menu-item, so the parent item's name survives delegation and both navigation levels reachwindow.dataLayertogether.
Edge Cases
- No block title matches the link title, so the item renders the generic link-list panel — the same path an unconfigured nav item takes on day one.
- An empty link list produces no column at all, rather than an empty grid cell holding a heading with nothing under it.
- Placeholder
#links render as non-interactive rows, which is how a column heading gets to look like a heading inside a list of links. - The external blog link carries a hand-written label instead of a derived one, because deriving it from the subdomain would name it after the host.
- The mobile menu is a separate rendering in the drawer rather than a reflow of the desktop panel, so the drawer keeps its native
<details>/<summary>markup instead of inheriting a wide multi-column layout.
Platform Primitives Used
- Liquid section schema and settings — the block settings on the header section are the merchandiser's entire interface to the menu.
- dataLayer — the delegated nav listener pushes
mega_menu_clickwith both navigation levels named. - Responsive images — a
<picture>with five sources, because panel images have known widths.
Where It Runs
The header renders on every page of the storefront, and five templates are recorded against this work — the home page, campaign story and press pages, and the bundle collection. That reach is the reason the fallback panel is the load-bearing piece: whatever the header renders, it renders on every template at once.
What This Demonstrates
- Mega menu and mobile navigation system — merchandiser-composed dropdown panels bound to navigation items, with a link-list panel underneath as the floor.
- Analytics and dataLayer instrumentation — navigation clicks named by level and pushed from the theme rather than inferred from URL patterns downstream.
How We Know
Three files read from the live theme, plus five client-side records — campaign specs and a task register. Those records treat the mega menu as content rather than architecture: which nav item should point where, how link titles should be cased. That is good evidence the menu is merchandiser-configured, and no evidence at all of why the block system is shaped the way it is. Documented from the code; no client-side record of the decision survives.
Related Builds
- Mega-menu header with separate mobile menu and image blocks — Three Ships puts imagery in the menu through section blocks too, but binds them positionally rather than by title.
- Adults/Kids audience-switching header — LUS Brands changes which link list the menu renders at all, one level below the question this build answers.
- Detached mobile drawer with three-level native-details navigation — the mobile counterpart on another storefront, where the drawer is rebuilt rather than duplicated.
The Buy-vs-Build Question
The menu is the one component that renders on every pageview and carries the site's internal link structure, so where it lives is an architecture decision before it's a design one. An app can style a dropdown; it cannot own your taxonomy for you. The full case, including when buying is the right answer, is in the mega menu and navigation build-or-buy page.
Ready to Let Your Merchandisers Own the Menu?
If a campaign menu change currently needs a developer, that's a design decision someone made, not a platform limit. Contact us today and we'll show you where the join goes.