How do I customise the menu HTML?

Mega Menu Builder comes with an out-of-the-box responsive template. You can customise this, or even provide your own set of styles. 

Customize with CSS

If you wish to create your own implementation of a Mega Menu, you can use Mega Menu Builder for menu management and then create your own styles. 

When adding your menu widget with PageBuilder, choose the "No Theme" option. This will add the menu to the page without any styles. It will also not hide your default navigation. 

We provide succinct but descriptive semantic HTML so that you can achieve almost any type of mega menu you like without having to modify the HTML. 

For reference, an example HTML outout would be:

<div class="s48meganav megamenu-visible">
  <span class="menu-back-btn">Back</span>
  <nav class="menu-block nav nav--responsive" data-space48-menu="main-menu">
    <ul class="menu-block__depth-0">
      <li class="menu-block--item menu-block__depth-0--item menu-block__tag- has-children"
        data-url="/kitchen-and-bathroom.html">
        <a href="/kitchen-and-bathroom.html" title="Kitchen & Bathroom"
          class="menu-block--link menu-block__depth-0--link">
          <span class="menu_block--heading menu_block__depth-0--heading">Kitchen & Bathroom</span>
        </a>
        <div class="menu-block--wrapper menu-block__depth-1--wrapper">
          <ul class="menu-block__depth-1">
            <li class="menu-block--item menu-block__depth-1--item menu-block__tag- has-children"
              data-url="/kitchen.html">
              <a href="/kitchen.html" title="Kitchen" class="menu-block--link menu-block__depth-1--link">
                <img src="/content/fitted-kitchens-_be64c3ea1e16c124c43626aca6da3be0f6d13213.webp"
                  class="menu-block--image menu-block__depth-1--image" alt="">
                <span class="menu_block--heading menu_block__depth-1--heading">Kitchen</span>
              </a>
              <div class="menu-block--wrapper menu-block__depth-2--wrapper">
                <ul class="menu-block__depth-2">
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/kitchen-doors.html">
                    <a href="/kitchen-doors.html" title="Kitchen doors"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Kitchen doors</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/kitchen-cabinets.html">
                    <a href="/kitchen-cabinets.html" title="Cabinets"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Cabinets</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/sinks.html">
                    <a href="/sinks.html" title="Sinks" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Sinks</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/taps.html">
                    <a href="/taps.html" title="Taps" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Taps</span>
                    </a>
                  </li>
                </ul>
              </div>
            </li>
            <li class="menu-block--item menu-block__depth-1--item menu-block__tag- has-children"
              data-url="/kitchen-worktops.html">
              <a href="/kitchen-worktops.html" title="Kitchen worktops"
                class="menu-block--link menu-block__depth-1--link">
                <img src="/content/solid-wood-worktops_5653efd305e7cedbb13eab76f44c11a8fc21ecba.webp"
                  class="menu-block--image menu-block__depth-1--image" alt="">
                <span class="menu_block--heading menu_block__depth-1--heading">Kitchen worktops</span>
              </a>
              <div class="menu-block--wrapper menu-block__depth-2--wrapper">
                <ul class="menu-block__depth-2">
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/splashbacks.html">
                    <a href="/splashbacks.html" title="Splashbacks" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Splashbacks</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/laminate-worktops.html">
                    <a href="/laminate-worktops.html" title="Laminate Worktops"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Laminate Worktops</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/solid-wood-worktops.html">
                    <a href="/solid-wood-worktops.html" title="Solid Wood Worktops"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Solid Wood Worktops</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/acrylic-worktops.html">
                    <a href="/acrylic-worktops.html" title="Acrylic Worktops"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Acrylic Worktops</span>
                    </a>
                  </li>
                </ul>
              </div>
            </li>
            <li class="menu-block--item menu-block__depth-1--item menu-block__tag- has-children"
              data-url="/kitchen-appliances.html">
              <a href="/kitchen-appliances.html" title="Appliances" class="menu-block--link menu-block__depth-1--link">
                <img src="/content/kitchen-appliances_591d768e238f85e27922452f1c656d9bec1da49d.webp"
                  class="menu-block--image menu-block__depth-1--image" alt="">
                <span class="menu_block--heading menu_block__depth-1--heading">Appliances</span>
              </a>
              <div class="menu-block--wrapper menu-block__depth-2--wrapper">
                <ul class="menu-block__depth-2">
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/washing-machines.html">
                    <a href="/washing-machines.html" title="Washing Machines"
                      class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Washing Machines</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/dishwashers.html">
                    <a href="/dishwashers.html" title="Dishwashers" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Dishwashers</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/kitchen/ovens.html">
                    <a href="/kitchen/ovens.html" title="Ovens" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Ovens</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/hobs.html">
                    <a href="/hobs.html" title="Hobs" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Hobs</span>
                    </a>
                  </li>
                  <li class="menu-block--item menu-block__depth-2--item menu-block__tag- has-no-children"
                    data-url="/fridges.html">
                    <a href="/fridges.html" title="Fridges" class="menu-block--link menu-block__depth-2--link">
                      <span class="menu_block--heading menu_block__depth-2--heading">Fridges</span>
                    </a>
                  </li>
                </ul>
              </div>
            </li>
          </ul>
        </div>
      </li>
      <li class="menu-block--item menu-block__depth-0--item menu-block__tag- has-no-children"
        data-url="/painting-and-decorating.html">
        <a href="/painting-and-decorating.html" title="Painting & Decorating"
          class="menu-block--link menu-block__depth-0--link">
          <span class="menu_block--heading menu_block__depth-0--heading">Painting & Decorating</span>
        </a>
      </li>
    </ul>
  </nav>
