Polyglot EPUB?

If you’re reading this post, you probably already know that polyglot HTML5 is markup that can be rendered both as HTML and as XML serialized XHTML without breakage. It’s not something that’s necessary to do in EPUB 3, since the format requires the handling of content as XHTML, but if you’re planning to use your content in a general web context it’s worth knowing about.

How to make EPUB content compatible with browser rendering as HTML seems to comes up enough that I thought it would be interesting to detail. What follows is a condensed list of common issues, but for all the details the W3C maintains a polyglot markup document that should be referenced, and the WHATWG maintains similar information in its HTML vs. XHTML wiki page.

Before launching in too deep, there’s no reason why you can’t take your EPUB content and serve it up on the web as XHTML and avoid polyglot entirely. You just have to remember to set the media type for the pages correctly to application/xhtml+xml, otherwise things won’t go as expected. Incorrect rendering as text/html is one issue that polyglot markup helps you get around, especially if you can’t control how the content gets served. Since it’s compatible as both HTML and XHTML, you’re not going to get broken pages.

The fortunate thing about EPUB using XHTML is that you already have to follow all the more rigid XML rules, like using the & entity instead of &, putting attribute values in quotes, etc. If you were readying yourself to make your HTML more XHTML friendly, you’d be looking at adopting the XHTML serialization for compatibility, and how to handle the much stricter rules. My assumption is you’re starting with XHTML slightly upstream from distribution (whether bookstore or web), so I’m not going to go into the minutiae of detail about how to make XHTML-compatible markup.

But with that bit of preamble out of the way, down to the issues…


In XHTML, and XML generally, it’s always good practice to include an xml declaration identifying the version and encoding of the content, typically like this:

<?xml version="1.0" encoding="utf-8"?>

There was a time when you really didn’t want this tag at the start of HTML pages, as it used to trigger quirks mode in good old IE6. As that browser is still kicking around despite all efforts to beat it down (oddly popular in China), and the declaration really has no use in HTML, dropping it is recommended for polyglot markup.

Doing away with it is not a great heresy, as EPUB requires compliance with version 1.0 of XML, and a somewhat arcane bit of xml knowledge is that when the declaration is omitted there is an assumption that the document conforms to 1.0 and is encoded either as UTF-8 or UTF-16.

Although EPUB supports UTF-16, the polyglot guidelines suggest encoding your content as UTF-8, as UTF-16 support is not as widely supported in older browsers and platforms. But if you know your readers will all be using modern UTF-16 capable browsers, or don’t care if they aren’t, the choice is yours.

That covers the XML side of things, but for HTML processing you also to consider how to specify the encoding. Although this can be done via HTTP headers, since we’re looking at markup independent of delivery you should include the following charset meta tag:

<meta charset="utf-8"/>


The HTML5 DOCTYPE statement is useless in XHTML5 as far as rendering goes:

<!DOCTYPE html>

The only value I’ve ever found in including it is that it differentiates XHTML5 from XHTML 1.1 in Oxygen (for code completion and validation). In HTML, the DOCTYPE is needed as it puts browsers into no quirks mode, which is how XHTML is handled by default. Since it’s harmless in XHTML, add it for the compatibility.


Optional tags — ones that are implied to exist when not actually included in your markup — is another case where creating XHTML first simplifies life. Unlike with HTML, you can’t omit the root htmlhead or body tags in XHTML. What is sometimes unexpected, however, is that polyglot rules are strict about not omitting optional tags anywhere. What this means for tables is that tbodythead and tfoot always have to be used to wrap tr elements. The reason is that HTML parsers will automatically generate tbody when not present for unwrapped tr tags, but XML parsers will not, so for DOM compatibility you need to be explicit.

The polyglot document shows a dated example about using colgroup for col elements, but col is no longer allowed outside a colgroup. A friendly reminder can be found in the example why it pays to validate your content and not just wave off markup errors as not affecting anything if you look close enough.


The difference between an empty element and a void element is that empty element can take child content (they just have none when empty) while void elements cannot. Void tags include: areabasebrcolembedhrimginputkeygenlinkmetaparamsourcetrack and wbr

For polyglot markup, void elements must always be self-closing: <br />. (It’s still recommended to include a space before the slash for the truly old browsers.)

Empty elements must always include both opening and closing tags: <p></p>


A couple of attribute quirks not worth a lot of explanation:

  • polyglot markup makes use of both the lang and xml:lang attributes for setting language; you can’t pick only one. But as we already recommend this practice for accessibility, if you’re being accessible it’s not something new to do.
  • the xml:basexml:id and xml:space attributes must not be used outside of svg or mathml


