This is a very specific solution that can easily be repurposed for similar designs if needed. The idea here is a mega menu that has multiple layers that relate to categories and their children to a maximum depth of 3 that outputs 4 columns into an Elementor mega menu.
The 2nd function that echos the JavaScript into the footer was done so because it is assumed that these shotcodes will always be used and loaded on every page on the front.
function wc_custom_product_category_shortcode($atts) {
// Shortcode attributes
$atts = shortcode_atts(array(
'parent_category' => 0,
), $atts, 'custom_product_categories');
$parent_category_id = (int)$atts['parent_category'];
// Get the direct children of the parent category
$categories = wc_get_child_terms($parent_category_id);
// If no categories found, return a message
if (empty($categories) || is_wp_error($categories)) {
return 'No child categories found.
';
}
// Start output (column 1: top-level child categories of the parent)
$output = '';
$output .= '';
$output .= '';
$output .= '';
// Build array of categories that do have children, to facilitate building column 2
$sub_category_lists = array();
foreach ($categories as $category) {
$child_categories = wc_get_child_terms($category->term_id);
// If the category has children, we store them for later rendering (column 2)
if (!empty($child_categories) && !is_wp_error($child_categories)) {
$output .= '- '
. '' . esc_html($category->name) . '
';
$sub_category_lists[$category->term_id] = $child_categories;
} else {
// No children => direct link
$output .= '- '
. '' . esc_html($category->name) . '
';
}
}
$output .= '
';
// Column 2: subcategories (children of the parent categories)
$output .= '';
$sub_sub_category_lists = array(); // Will store data for column 3
foreach ($sub_category_lists as $parent_id => $child_terms) {
// Get slug of the parent
$parent_term = get_term($parent_id, 'product_cat');
$parent_slug = $parent_term ? $parent_term->slug : '';
$output .= '';
foreach ($child_terms as $sub) {
$sub_child_cats = wc_get_child_terms($sub->term_id);
if (!empty($sub_child_cats) && !is_wp_error($sub_child_cats)) {
$output .= '- '
. '' . esc_html($sub->name) . '
';
$sub_sub_category_lists[$sub->term_id] = $sub_child_cats;
} else {
$output .= '- '
. '' . esc_html($sub->name) . '
';
}
}
$output .= '
';
}
$output .= '';
// Column 3: sub-subcategories
$output .= '';
$sub_sub_sub_category_lists = array(); // For column 4
foreach ($sub_sub_category_lists as $sub_id => $sub_sub_categories) {
$sub_term = get_term($sub_id, 'product_cat');
$sub_slug = $sub_term ? $sub_term->slug : '';
$output .= '';
foreach ($sub_sub_categories as $sub_sub) {
$sub_sub_children = wc_get_child_terms($sub_sub->term_id);
if (!empty($sub_sub_children) && !is_wp_error($sub_sub_children)) {
$output .= '- '
. '' . esc_html($sub_sub->name) . '
';
$sub_sub_sub_category_lists[$sub_sub->term_id] = $sub_sub_children;
} else {
$output .= '- '
. '' . esc_html($sub_sub->name) . '
';
}
}
$output .= '
';
}
$output .= '';
// Column 4: sub-sub-subcategories (only show if any exist)
if (!empty($sub_sub_sub_category_lists)) {
$output .= '';
foreach ($sub_sub_sub_category_lists as $sub_sub_id => $sub_sub_subs) {
$sub_sub_term = get_term($sub_sub_id, 'product_cat');
$sub_sub_slug = $sub_sub_term ? $sub_sub_term->slug : '';
$output .= '';
foreach ($sub_sub_subs as $sub_sub_sub) {
$output .= '- '
. '' . esc_html($sub_sub_sub->name) . '
';
}
$output .= '
';
}
$output .= '';
}
$output .= '';
return $output;
}
add_shortcode('custom_product_categories', 'wc_custom_product_category_shortcode');
function wc_custom_product_category_footer_script() {
echo '
';
}
add_action('wp_footer', 'wc_custom_product_category_footer_script');
This can be put anywhere as long as it loads whenever the shorcodes are used. Appearance -> Customise -> Additional CSS is where we’ve been putting extra CSS lately.
Note: Content width is only defined for Desktop and Laptop because it is assumed that the mega menu will be hidden on tablet due to it’s size.
.mega-category-list-wrapper ul {
list-style-type: none;
padding: 20px 0;
width: 100%;
}
.mega-category-list-wrapper ul li {
padding: 7px 15px 7px 3px;
margin: 3px 0 !important;
width: fit-content;
}
.mega-category-list-wrapper ul li a {
color: white;
}
.mega-category-list-wrapper ul li a:hover {
opacity: 0.7;
}
.mega-category-list {
padding: 0;
}
.mega-category-list-wrapper {
display: flex;
gap: 0;
width: min(1300px, 100% - 80px);
margin: 0 auto;
}
@media only screen and (max-width: 1380px) {
.mega-category-list-wrapper {
width: max(100% - 80px, 984px);
}
}
.mega-cat-col-1,
.mega-cat-col-2,
.mega-cat-col-3,
.mega-cat-col-4 {
width: 25%;
}
.mega-cat-col-2,
.mega-cat-col-3,
.mega-cat-col-4 {
padding-left: 40px;
}
.mega-category-list[data-depth="0"] {
background: #00114F;
}
.mega-category-list[data-depth="1"] {
background: linear-gradient(90deg, #00114F 0% 50%, #172660 50% 100%);
}
.mega-category-list[data-depth="1"] .mega-category-list-wrapper {
background: linear-gradient(90deg, #00114F 0% 25%, #172660 25% 100%);
}
.mega-category-list[data-depth="2"] {
background: linear-gradient(90deg, #00114F 0% 50%, #323D67 50% 100%);
}
.mega-category-list[data-depth="2"] .mega-category-list-wrapper {
background: linear-gradient(90deg, #00114F 0% 25%, #172660 25% 50%, #323D67 50% 100%);
}
.mega-category-list[data-depth="3"] {
background: linear-gradient(90deg, #00114F 0% 50%, #485071 50% 100%);
}
.mega-category-list[data-depth="3"] .mega-category-list-wrapper {
background: linear-gradient(90deg, #00114F 0% 25%, #172660 25% 50%, #323D67 50% 75%, #485071 75% 100%);
}
.mega-category-list-wrapper ul li.active {
background: #B70007;
}
.sub-category-list,
.sub-sub-category-list,
.sub-sub-sub-category-list {
display: none;
}
.sub-category-list.active,
.sub-sub-category-list.active,
.sub-sub-sub-category-list.active {
display: block;
}