XDV and HTML5

written on 2011-01-31

The greatest thing to me about XDV is that you can leave Plone as it is and alter nearly everything about it. Mostly xdv will suffice, but when necessary you can even grab for xsl. You're not even doctype bound. I've been working on an XDV theme based on HTML5 boilerplate.

Compared to my previous post on moving from xhtml 1.0 transitional to strict, this was a bit less of a picknick. More like a night in the wild having to catch and collect your own food. I ran into some major hurdles. Here is how I crossed them.

First off, get HTML5 Boilerplate and create a basic theme. Then start with the rules file, starting off with changing the doctype according to Denys his method.

<?xml version="1.0" encoding="UTF-8"?>
<xdv:rules xmlns:xhtml="http://www.w3.org/1999/xhtml"
 xmlns:xdv="http://namespaces.plone.org/xdv"
 xmlns:css="http://namespaces.plone.org/xdv+css"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- Set output to HTML5 -->
<xsl:output doctype-public="html" doctype-system="" />

Mostly I start with filling the head, working my way down through the HTML. Now after the doctype is where the trouble started. Wanting to use most of the plone metatags, but have the html5 charset and the boilerplate IE edge statement proved more of a problem than I anticipated. Though mostly because I needed to refresh my xpath expressions. I went through a few XSL attempts after realizing i could just do it with regular XDV.

<!-- Pull in Plone Meta -->

<!-- Exclude HTML5 specific meta defined in Boilerplate -->

<xdv:after
     theme="/html/head/meta[last()]"
     content="/html/head/meta[
         not(@content='IE=edge' or
             @content='text/html; charset=utf-8')]" />

Next up, were the script tags. The XSL transform caused them to self close, rendering them invalid (Gotta love that validator). They always need a closing tag. I did not fill any scripts, because I wanted them all to be static resources. This was not a problem when turning xhtml to strict so I was a bit baffled. Turns out that XSLT works different when you have an xhtml doctype. It turns out all empty tags were automatically self closed. The solution for this was adding comments to all the non void tags (tags that are not allowed to self close) or part of the solution at least. This issue had a tail. Beware that script tags need javascript comments instead of regular html ones to be valid.

All the issues seemed to be solved, so I started filling content until I was ready to run it through a validator again. Once again I ran into self closed tags that were not allowed to be closed. This time it was span's and div's from the content side. Looking through posts and forums I ran into the problem, but not really a feasible solution. After a lot of part solutions and even a post from Laurence Row, I ended up with the following XSL. It seems to work, but it has not been greatly tested yet.

<!-- Get empty elements -->

<xsl:template match="*[not(node())]">
 <xsl:copy>
 <xsl:for-each select="@*">
 <xsl:variable name="LocalName" select="local-name()"/>
 <xsl:attribute name="{$LocalName}">
 <xsl:value-of select="."/>
 </xsl:attribute>
 </xsl:for-each>
 <xsl:comment/>
 </xsl:copy>
 </xsl:template>

<!-- Handle script tags with a valid comment of its own -->

<xsl:template match="script[not(node())]">
 <xsl:copy>
 <xsl:for-each select="@*">
 <xsl:variable name="LocalName" select="local-name()"/>
 <xsl:attribute name="{$LocalName}">
 <xsl:value-of select="."/>
 </xsl:attribute>
 </xsl:for-each>
 <xsl:text disable-output-escaping="yes">// Stop XDV from closing tag
 </xsl:text>
 </xsl:copy>
 </xsl:template>

<xsl:template match="*[not(node())]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:comment/>
</xsl:copy>
</xsl:template>

<!-- Exclude void elements -->

<xsl:template match="
  @*|area[not(node())]|
  base[not(node())]|br[not(node())]|
  col[not(node())]|command[not(node())]|
  embed[not(node())]|hr[not(node())]|
  img[not(node())]|input[not(node())]|
  keygen[not(node())]|link[not(node())]|
  meta[not(node())]|param[not(node())]|
  source[not(node())]">
 <xsl:copy>
 <xsl:apply-templates select="@*|node()"/>
 </xsl:copy>
 </xsl:template>

<xsl:template match="area[not(node())]|base[not(node())]|br[not(node())]|
 col[not(node())]|command[not(node())]|embed[not(node())]|
 hr[not(node())]|img[not(node())]|input[not(node())]|
 keygen[not(node())]|link[not(node())]|meta[not(node())]|
 param[not(node())]|source[not(node())]">
 <xsl:copy-of select="."/>
 </xsl:template>

One more thing and then I close off. There's an acronym in plone so, of course, you switch it for an abbr like so:

<!-- Flip acronym to abbr -->

<xsl:template match="acronym">
 <xsl:element name="abbr">
 <xsl:for-each select="@*">
 <xsl:variable name="LocalName" select="local-name()"/>
 <xsl:attribute name="{$LocalName}">
 <xsl:value-of select="."/>
 </xsl:attribute>
 </xsl:for-each>
 <xsl:value-of select="."/>
 </xsl:element >
 </xsl:template>

<xsl:template match="acronym">
 <xsl:element name="abbr">
 <xsl:copy-of select="@*"/>
 <xsl:value-of select="."/>
 </xsl:element>
 </xsl:template>

Update: Found a way to shorten the code. copy-of can strip the whole for-each blocks and the xsl:comment gets stripped, but leaves the closing tag in place. This removes the need for special casing the script element.

Let me know if you use it. Any changes I'll post here.

Update 2: For Diazo all this is no longer necessary

Pagination