Shopify Builds>Three Ships>Mega-menu header with separate mobile menu and image blocks

Mega-Menu Header With a Separate Mobile Menu and Image Blocks

A second link-list setting gives the mobile drawer its own information architecture, and section blocks put imagery into panels that Shopify's link lists have no way to carry it in.

Shopify link lists carry no imagery and the header renders on every page, so an image-rich menu has to come from section blocks and stay cheap. On Three Ships' licensed Palo Alto theme we added a second link list for mobile, image and image-triple blocks, and a sizes calculation that derives each menu image's real rendered width from the panel it lands in.

Fact Strip

  • Client: Three Ships
  • Surface: Global — the header section group, on every page
  • Mounted by: one section group, group-header.json
  • Settings on the section: 23
  • Block types: three
  • Complexity: High
  • Attribution: Built on top of Palo Alto 5.8.0 by Presidio Creative, a paid premium theme the client licenses. This header is our modification of that theme, not a header we authored. The base header, its markup conventions and its animation library are the vendor's.
  • Status: Live, verified 2026-09-06
  • Evidence: Moderate — see How We Know

The Problem

Three Ships sells skincare organized three ways at once — by concern, by ingredient and by format — and a shopper arrives holding one of those three questions. A menu that answers them needs pictures: a serum next to the word serum, a collection tile that shows what a routine looks like. A list of blue links makes every branch of the catalog look the same.

The second problem is that the structure which works on a wide desktop hover panel is the wrong structure in a phone drawer. On desktop, "Shop" wants to be a multi-column panel. On mobile, it wants to be tabs, with different items promoted and some desktop entries renamed or dropped entirely. And through all of it, the header has to be able to spotlight one entry — a sale, usually — for the length of a promotion and then stop.

The Constraint

Shopify link lists are the only navigation data structure on offer, and they carry no imagery. A menu item is a title and a URL. So every picture in the menu has to come from somewhere else: either a section block a merchandiser fills in, or the linked object itself, which is the only place a collection's or product's image can be found without new data entry.

The header is also a section-group section, so it renders on every page and everything it does is paid site-wide: every setting evaluated, every heading counted in the page's structure. Transparent-header mode has to keep working over every kind of first section beneath it.

And the header is licensed code. Palo Alto 5.8.0 is a paid premium theme with its own upgrade path and conventions, so this was an extension made in place. Replacing it would have discarded both the vendor's updates and the settings surface the merchandising team already knows.

What We Built

The mobile split comes first, because it's the structural decision. A mobile_main_menu link-list setting lets the drawer render a different menu from the desktop nav — its own item order, its own naming, its own depth — with an independent show_images_mobile toggle deciding whether that menu carries pictures at all. Two menus, both edited in Shopify Navigation, no responsive gymnastics trying to make one structure serve two contexts.

header-block.liquid renders the image mega-menu block, and the interesting part of it is the sizes attribute. A menu panel's column count isn't fixed: it depends on how many children the nav item has, how many image blocks sit at that position, and whether the block is flagged wide. So the snippet computes the image's real rendered width from those three facts rather than guessing a value, which is what lets the browser pick the right source for a panel whose layout is only known at render time. Each block also carries its own heading and text sizing, color, border radius and an aspect-ratio choice — square, portrait, wide or adapt.

header-block-triple.liquid adds an image_triple block: three image cells in a gapless grid, each with its own image, title, description and link, with configurable gap and maximum width. It's driven by indexed setting keys read through block.settings[key], so one block type does the work of three.

header-link-image.liquid covers the case where no block is needed at all. It pulls featured_image off collection_link and product_link menu entries, so a collection or product in the menu shows its own thumbnail with nothing for a merchandiser to upload.

Two smaller pieces finish it. A highlight_item and highlight_item_color setting pair emits a --highlight custom property against one named menu entry, which is how a sale link gets to look like a sale link for three weeks and then not. And the tabbed mobile shop menu runs on shop-tabs.js, a small custom element that switches [data-shop-tab] triggers against .shop-scroll__panel panels, setting is-active along with aria-selected and aria-hidden.

Why This Way

Two link lists instead of one responsive menu is the honest answer to a genuinely different mobile information architecture. Trying to reflow the desktop panel into a drawer produces a menu that is a compromise in both places; giving mobile its own list keeps both merchandiser-editable in the same admin, and lets the mobile menu say "Learn" where the desktop menu says something else.

The sizes calculation is the piece of engineering worth reading twice. Responsive images in a mega menu are usually wrong, because the panel's layout varies per menu item and the developer picks one number and moves on. Deriving the width from sibling counts and the wide flag gets the browser an accurate hint for the panel the image actually lands in.

Both decisions carry a bill. Two menus mean two menus to maintain, and nothing reconciles them — a category renamed on desktop stays as it was on mobile until someone changes it there too, which is the price of letting them differ on purpose. And this is a licensed theme: every modification here is reconciled by hand whenever the vendor ships an update, because it lives inside vendor files rather than beside them.