</div><br>

Customize HTML

While we recommend customising your menu with just CSS. It is possible to build your menu and generate your own HTML. It should be considered as an advanced implementation.

Each time a menu is updated, multiple channel metafields are updated with the menu item data.

Menus have to be stored across multiple metafields so that large menus do not hit the metafield character limit.

Metafields are stored in the space48-mega-menu-builder-$name namespace, where $name is the hyphenated version of your menu name. For example, if your menu was called "Main menu" the metafields would be stored within the space48-mega-menu-builder-main-menu namespace.


GraphQL Query

To fetch the metafields using the storefront GraphQL API, the query would look like this:

query metafields {
  channel {
    metafields(namespace: "space48-mega-menu-builder-main-menu") {
      edges {
        node {
          key
          id
          value
        }
      }
    }
  }
}

Your namespace for the metafield is automatically generated based on the name of the menu. The format will be "space48-mega-menu-builder-" followed by a hyphenated/lowercase version of the name. E.g. for a menu called 'Main menu', the namespace would be "space48-mega-menu-builder-main-menu"

Combining the metafields

The menu is split across multiple metafields. The first metafield will include the top level menu items. Then, for each top level menu item that has children, the "tree" of menu items beneath it will be stored in a separate field.

In terms of naming conventions, the top-level menu items will be stored in a metafield with a key that matches your menu name (with spaces replaced with hyphens). For example, a menu called "Main Menu", the first metafield will have namespace space48-mega-menu-builder-main-menu and key main-menu.

If a top-level menu item has children menu items, they will be stored in a separate metafield. The key is based on the menu name with a unique ID suffix. The ID is the id property of the parent top-level menu item.

So, to rebuild the menu from a list of metafields, the pseudo-code would be:

<ul>
  {for each menu items from the top level metafield}
      <li>{menu item}
          {if has children, loop through metafields to find metafield where key matches menu item id}
              {render children menu items}
      </li>
</ul>

In a stencil template, the logic would look something like this:


