Summary

Use of progressive enhancement techniques ensures content is available when scripting is not.

Techniques

Example

Example 1 — An enhanced link

In the following example, the secret code is only hidden and made activatable when scripting is available. Otherwise, the text will appear as part of the paragraph so that users can read it regardless of scripting support.

<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>
   <![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.

Explanation

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 that:

[t]op-level content documents that include spine-level scripting should remain consumable by the user without any information loss or other significant deterioration when scripting is disabled or not available

EPUB 3.3

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 user agent.

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 user agents.