diff -r 48c4eec2b7e6 -r 8c2e4d02f4ef wp/wp-admin/js/customize-nav-menus.js --- a/wp/wp-admin/js/customize-nav-menus.js Fri Sep 05 18:40:08 2025 +0200 +++ b/wp/wp-admin/js/customize-nav-menus.js Fri Sep 05 18:52:52 2025 +0200 @@ -223,6 +223,9 @@ this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { $( this ).removeClass( 'invalid' ); + var errorMessageId = $( this ).attr( 'aria-describedby' ); + $( '#' + errorMessageId ).hide(); + $( this ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' ); }); // Load available items if it looks like we'll need them. @@ -546,8 +549,11 @@ var menuItem, itemName = $( '#custom-menu-item-name' ), itemUrl = $( '#custom-menu-item-url' ), + urlErrorMessage = $( '#custom-url-error' ), + nameErrorMessage = $( '#custom-name-error' ), url = itemUrl.val().trim(), - urlRegex; + urlRegex, + errorText; if ( ! this.currentMenuControl ) { return; @@ -566,15 +572,37 @@ * so this pattern does not need to be complete. */ urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/; - - if ( '' === itemName.val() ) { - itemName.addClass( 'invalid' ); - return; - } else if ( ! urlRegex.test( url ) ) { - itemUrl.addClass( 'invalid' ); + if ( ! urlRegex.test( url ) || '' === itemName.val() ) { + if ( ! urlRegex.test( url ) ) { + itemUrl.addClass( 'invalid' ) + .attr( 'aria-invalid', 'true' ) + .attr( 'aria-describedby', 'custom-url-error' ); + urlErrorMessage.show(); + errorText = urlErrorMessage.text(); + // Announce error message via screen reader + wp.a11y.speak( errorText, 'assertive' ); + } + if ( '' === itemName.val() ) { + itemName.addClass( 'invalid' ) + .attr( 'aria-invalid', 'true' ) + .attr( 'aria-describedby', 'custom-name-error' ); + nameErrorMessage.show(); + errorText = ( '' === errorText ) ? nameErrorMessage.text() : errorText + nameErrorMessage.text(); + // Announce error message via screen reader + wp.a11y.speak( errorText, 'assertive' ); + } return; } + urlErrorMessage.hide(); + nameErrorMessage.hide(); + itemName.removeClass( 'invalid' ) + .removeAttr( 'aria-invalid', 'true' ) + .removeAttr( 'aria-describedby', 'custom-name-error' ); + itemUrl.removeClass( 'invalid' ) + .removeAttr( 'aria-invalid', 'true' ) + .removeAttr( 'aria-describedby', 'custom-name-error' ); + menuItem = { 'title': itemName.val(), 'url': url, @@ -1107,7 +1135,7 @@ var section = this, $title; - $title = section.container.find( '.accordion-section-title:first' ); + $title = section.container.find( '.accordion-section-title button:first' ); $title.find( '.menu-in-location' ).remove(); _.each( themeLocationSlugs, function( themeLocationSlug ) { var $label, locationName;