EPUB 3 Accessibility Guidelines

Progressive Enhancement

Although EPUB 3 adds support for scripted interactivity via JavaScript, it also places some constraints on the way you can use interactivity to ensure publications remain accessible. The guiding principle for scripting is that of progressive enhancement, namely:

when the document is rendered by a Reading System without scripting support or with scripting support disabled, the top-level document content must retain its integrity, remaining consumable by the User without any information loss or other significant deterioration.

EPUB Content Documents 3.0

Progressive enhancement does not mean that you cannot script content, but that scripting must not be required. It is perfectly valid, for example, to remove content from the default rendering and replace it with an enhanced experience when scripting is available. It is likewise permitted to augment the default rendering with scripted interactivity. What you must avoid doing is scripting content such that without the scripting the primary narrative is not available in whole or part.

Validators alone cannot verify conformance to this criteria, so care must be taken when creating content to ensure that the key principles are observed. Notably:

Whenever adding script, do so in a way that augments.

A common example of a scripted enhancement is the adding line numbering to code snippets. Many content creators like to use ordered lists inside pre tags to emulate line numbers, but an ordered list can be distracting to someone using an assistive technology, and consufing and ugly to someone not using a CSS-enabled reading system.

Instead of hard-coding the markup, a progressive enhancement technique would be to lay out your code samples so that they render as expected in the default preformatted view. You can then script, or grab a library, that can dynamically add the ordered list tagging to the element (which is what this site does). This approach ensures a baseline of readability in the most basic reading scenarios, while providing a richer visual view for those who have more advanced reading systems.

Example

Example 1 — An enhanced link
<p>
   The <span id="code">secret code</span> 
   <span id="secret">D E A D M A N</span> had to be
   spelled out to gain passage to the pirates' lair.
</p>

<div id="revealed" aria-live="assertive"/>

<style type="text/css">

   span#secret { color: rgb(255,0,0); }
   
   div#revealed { width: 100%; text-align: center; }
   
   span.letter { 
      font-family: "Times New Roman";
      font-size: 240%;
      font-weight: bold;
      color: rgb(255,0,0);
      text-shadow: rgb(0,0,0) 1px 1px 2px;
      animation: bleedin 2s;
   }

   @keyframes bleedin {
      from {
         font-weight: normal;
         font-size: 0%;
         background: rgb(255,0,0);
         text-shadow: rgb(0,0,0) 1px 1px 50px;
      }
      to {
         font-weight: bold;
         font-size: 240%;
         background: rgb(255,255,255);
         text-shadow: rgb(0,0,0) 1px 1px 2px;
      }
   }

</style>

<script type="text/javascript">
   <![CDATA[
   var code = document.getElementById('code');
         code.style.color = 'rgb(0,0,200)';
         code.onclick = function () { revealCode(); };
         code.setAttribute('role', 'button');
         code.setAttribute('tabindex', 0);
   
   var secret = document.getElementById('secret'); 
         secret.style.visibility = 'hidden';
         secret.style.display = 'none';
         secret.setAttribute('aria-hidden', true);
   
   function revealCode() {
      var revealed = document.getElementById('revealed');
      if (revealed.childNodes.length == 0) {
         var codeArray = 'DEADMAN'.split('');
         var i = 0;
         var revealLetters = setInterval(function(){
            if (i < codeArray.length) {
               var letter = document.createElement('span');
               letter.setAttribute('class', 'letter');
               letter.appendChild(document.createTextNode(codeArray[i]));
               revealed.appendChild(letter);
               i++;
            }
            
            else { 
               clearInterval(revealLetters);
            }
         }, 300);
      }
   }
   ]]>
</script>

A working example of this code follows:

The secret code D E A D M A N had to be spelled out to gain passage to the pirates' lair.