illustration of person sitting cross-legged with a laptop that has a signup form on the screen

Accessible MailChimp Signup Form for WordPress

I recently created an accessible version of a MailChimp signup form for a client so that we could replace the Divi Contact Form module that does not meet WCAG requirements. The form is an adaptation of a MailChimp embedded form that has custom error validation and announces errors and status updates to screen readers using wp.a11y.speak().

About WP.A11y.Speak() and ARIA Live

WP.a11y.speak() is a javascript method that is included in WordPress, since version 4.2, that you can use to announce changes within ARIA live regions to screen readers. ARAI live regions are portions of a webpage where the content may dynamically change while user user’s focus is on a different part of the page. In this form example, I use wp.a11y.speak() to announce to screen reader users that error messages are present, that the form submission is processing and that it has finished successfully.

Complete Example Code

Here is what the end result will look like.

<form method="post" id="newsletter" action="https://AAAAA.list-manage.com/subscribe/post-json?u=BBBBB&id=CCCCC&f_id=DDDDD&c=?" novalidate>
  <div id="responses">
    <div class="response" id="newsletter-errors" style="display: none;"></div>
    <div class="response" id="newsletter-processing" style="display: none;">
      <p id="wp-a11y-speak-processing" aria-live="polite" aria-relevant="additions text" aria-atomic="true" class="wp-a11y-speak-region">Processing...</p>
    </div>
    <div class="response" id="newsletter-success" style="display: none;">
      <p id="wp-a11y-speak-success" aria-live="polite" aria-relevant="additions text" aria-atomic="true" class="wp-a11y-speak-region">You have been successfully added!</p>
    </div>
  </div>
  <div class="form-container">
    <div class="indicates-required">
      <p>Required fields are marked with an asterisk (<abbr title="required">*</abbr>).</p>
    </div>
    <div class="field-group">
      <label for="FNAME"><abbr class="req" title="required">*</abbr>First Name:</label>
      <input type="text" name="FNAME" id="FNAME" class="input" value="" data-msg="First Name is Required" autocomplete="on" required>
    </div>
    <div class="field-group">
      <label for="LNAME"><abbr class="req" title="required">*</abbr>Last Name:</label>
      <input type="text" name="LNAME" id="LNAME" class="input" value="" data-msg="Last Name is Required" autocomplete="on" required>
    </div>
    <div class="field-group">
      <label for="EMAIL"><abbr class="req" title="required">*</abbr>Email Address:</label>
      <input type="email" name="EMAIL" id="EMAIL" class="input email" value="" data-msg="Email is Required" autocomplete="on" required>
    </div>
  </div>	
  <div aria-hidden="true" style="position: absolute; left: -5000px;">
    /* real people should not fill this in and expect good things - do not remove this or risk form bot signups */
    <label for="nobots">nobots</label>
    <input id="nobots" type="text" name="EEEEEE" tabindex="-1" value="">
  </div>		
  <button type="submit" class="button">Subscribe</button>				
</form>

<script>
jQuery(document).ready(function($) {      
  
  $("#newsletter").on("submit",function(e){
    e.preventDefault();
    $("#newsletter-errors").html("").hide();
	var error = "";
    var submitForm="1";
	
	$(".input").each(function(){
	  if($(this).attr("required")){
	    if($(this).val()==""){
		  error=error + "<li>" + $(this).attr("data-msg") + "</li>";
		  submitForm="0";
		  $(this).addClass("invalid");
		}
	  }	
	});

	var email = $(".email").val();
	  
    if(email.indexOf('.')==-1 && email.indexOf('@')==-1){
      error = error + "<li>Email must be valid format</li>";
      submitForm="0";
      $(".email").addClass("invalid");
    }
	  
    if(submitForm=="0"){
      $("#newsletter-errors").append("<ul id='wp-a11y-speak-errors' aria-live='polite' aria-relevant='additions text' aria-atomic='true' class='wp-a11y-speak-region'>"+error+"</ul>");
      $("#newsletter-errors").show();
      $("input.invalid").first().focus();
	  wp.a11y.speak( $("#wp-a11y-speak-errors").text());
    }
    else{
      $(".form-container").hide();
      $("#newsletter-processing").show().focus();
	  wp.a11y.speak( $("#wp-a11y-speak-processing").text());
      $.ajax({
	    type: $("#newsletter").attr('method'),
       	url: $("#newsletter").attr('action'),
		data: $("#newsletter").serialize(),
        dataType: 'json',
		contentType: "application/json; charset=utf-8",
		success: function( msg ) {
          $("#newsletter-processing").hide();
		  $("#newsletter-success").show().focus();
	  	  wp.a11y.speak( $("#wp-a11y-speak-success").text());
		},
		error: function(xhr, status, error) {
		  var acc = []
          $.each(xhr, function(index, value) {
            acc.push(index + ': ' + value);
          });
          console.log(JSON.stringify(acc));
    	}
	  });
    }
  });
});
</script>

Let’s break down the key pieces of code and how to customize it for your own purposes. Note: this requires that you are comfortable editing HTML and adding PHP code to your theme.

  1. Create an Embedded Signup Form in Your MailChimp Account
    • The first step is to log into your MailChimp account and create a Signup form under Audiences > Signup Forms.
    • Choose the Embedded Form type, give your form a name and select the corresponding audience that people should be added to.
    • Choose the fields to include and which fields are required (for the purpose of my form, I included email, first name and last name – all required).
    • Click Continue until you get to the screen where you can copy the code that MailChimp has generated for you.
  2. Modify the Form Action
    • Within the code that MailChimp generates, find the form action. Copy that value into the form action of the code above and make two specific changes:
      • change “/subscribe/post” to “/subscribe/post-json”
      • add “&c=?” to the end
  3. Edit Your Anti-bot Field
    • In your MailChimp generated code, find the comment that starts with “real people…”. Copy the name of that field into the example code instead of the string “EEEEE”.
  4. Adjust Fields
    • Manually remove fields from the example code that you do not want to include; manually add additional fields following the pattern of the other fields.
    • If you do not want fields to be required, remove the ‘required’ attribute on the tag and remove the ‘*’ from the field label.
  5. Add WP.A11y function to your WordPress theme
    • Add the following PHP function to your WordPress theme, changing “yourprefix” to a custom string. You can add it directly into your functions file or you can use a plugin like Code Snippets.
      add_action( 'wp_enqueue_scripts', 'yourprefix_a11y' );
      function yourprefix_a11y() {
      	wp_enqueue_script( 'wp-a11y' );
      }
  6. Customize Messages and Styles
    • Customize the messages that users should see and hear in the #newsletter-processing and #newsletter-success divs.
    • Adjust the data-msg attributes on each required field if you want a different error message.
    • Adjust the Submit button text as desired.
    • Add your own CSS and classes to make sure the form appears the way you want.
      • Make sure that all labels remain visible and have sufficient color contrast with any background color.
      • Make sure that all input field borders, backgrounds and text have sufficient color contrast ratios.
      • Make sure the submit button background color and text have sufficient color contrast ratios.
  7. Test
    • Add the code to a dev site or test page and thoroughly test including submissions that have errors and submissions that are fully correct.
    • This example uses very simple pattern testing for email fields. Feel free to add more complex pattern testing as needed.