So I have multiple categories which have their subcategories and which have their subcategories etc etc.. So right now, when I'm navigating through vertical-menu, and hover over a category, the subcategories always display AT THE TOP of the menu. Even if the parent category was way down on the menu. I want subcategories to open on the same height as the parent category. I added both text and picture to demonsrate the problem.

Here's how the menu works at the moment:

 - menu________subcategory
 - menu________subcategory2
 - menu
 - menu
 - opened menu

And here's how I want it to work:

 - menu
 - menu
 - menu
 - menu
 - opened menu|subcategory
 - ____________subcategory2

enter image description here

What do I have to do?

1 Answer 1


I wish it was as simple as changing some CSS, unfortunately it's not...

In _navigation.less, Magento sets the top property of the submenu elements as 0 !important. It does this within the 'lib-main-navigation-desktop' mixin.

To change this, your theme has to overwrite _navigation.less. You do this by copying vendor/magento/theme-frontend-blank/web/css/source/_navigation.less into your custom theme under {theme-directory}/web/css/source/_navigation.less. Next, open lib/web/css/source/lib/_navigation.less and find .lib-main-navigation-desktop (line 219-443). Copy this entire block of code to the top of the _navigation.less file in your theme. Rename it to something like .my-main-navigation-desktop.

Find this code in the mixin:

.submenu {
    top: 0 !important;
    left: 80% !importatn;

Delete the line top: 0 !important;. Now find the line .lib-main-navigation-desktop(); near the bottom of the file and replace it with .my-main-navigation-desktop();, or whatever you named the mixin.

Now that we have removed the hard-coded top property, if you recompiled the css at this point you'd find that the submenus are now vertically positioned beneath the parent, which is not what we want. To get them to be at the correct level, we need to make some changes to the javascript that controls the menu.

To do this, we create a new module with typical boilerplate files.

Copy lib/web/mage/menu.js into {your_module_dir}/view/frontend/web/js/menu.js. In this file, find this section of code (lines 335-344):

"mouseenter .ui-menu-item": function (event) {
                    var target = $(event.currentTarget),

                    if (target.has('ul')) {
                        ulElement = target.find('ul');

Right after that last line, insert this:

if (ulElement[0] && !ulElement[0].classList.contains('level0')) {
    var cssTop = event.target.offsetTop,
        ele = $(ulElement[0]);                         

    window.setTimeout(function () {                        
        ele.css('top', cssTop).animate({top: cssTop}, 20);                        
    }, this.options.delay-10);                        

The reason we are using the timeout and animate is that there is a script somewhere along the line that is setting the css top property (I cannot find where this occurs) and so we have to 'force' it to use the correct value.

In our module, we need to tell magento to use this script so we create a requirejs-config.js file here: {module_dir}/view/frontend/web/requirejs-config.js with this contents:

var config = {
    map: {
        "*": {
            menu: "Your_Module/js/menu"

Now we just need to run some shell commands from the magento root:

rm -rf pub/static/*
rm -rf var/view_preprocessed/*
php bin/magento setup:upgrade
php bin/magento cache:flush

Ok, that should do it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.