One difference between XHTML and HTML to keep in mind is that HTML does not make use of namespaces, and typically does not recognize prefixes (xlink: being one exception). Although namespace declarations are harmless in HTML, avoiding prefixes is key to interoperability. In HTML, prefixed elements like <m:math and <svg:svg are not going to render as expected as an HTML parser is looking for <math and <svg, respectively. To accommodate both models, set the default namespace on the elements and avoid prefixing:

<math xmlns="http://www.w3.org/1998/Math/MathML">
<svg xmlns="http://www.w3.org/2000/svg">


Talking about namespaces… I’m going to make this section up myself since the HTML world knows not EPUB.

Knowing that HTML parsers aren’t going to interpret special namespaced elements, and that browsers have no awareness of EPUB declarative markup, it’s best to avoid both the epub:switch and epub:trigger elements if you can.

If you can’t avoid epub:switch, you’re going to want to filter your content, otherwise things could appear in duplicate and triplicate. That doesn’t necessarily mean running your EPUB through some pipeline tool chain to rework the content, although that’s the more reliable option. You could try to script-hide the parts of the switch you don’t want rendered, but getting the current page’s mime type to see if you’re in HTML rendering is tricky, at best. YMMV.

The epub:trigger element is a little more complex to handle, since it acts as an interface to audio/video, but you could wire up the events yourself using the JavaScript API functions as a fallback. It somewhat defeats the purpose of having a declarative model, but maybe you want to let someone design a front end using HTML-native elements regardless. The triggers are there for reading systems that don’t support scripting, so technically there’s use in having both options. Now whether anyone uses triggers is still a mystery to me, but not trying to answer that today.

Note that it’s best to include epub:trigger elements in the head so that they don’t cause any potential rendering quirks. It’s also best to include both opening and closing tags, especially if you have to put them in the body for some reason.

There’s actually nothing you need to do about epub:type, since as a custom attribute it will just be ignored. If you are basing styling on it, you’ll need to account for the lack of namespace handling in the CSS (i.e., you’ll need selectors with the colon escaped), but I’m not terribly enthused about mixing styling and semantics in either direction, so including classes for CSS in accompaniment to semantic inflection is the best approach, whatever verbosity it adds.


Once again, starting from XHTML is a big win when it comes to cross-compatibility. You probably already know that if your script contains the special XML characters < and & you’re going to get a parsing error unless you put a CDATA block around your code:

for (i = 0; i <= 10; i++) { alert('My <s and &s are plain old character data in here. Repeat'); }

The CDATA blocks are not going to have the intended effect of treating the data as text for HTML parsing. To account for browsers that may not recognize the scripting, you also have to add comments to hide the CDATA tag:

// <![CDATA[
for (i = 0; i <= 10; i++) { alert('My <s and &s are plain old character data in here. Repeat'); }
// ]]>

Note that if you include style blocks have either character in them, you’re also going to need to put a CDATA section around them. Only when your code/styles are hosted in a separate file do you not have to worry about XML parsing and how to avoid unintended interpretation of your code as XML.

Depending on your take on node operations, the lack of document.write and document.writeln in XHTML are either a godsend of good form or a royal pain in the @ss. But either way, you can’t use those operations to write arbitrary content.

But I started out with a simple goal, and now the post is getting kind of lengthy. I’m going to stop on the detail now, as my ambition wasn’t to rewrite the existing documents and I think I’ve hit on all the major points (but feel free to post in the comments if you think I’ve missed something key).

There are plenty of additional bits to consider, so before attempting to make polyglot markup make sure to give both documents linked at the top a good thorough read. Also make sure you have a real need for polyglot markup, otherwise you could wind up wasting a lot of time to do something with no real value.

One Reply to “Polyglot EPUB?”

  1. For what appears to me as millenia, I’ve been chasing a holy grail of geditable markup syntax subset which can live also in a filesystem and be rendered relationally in DKNF, which includes Dublin Core (large offline debate about library markup here) compatible with Libre/OpenOffice (do NOT ever mention MSOffice) that can be used effectively by parsers/renderers that are familiar with HTML, XHTML, XML, EPUB and PDF/A-1 and a plague on everyone who caused infrastructural SVG (and MathML for that matter) to have a separate namespace.

    I’ve saved your post as a continual reminder that I’m not standing out here by myself spitting into the wind.

Leave a Reply

Your email address will not be published. Required fields are marked *