<div class="s48meganav">
    <nav class="menu-block nav">
        <ul class="menu-block__depth-0">
            {{#forEach _.data.channel.metafields.edges}}
                {{#if node.key '==' 'main-menu' }}
                    <!-- top level menu -->
                    {{#JSONparse node.value}}
                        {{#forEach items}}                                      
                            <li class="menu-block--item">
                                <a href="{{#if url}}{{url}}{{else}}#{{/if}}" class="menu-block--anchor">
                                    <span class="menu_block--heading menu_block__depth-0--heading">{{name}}</span>
                                </a>

                                {{#if ../../../_.data.channel.metafields.edges}}
                                    {{#forEach ../../../_.data.channel.metafields.edges}}
                                         {{#if node.key '==' (concat 'main-menu-' ../id)}}
                                                    {{ render children, e.g. with partial}}
                                         {{/if}}
                                     {{/forEach}}
                                {{/if}}
                            </li>
                        {{/forEach}}
                    {{/JSONparse}}
                {{/if}}
            {{/forEach}}
        </ul>
    </nav>
</div>

Widget

If you're using a stencil theme, we recommend using a widget, even a custom one, as it's the easiest way to ensure that the navigation can be placed on every page without making modifications to multiple templates.

This is our widget template for reference. It uses handlebars partials to reduce the duplicate code required between sections.

{{#partial "sp48Image"}}
    {{#if image}}
        {{#startsWith '/content' image}}
            {{assignVar 'image_path' (strReplace image '/content/' 'webdav:')}}
        {{else}}
            {{#startsWith 'https://' image}}
                {{assignVar 'image_path' image}}
            {{else}}
                {{assignVar 'image_path' (add 'webdav:..' image)}}
            {{/startsWith}}
        {{/startsWith}}

        {{#any ../lazy_load_images ../../lazy_load_images}}
            <img data-src='{{cdn (getVar "image_path")}}' class="menu-block--image {{#if mobile_image}}menu-block--has-mobile-image{{/if}} menu-block__depth-{{depth}}--image lazyload" alt='{{image_alt}}' />
        {{else}}
            <img src='{{cdn (getVar "image_path")}}' class="menu-block--image {{#if mobile_image}}menu-block--has-mobile-image{{/if}} menu-block__depth-{{depth}}--image" alt='{{image_alt}}' />
        {{/any}}
    {{/if}}

    {{#if mobile_image}}
        {{#startsWith '/content' mobile_image}}
            {{assignVar 'mobile_image_path' (strReplace mobile_image '/content/' 'webdav:')}}
        {{else}}
            {{#startsWith 'https://' mobile_image}}
                {{assignVar 'mobile_image_path' mobile_image}}
            {{else}}
                {{assignVar 'mobile_image_path' (add 'webdav:..' mobile_image)}}
            {{/startsWith}}
        {{/startsWith}}

        {{#any ../lazy_load_images ../../lazy_load_images}}
            <img data-src='{{cdn (getVar "mobile_image_path")}}' class="menu-block--image menu-block--mobile-image menu-block__depth-{{depth}}--image lazyload" alt='{{mobile_image_alt}}' />
        {{else}}
            <img src='{{cdn (getVar "mobile_image_path")}}' class="menu-block--image menu-block--mobile-image menu-block__depth-{{depth}}--image" alt='{{mobile_image_alt}}' />
        {{/any}}
    {{/if}}
{{/partial}}

{{#partial "sp48Parent"}}
    {{assignVar "depth_int" depth}}
    <div class="menu-block--wrapper menu-block__depth-{{depth}}--wrapper">
        <ul class="menu-block__depth-{{depth}}{{#if cols}} menu-block__cols-{{cols}}{{/if}}">
            {{#JSONparse node.value}}
                {{#forEach items}}
                    {{#if active}}
                        {{#if customer_groups}}
                            {{#eachIndex customer_groups}}
                                {{#any (if item '==' (getVar "authStatus")) (if item '==' ../../../../_.data.customer.customerGroupId) (if item '==' ../../../../../../_.data.customer.customerGroupId)}}
                                    {{assignVar "renderRow" 1}}
                                {{/any}}
                            {{/eachIndex}}
                        {{#if (getVar "renderRow") '==' 1}}
                            {{assignVar "renderRow" 0}}
                            {{#if (getVar "depth_int") '<' 10}}
                                {{> sp48Children depth=(getVar "depth_int")}}
                            {{/if}}
                        {{/if}}
                    {{else}}
                        {{#if (getVar "depth_int") '<' 10}}
                            {{> sp48Children depth=(getVar "depth_int")}}
                        {{/if}}
                    {{/if}}
                {{/if}}
            {{/forEach}}
        {{/JSONparse}}
    </ul>

    {{#if custom_html}}
        <div class="menu-block__custom-html">
            {{{custom_html}}}
        </div>
    {{/if}}
    </div>
{{/partial}}

{{#partial "sp48Children"}}
    {{assignVar "depth_children_int" depth}}
    <li class="menu-block--item menu-block__depth-{{depth}}--item menu-block--{{visibility}} {{#if cols}}menu-block__colspan-{{cols}}{{/if}} {{#if column_design}}menu-block__column-design-{{column_design}}{{/if}} {{#if wrap_content}}menu-block__column-wrap-content{{/if}} {{tags}} {{#if has_children}}has-children{{else}}has-no-children{{/if}}" data-url="{{#if url}}{{url}}{{else}}#{{/if}}">
        <a href="{{#if url}}{{url}}{{else}}#{{/if}}" {{#if new_tab}}target="_blank"{{/if}}  class="menu-block--anchor {{#if url '!=' ''}}menu-block--link menu-block__depth-{{depth}}--link{{else}}menu-block--no-link menu-block__depth-{{depth}}--no-link{{/if}}" title="{{name}}">
            {{> sp48Image depth=(getVar "depth_children_int")}}
            <span class="menu_block--heading menu_block__depth-{{depth}}--heading">{{name}}</span>
        </a>

        {{#if children}}
            {{assignVar "depth_children_int" (add (toInt depth) 1)}}
            <div class="menu-block--wrapper menu-block__depth-{{getVar "depth_children_int"}}--wrapper">
            <ul class="menu-block__depth-{{getVar "depth_children_int"}}">
                {{#forEach children}}
                    {{#if active}}
                        {{#if customer_groups}}
                            {{#eachIndex customer_groups}}
                                {{#any (if item '==' (getVar "authStatus")) (if item '==' ../../../_.data.customer.customerGroupId) (if item '==' ../../../../../../_.data.customer.customerGroupId)}}
                                    {{assignVar "renderRow" 1}}
                                {{/any}}
                            {{/eachIndex}}
                            {{#if (getVar "renderRow") '==' 1}}
                                {{assignVar "renderRow" 0}}
                                {{#if (getVar "depth_children_int") '<' 10}}
                                    {{> sp48Children depth=(getVar "depth_children_int")}}
                                {{/if}}
                            {{/if}}
                        {{else}}
                            {{#if (getVar "depth_children_int") '<' 10}}
                                {{> sp48Children depth=(getVar "depth_children_int")}}
                            {{/if}}
                        {{/if}}
                    {{/if}}
                {{/forEach}}
            </ul>
        </div>
        {{assignVar "depth_children_int" depth}}
    {{/if}}
</li>
{{/partial}}

{{assignVar "renderRow" 0}}

{{#if _.data.customer '!==' null}}
    {{assignVar "authStatus" -1}}
{{else}}
    {{assignVar "authStatus" -2}}
{{/if}}


<div class="s48meganav">
    <nav class="menu-block nav nav--{{theme}}{{#if mobile_breadcrumb_enabled}} menu-block--mobile-breadcrumb{{/if}}" data-space48-menu="main-menu">
        <ul class="menu-block__depth-0">
            {{#forEach _.data.channel.metafields.edges}}
                {{#if node.key '==' 'main-menu' }}
                    <!-- top level menu -->
                    {{#JSONparse node.value}}
                        {{#forEach items}}
                            {{#if active}}
                                {{#if customer_groups}}
                                    {{#eachIndex customer_groups}}
                                        {{#any (if item '==' (getVar "authStatus")) (if item '==' ../../../../_.data.customer.customerGroupId) (if item '==' ../../../../../../../_.data.customer.customerGroupId)}}
                                            {{assignVar "renderRow" 1}}
                                        {{/any}}
                                    {{/eachIndex}}
                                    {{#if (getVar "renderRow") '==' 1}}
                                        {{assignVar "renderRow" 0}}
                                        <li class="menu-block--item menu-block__depth-0--item menu-block--{{visibility}} {{tags}} {{#if has_children}}has-children{{else}}has-no-children{{/if}} " data-url="{{url}}">
                                            <a href="{{#if url}}{{url}}{{else}}#{{/if}}" {{#if new_tab}}target="_blank"{{/if}} class="menu-block--anchor {{#if url '!=' ''}}menu-block--link menu-block__depth-0--link{{else}}menu-block--no-link menu-block__depth-0--no-link{{/if}}" title="{{name}}">
                                                {{> sp48Image depth='0'}}
                                                <span class="menu_block--heading menu_block__depth-0--heading">{{name}}</span>
                                            </a>

                                            {{#if has_children}}
                                                <button class="menu-block__keyboard" aria-label="submenu toggle">
                                                    <div class='menu-block__keyboard--icon'>
                                                        <span class="visually-hidden">show submenu for {{name}}</span>
                                                    </div>
                                                </button>
                                            {{/if}}

                                            {{#if ../../../_.data.channel.metafields.edges}}
                                                {{#forEach ../../../_.data.channel.metafields.edges}}
                                                    {{#if node.key '==' (concat 'main-menu-' ../id)}}
                                                        {{> sp48Parent depth="1" cols=../cols custom_html=../../../../custom_html}}
                                                    {{/if}}
                                                {{/forEach}}
                                            {{/if}}
                                        </li>
                                    {{/if}}
                                {{else}}
                                    <li class="menu-block--item menu-block__depth-0--item menu-block--{{visibility}} {{tags}} {{#if has_children}}has-children{{else}}has-no-children{{/if}} " data-url="{{url}}">
                                        <a href="{{#if url}}{{url}}{{else}}#{{/if}}" {{#if new_tab}}target="_blank"{{/if}} class="menu-block--anchor {{#if url '!=' ''}}menu-block--link menu-block__depth-0--link{{else}}menu-block--no-link menu-block__depth-0--no-link{{/if}}" title="{{name}}">
                                            {{> sp48Image depth='0'}}
                                            <span class="menu_block--heading menu_block__depth-0--heading">{{name}}</span>
                                        </a>

                                         {{#if has_children}}
                                            <button class="menu-block__keyboard" aria-label="submenu toggle">
                                                <div class='menu-block__keyboard--icon'>
                                                    <span class="visually-hidden">show submenu for {{name}}</span>
                                                </div>
                                            </button>
                                         {{/if}}


                                         {{#if ../../../_.data.channel.metafields.edges}}
                                            {{#forEach ../../../_.data.channel.metafields.edges}}
                                                {{#if node.key '==' (concat 'main-menu-' ../id)}}
                                                    {{> sp48Parent depth="1" cols=../cols custom_html=../../../../custom_html}}
                                                {{/if}}
                                            {{/forEach}}
                                         {{/if}}
                                    </li>
                                {{/if}}
                            {{/if}}
                        {{/forEach}}
                    {{/JSONparse}}
                {{/if}}
            {{/forEach}}
        </ul>
    </nav>
</div>

Don't forget, when creating a widget, you will also need to include your GraphQL query in the `storefront_api_query` field.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us