Why I Needed Region-Specific Menu Templates
Drupal’s core theming layer only recognises two menu templates out-of-the-box:
menu.html.twig ← global fallback
menu--{menu_name}.html.twig ← e.g. menu--top.html.twig
When I placed the same Top menu block twice—once in header_top
and once in mobile
—both instances rendered with the same markup. I wanted totally different HTML for each region, not just different block wrappers.
Step 1. Pass the Region Down to the Menu Template
Create (or edit) copernicus.theme
in your theme folder and add a hook that injects the block’s region into the inner menu render array:
use Drupal\block\Entity\Block;
/**
* Implements hook_preprocess_block().
* Adds the block’s region as an attribute on the child menu render array.
*/
function MYTHEME_preprocess_block(array &$variables) {
if (!isset($variables['elements']['#id'])) {
return; // Skip anonymous blocks.
}
$region = Block::load($variables['elements']['#id'])->getRegion();
$variables['content']['#attributes']['region'] = $region;
}
Step 2. Add a Theme-Hook Suggestion per Region
/**
* Implements hook_theme_suggestions_menu_alter().
*/
function MYTHEME_theme_suggestions_menu_alter(array &$suggestions, array $variables) {
// Work only on the “top” menu.
if (($variables['menu_name'] ?? '') !== 'top') {
return;
}
// Pick up the region we injected in preprocess_block().
if (isset($variables['attributes']['region'])) {
// Convert anything non-alphanumeric to an underscore,
// matching Drupal’s internal sanitiser.
$region = preg_replace('/[^A-Za-z0-9_]+/', '_', $variables['attributes']['region']);
// Append the new suggestion (last = highest priority).
$suggestions[] = "menu__top__{$region}";
}
}
After a cache rebuild (drush cr
or the UI), Drupal will look for templates such as menu--top--header-first.html.twig
or menu--top--mobile.html.twig
before it falls back to the generic files.
Step 3. Create the Region-Specific Twig Files
themes/custom/copernicus/templates/navigation/
├── menu--top--header-first.html.twig
└── menu--top--mobile.html.twig
Add your custom markup inside each:
{# menu--top--header-first.html.twig #}
<nav class="top-nav desktop">
{{ block.content }}
</nav>