Why Not an App

Image-rich navigation is one of the most commonly bought Shopify components, and on a store with a simple catalog it's a reasonable purchase.

The arithmetic changes on a section that renders on every pageview. A menu app is a script on every page, a second admin surface for navigation, and a set of panels that don't reuse the theme's cards, aspect ratios or type scale. Built as theme blocks, the panels inherit the design system the rest of the storefront already uses, the menu data stays in Shopify Navigation where the merchandising team edits everything else, and the header ships nothing extra to fetch.

Implementation Notes

  • The header section carries 23 settings and three block types — image, image_triple and mobile_featured_image — and is enabled on the header group rather than placed per template, so it is site-wide by construction.
  • header-block.liquid builds its sizes attribute as a calc() over the container fraction, the panel's margins and gaps and the image count, with the container fraction selected from a child-count by image-count matrix.
  • header-block-triple.liquid loops one to three and reads indexed setting keys through block.settings[key], so a three-cell layout is one block type rather than three near-identical ones.
  • The sticky header reserves a fixed --header-sticky-height and exposes --full-height, so a full-viewport first section can size itself against the space the bar occupies.
  • Transparent-header mode adds top padding to the first section's content across nine different section content selectors — banner, banner rows and columns, banner image, slideshow, split images, video text and countdown — with a separate mobile override.
  • The header renders the nav drawer, and the cart drawer when the cart type is set to drawer and the current template isn't the cart page; the cart toggles carry aria-controls pointing at it.
  • Submenu labels were moved out of heading tags into non-heading elements, because a header section renders on every page and a navigation full of H2s becomes a site-wide heading-structure problem rather than a menu one.
  • shop-tabs.js is a short custom element registered behind a customElements.get guard, so a second registration of the same tag name — from a duplicated script tag or a theme-editor re-render — can't throw.

Edge Cases

  • The logo has separate settings for the home page, every other page and the scrolled sticky state, with heights derived from each image's aspect ratio so it doesn't jump as the header shrinks.
  • Menu images are skipped for link types other than collection and product links, and skipped again when the linked object has no featured image of its own.
  • The highlight color is ignored when it's empty or fully transparent, so a half-configured highlight setting doesn't repaint a menu item.
  • request.design_mode adds a data-header-in-editor hook, so the header can behave one way in the theme editor and another way on the storefront.
  • Cart-drawer toggling is suppressed on the cart template and whenever the cart type is set to page, falling back to a plain link to routes.cart_url rather than a control that opens nothing.
  • Mega-menu image blocks fall back to a placeholder class when no image has been set, so an unfinished block is visible to the merchandiser configuring it instead of collapsing to nothing.
  • The tab switcher sets aria-selected and aria-hidden alongside its class toggles, so the panel state is expressed in the markup and not only in CSS.

Platform Primitives Used

  • Liquid section schema and settings — 23 settings and three block types are the merchandiser's whole interface to the header.
  • Theme blocks — the image and image-triple panels are blocks, added and ordered in the theme editor.
  • Section groups — the header lives in the header group, so one edit reaches every template at once.
  • Custom elements — the mobile shop-tab switcher, with no library behind it.
  • CSS custom properties--highlight, --header-sticky-height and --full-height carry state out of the header and into the page.
  • Responsive images — the computed sizes attribute for panel imagery.
  • Cart AJAX API — the cart drawer the header opens and the count it shows.

Where It Runs

One section group, every template, every pageview. Roughly 1,100 lines across the section and its three snippets — a scale worth stating not as an achievement but as an exposure, since a header this size is the component with the largest blast radius in any theme.

What This Demonstrates

  • Mega menu and mobile navigation system — a desktop mega menu built from section blocks, a mobile menu with its own link list and tab switcher, and imagery derived from the linked object where a merchandiser shouldn't have to supply it.

How We Know

Four files read from the live theme — the header section and three snippets — plus seven client-side records from a task register and a roadmap. Those records schedule the menu refresh and note one consequence of it, that submenu items had to move out of heading tags. Why the header is shaped this way, they don't say. Documented from the code; no client-side record of the decision survives.

That is why the attribution above is worded carefully. On a licensed theme the line between vendor code and our work is not perfectly separable from the code alone. The second link-list setting, the image-triple block, the tab switcher and the highlight settings read as our additions; the base header and its markup conventions do not, and we don't claim them.

Related Builds

The Buy-vs-Build Question

A menu app can restyle a dropdown. It cannot restructure the taxonomy underneath it, and on a store organized by concern, ingredient and format the taxonomy is most of the problem. Navigation also renders on every pageview, which makes it the component where an architecture mistake compounds hardest. The full comparison is in the mega menu and navigation build-or-buy page.

Ready to Give Mobile Its Own Menu?

If your drawer is a squeezed copy of your desktop nav, that's a structure problem wearing a design problem's clothes. Contact us today to talk it through.

More builds