When a block theme’s Navigation block collapses into the hamburger overlay, the open menu is white with black text. That’s fine until your header is dark, or your brand palette has no white in it. You can set Overlay Menu colors in the block’s Styles panel, but only one block at a time. Each header template part, each pattern and each client site needs the same clicks again, and someone will eventually drop in a new Navigation block that still has the white overlay.
Plain CSS is also harder than it looks. Core sets the white background with .wp-block-navigation:not(.has-background) .wp-block-navigation__responsive-container.is-menu-open. That selector has four classes, so a quick override in theme.json or Additional CSS loses on specificity. The snippet below takes a different route. It gives every Navigation block default overlay colors before it renders, so core writes its normal color classes and you don’t need any extra CSS.
The snippet
Put it in a small plugin, an mu-plugin or your theme’s functions.php. Change contrast and base to slugs from your theme.json palette, or use hex values.
<?php
/**
* Plugin Name: DPC Navigation Overlay Colors
* Description: Default overlay (mobile menu) colors for every core/navigation block.
*/
/**
* Overlay colors: a palette slug from theme.json (e.g. 'contrast') or a hex value.
* Use an empty string to leave that color alone.
*/
function dpc_nav_overlay_colors(): array {
return (array) apply_filters(
'dpc_nav_overlay_colors',
array(
'background' => 'contrast',
'text' => 'base',
)
);
}
function dpc_nav_overlay_default_colors( array $parsed_block ): array {
if ( 'core/navigation' !== ( $parsed_block['blockName'] ?? '' ) ) {
return $parsed_block;
}
$attrs = is_array( $parsed_block['attrs'] ?? null ) ? $parsed_block['attrs'] : array();
$colors = dpc_nav_overlay_colors();
$map = array(
'background' => array( 'overlayBackgroundColor', 'customOverlayBackgroundColor' ),
'text' => array( 'overlayTextColor', 'customOverlayTextColor' ),
);
foreach ( $map as $key => [ $preset_attr, $custom_attr ] ) {
// Respect colors the editor already chose for this block.
if ( empty( $colors[ $key ] ) || isset( $attrs[ $preset_attr ] ) || isset( $attrs[ $custom_attr ] ) ) {
continue;
}
$value = (string) $colors[ $key ];
$hex = sanitize_hex_color( $value );
if ( $hex ) {
$attrs[ $custom_attr ] = $hex;
} elseif ( '' !== sanitize_key( $value ) ) {
$attrs[ $preset_attr ] = sanitize_key( $value );
}
}
$parsed_block['attrs'] = $attrs;
return $parsed_block;
}
add_filter( 'render_block_data', 'dpc_nav_overlay_default_colors' );
How it works
render_block_dataruns on the parsed block before WordPress renders it. The snippet only changescore/navigationblocks.- The Navigation block stores overlay colors in four attributes:
overlayBackgroundColorandoverlayTextColorhold palette slugs, andcustomOverlayBackgroundColorandcustomOverlayTextColorhold custom values. The snippet fills these in the same way the Styles panel does. - Core’s renderer turns the preset slugs into
has-background has-contrast-background-colorandhas-text-color has-base-colorclasses on.wp-block-navigation__responsive-container. Hex values go in as an inline style, filtered throughsafecss_filter_attr(). Once the container hashas-background, the:not(.has-background)white default no longer applies, so you don’t need a specificity fight. - If a block already has its own overlay color (preset or custom), it keeps it. The snippet only fills in colors that are missing.
Customise it
Override the colors from another plugin or a child theme with the dpc_nav_overlay_colors filter. For example, use a hex background and leave the text color alone:
add_filter( 'dpc_nav_overlay_colors', fn() => array(
'background' => '#0f172a',
'text' => '',
) );
Palette slugs are better than hex values. The preset class points to var(--wp--preset--color--contrast), so the overlay follows the colors when someone switches a style variation in the Site Editor.
Gotchas
- The slug has to exist in your theme.json palette. Otherwise the
has-{slug}-background-colorclass has no CSS behind it and the overlay looks transparent. Check the slugs undersettings.color.palette. - This changes the front end only. The Site Editor’s overlay preview still shows the block’s saved attributes, so editors will see the old colors there. If you also need the editor to match, load a small stylesheet into the editor iframe, as in CSS In Gutenberg editor.
- Submenus inside the open overlay inherit the overlay text color. The separate Submenu & overlay colors only affect desktop dropdowns.
- If the colors don’t appear, another plugin may be filtering
render_block_dataafter this one and resetting the attributes. List everything hooked to that filter to find it.