Using HTML5 To Transform WordPress’ TwentyTen Theme
Somewhat surprisingly, TwentyTen declares the HTML5 doctype but doesn’t take advantage of many of the new elements and attributes that HTML5 brings.
Now, HTML5 does many things, but you can’t just add <!doctype html>
to the top of a document and get excited that you’re so 2011. Mark-up, as they say, is meaning, and HTML5 brings a whole bunch of meaning to our documents.
Further Reading on SmashingMag:
- Coding An HTML 5 Layout From Scratch
- HTML 5 Cheat Sheet (PDF)
- What To Consider When Choosing WordPress Themes
- Are You Getting Cheated When Buying A WordPress Theme?
In survey by Smashing Magazine, only 37% of voters said they use HTML5. This is depressing reading. Perhaps developers and designers are scared off by cross-browser incompatibility and the chore of learning new mark-up. The truth is that with a pinch of JavaScript, HTML5 can be used safely today across all browsers, back to IE6.
TwentyTen is a fine theme that already validates as HTML5; but in order to cater to users without JavaScript, it has to forgo a large chunk of HTML5 elements. The reason? Our old friend Internet Explorer doesn’t support most of them prior to version 9.
The default TwentyTen WordPress Theme.
For example, you’ve probably already heard of the <section>
and <article>
tags, both of which are champing at the bit to be embedded in a WordPress template. But to use these HTML5 elements in IE8 (and its predecessors), you need JavaScript in order to create them in the DOM. If you don’t have JavaScript, then the elements can’t be styled with CSS. Turn off JavaScript and you turn off the styling for these elements; invariably, this will break the formatting of your page.
I assume that WordPress decided to exclude these problematic tags so that its default theme would be supported by all browsers — not just those with JavaScript turned on.
While I understand this decision, I also think it’s a mistake. Three core technologies make the Web work: HTML, CSS and JavaScript. All desktop browsers support them (to some degree), so if any one of them off is disabled the user will have to expect a degraded experience. JavaScript is now fundamental to the user experience and while we should support users who turn off JavaScript, or have it turned off for them and have no chance to turn it on again as they don’t have the right to do so, I question just how far we should support them.
Why Using JavaScript Makes Sense
Yahoo gives compelling evidence that less than 1.5% of its users turn off JavaScript. My own research into this, ably assisted by Greig Daines at eConversions, puts the figure below 0.5% (based on millions of visitors to a UK retail website).
Whilst it’s true that JavaScript should be separated from a site’s content, design and structure the reality is no longer black and white. I strongly believe that the benefits and opportunities HTML5 brings, together with related technologies such as CSS3 and media queries (both of which sometimes rely on JavaScript for cross-browser compatibility), is more than enough reason to use JavaScript to ‘force’ new elements to work in Internet Explorer. I am a passionate advocate for standards-based design that doesn’t rely on JavaScript; HTML5 is the one structural exception.
Yes, we should respect a user’s decision to deactivate JavaScript in their browser. However, I don’t believe that this is a good enough reason for not using modern technologies, which would provide the vast majority of users with a richer user experience. After all, in the TwentyTen example, if the theme had HTML5 tags in it, everything would look fine in modern browsers (latest versions of Safari, Firefox, Opera, Chrome and IE9), with or without JavaScript.
If the browser is IE6 – IE8, and JavaScript is turned off, then users would see the content but it will not be styled correctly. If the content would not be displayed at all, we’d have a completely different discussion. If you are still not convinced, I will briefly discuss another option for those who absolutely must support users with JavaScript turned off.
To make TwentyTen play fair with IE, I suggest Remy Sharp’s HTML5 shim or, if you want to sink your teeth into CSS3, Modernizr. Modernizr not only adds support for HTML5 elements in IE but also tells you which CSS3 properties are supported by the user’s browser by adding special classes to the <html>
element.
Mordernizr.js
So, let’s assume you’ve rightly banished non-JavaScript users with a polite message in a <noscript>
tag. We can now start tinkering under the hood of TwentyTen to bring some more HTML5 to WordPress.
Upgrading To HTML5
TwentyTen gets a number of things spot on. First of all, it declares the right doctype and includes the abbreviated meta charset
tag. It also uses other semantic goodness like Microformats and great accessibility features like WAI-ARIA. But we can go further.
Important notes:
- I am referencing the HTML generated at https://wp-themes.com/twentyten/, rather than the simple “Hello World” clean installation of WordPress 3.
- For this article, I’ll be editing the files directly in the
/wp-content/themes/twentyten/
directory. I’ve provided all the updated HTML5 theme source files for you to download from TwentyTen Five. - Line numbers may change over time, so when I reference one, I’ll usually say “on or around line…” The version of WordPress at the time of writing is 3.0.4.
Articles
One of the more confusing parts of the HTML5 spec is the <section>
and <article>
tags. Which came first, the chicken or the egg? The easiest way to remember is to refer to the specification. The HTML5 spec may be dry at the best of times, but its explanation of articles will always point you in the right direction:
The article
element represents a self-contained composition in a document, page, application, or site and that is, in principle, independently distributable or reusable, e.g. in syndication.
If the piece of content in question can be, and most likely will be, syndicated by RSS, then there’s a good chance it’s an <article>
. A blog post in WordPress fits the bill perfectly.
On the TwentyTen home page, we get the following HTML:
<div id="post-19">
…
</div>
Semantically this means very little. But with the simple addition of an article
tag, we’re able to transform it into mark-up with meaning.
<article id="post-19">
…
</article>
Note that we retain the id
to ensure that this <article>
remains unique.
To make this change in the TwentyTen theme, open loop.php, which is in /wp-content/themes/twentyten/
. On or around line 61, you should find the following code:
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
We’ll need to change that <div>
to an <article>
, so that it reads:
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
And then we close it again on or around line 97, so that…
</div><!-- #post-## -->
… becomes:
</article><!-- #post-## -->
There are also instances on lines 32, 101 and 124. Opening some of the other pages in the theme, for example single.php, and making the same change is worthwhile. Thus, line 22 in single.php would change from…
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
… to:
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
And line 55 would change from…
</div><!-- #post-## -->
… to:
</article><!-- #post-## -->
So far, so good. These are simple changes, but they already serve to overhaul the semantics of the website.
Time and Date
According to the HTML5 spec:
The <time>
element either represents a time on a 24-hour clock, or a precise date on the proleptic Gregorian calendar, optionally with a time and a time-zone offset.
This means we can give the date and time of an article’s publication more context with HTML5’s <time>
tag. Look at the code that WordPress generates:
<a href="https://wp-themes.com/?p=19" title="4:33 am"
rel="bookmark"><span>October 17, 2008</span></a>
We can add meaning to our mark-up by transposing this to:
<a href="https://wp-themes.com/?p=19" title="4:33 am"
rel="bookmark"><time datetime="2008-10-17T04:33Z"
pubdate>October 17, 2008</time></a>
This time is now machine-readable, and the browser can now interact with the date in many ways should we so wish. I’ve also added the boolean attribute pubdate
, which designates this as the date on which the article or content was published.
Time in the datetime
attribute is optional, but because WordPress includes it when you post an article, we can too. Implementing this in TwentyTen requires us to dig a little deeper. In loop.php, the following function on or around line 65 calls for the date to be included:
<?php twentyten_posted_on(); ?>
To make our HTML5 changes, let’s head over to /wp-content/themes/twentyten/
and open functions.php. On or around line 441, you’ll see this:
function twentyten_posted_on() {
printf( __( '<span>Posted on</span> %2$s <span>by</span> %3$s', 'twentyten' ),
'meta-prep meta-prep-author',
sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span>%3$s</span></a>',
get_permalink(),
esc_attr( get_the_time() ),
get_the_date()
),
If you don’t know what that means, don’t worry. We’re focusing on the sprintf function, which basically takes a string and inserts the variables that are returned by the three functions listed: that is, get_permanlink()
, get_the_time()
and get_the_date()
are inserted into %1$s
, %2$s
and %3$s
, respectively.
We need to change how the date is formatted, so we’ll have to add a fourth function: get_the_date(‘c’)
. WordPress will then return the date in Coordinated Universal Time (UTC) format, which is exactly what the <time>
element requires. Our finished code looks like this:
printf( __( 'Posted on %2$s by %3$s', 'twentyten' ),
'meta-prep meta-prep-author',
sprintf( '<a href="%1$s" rel="bookmark"><time datetime="%2$s"
pubdate>%3$s</time></a>',
get_permalink(),
get_the_date('c'),
get_the_date()
),
I’ve included get_the_date()
twice because we need two different formats: one for the <time>
element and one that’s displayed to the user. I’ve also removed title=”[time published]”
because that information is already included in the <time>
element.
For more details on WordPress’ date and time functions, check out:
Figures
A figure—for our purposes at least—is a piece of media that you upload in WordPress to embed in a post. The most obvious example would be an image, but it could be a video, too, of course. WordPress 3 is helpful enough to add captions to images when you first import the images, but it doesn’t display those captions using the new HTML5 <figure>
and <figcaption>
tags.
The spec defines <figure>
as follows:
The figure
element represents a unit of content, optionally with a caption, that is self-contained, that is typically referenced as a single unit from the main flow of the document, and that can be moved away from the main flow of the document without affecting the document’s meaning.
And it defines <figcaption>
like so:
Thefigcaption
element represents a caption or legend for the rest of the contents of thefigcaption
element’s parentfigure
element, if any.
Currently an image with a caption is rendered like this:
<div class="wp-caption" style="width: 445px;"><img alt="Boat"
src="https://wpdotorg.files.wordpress.com/2008/11/boat.jpg"
title="Boat" width="435" height="288" />
<p class="wp-caption-text">Boat</p>
</div>
A WordPress image with a caption.
Changing this HTML to include HTML5 elements requires us to first look at media.php in the /wp-includes/
directory, where this code is generated. On or around line 739, you’ll find:
return '<div ' . $id . 'class="wp-caption ' . esc_attr($align) . '" style="width: ' . (10 + (int) $width) . 'px">'
. do_shortcode( $content ) . '<p>' . $caption . '</p></div>';
To upgrade this to HTML5, we need to define a new function that outputs our <figure>
-based HTML and assign this function to the same shortcode that calls img_caption_shortcode()
. I’ve done this in /wp-content/themes/twentyten/functions.php
by adding the following to the bottom of the file:
add_shortcode('wp_caption', 'twentyten_img_caption_shortcode');
add_shortcode('caption', 'twentyten_img_caption_shortcode');
function twentyten_img_caption_shortcode($attr, $content = null) {
extract(shortcode_atts(array(
'id' => ’,
'align' => 'alignnone',
'width' => ’,
'caption' => ’
), $attr));
if ( 1 > (int) $width || empty($caption) )
return $content;
if ( $id ) $idtag = 'id="' . esc_attr($id) . '" ';
return '<figure ' . $idtag . 'aria-describedby="figcaption_' . $id . '" style="width: ' . (10 + (int) $width) . 'px">'
. do_shortcode( $content ) . '<figcaption id="figcaption_' . $id . '">' . $caption . '</figcaption></figure>';
}
First, we point the shortcodes for wp-caption
and caption
to our new function twentyten_img_caption_shortcode()
. Then, we simply copy the original function from media.php, and change the last few lines to include our <figure>
element. This now renders our boat.jpg example from above like so:
<figure id="attachment_64" style="width: 445px;">
<a href="https://localhost/wp-content/uploads/2010/07/boat.jpg">
<img title="boat" src="https://localhost/wp-content/uploads/2010/07/boat.jpg"
alt="Screenshot" width="435" height="288" aria-describedby="figcaption_attachment_64"></a>
<figcaption id="figcaption_attachment_64">Boat</figcaption>
</figure>
The Comments Form
One of the biggest improvements introduced in HTML5 is how form fields work and respond to user input. We can take advantage of these changes by using HTML5 form elements in the default WordPress comments form in three ways:
- We can set the text-input type to
email
andurl
for the relevant fields. This not only more accurately describes the input field, but also adds better keyboard functionality for the iPhone, for example. - We can add the boolean attribute
required
to our required form fields. This goes beyond WAI-ARIA’saria-required='true'
because it invokes the browser’s ownrequired
behavior. - We can add placeholder text to our form fields, a popular JavaScript method that is now handled in-browser. Placeholder text allows you to go into more detail about what information is required than a form label generally allows.
Before adding HTML, a typical comment input field might look like this:
<label for="email">Email</label> <span>*</span>
<input id="email" name="email" type="text" value=""
size="30" aria-required='true' />
After our HTML5 changes, it would look like this:
<label for="email">Email</label> <span>*</span>
<input id="email" name="email" type="email" value=""
size="30" aria-required='true'
placeholder="How can we reach you?" required />
To make these improvements in the code, we need to do two things. First, we need to change the HTML for the default fields (name, email address and website URL), and then we need to change it for the comment’s <textarea>
. We can achieve both of these changes with additional filters and custom functions.
To change the HTML for the default form fields, we need to add the following filter to the bottom of functions.php:
add_filter('comment_form_default_fields', 'twentytenfive_comments');
And then we have to create our custom function twentytenfive_comments()
to change how these fields are displayed. We can do so by creating an array containing our new form fields and then returning it to this filter. Here’s the function:
function twentytenfive_comments() {
$req = get_option('require_name_email');
$fields = array(
'author' => '<p>' . '<label for="author">' . __( 'Name' ) . '</label> ' . ( $req ? '<span>*</span>' : ’ ) .
'<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' placeholder = "What should we call you?"' . ( $req ? ' required' : ’ ) . '/></p>',
'email' => '<p><label for="email">' . __( 'Email' ) . '</label> ' . ( $req ? '<span>*</span>' : ’ ) .
'<input id="email" name="email" type="email" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' placeholder="How can we reach you?"' . ( $req ? ' required' : ’ ) . ' /></p>',
'url' => '<p><label for="url">' . __( 'Website' ) . '</label>' .
'<input id="url" name="url" type="url" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" placeholder="Have you got a website?" /></p>'
);
return $fields;
}
You can see here that each element in the form has a name in the array()
: author, email and url. We then type in our custom code, which contains the new HTML5 form attributes. We have added placeholder text to each of the elements and, where required, added the boolean required
attribute (and we need to check if the admin has made these fields required using the get_option()
function). We’ve also added the correct input type to the inputs for author, email address and website URL.
Finally, we need to add some HTML5 to the <textarea>
, which is home to the user’s comments. We have to use another filter here, also in functions.php:
add_filter('comment_form_field_comment', 'twentytenfive_commentfield');
We follow this with another custom function:
function twentytenfive_commentfield() {
$commentArea = '<p><label for="comment">' . _x( 'Comment', 'noun' ) . '</label><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true" required placeholder="What's on your mind?" ></textarea></p>';
return $commentArea;
}
This is more or less the same as the default <textarea>
, except with placeholder
and required
attributes.
You can control exactly which fields appear in your form with these two filters, so feel free to add more if you want to collect more information.
Although relatively simple, these changes to the comment form provide additional (and useful!) features to users with latest-generation browsers. Look in Opera, Chrome (which doesn’t yet support required
) or Firefox 4 to see the results.
Header, Navigation and Footer
We finally get around to inserting the new <header>
, <nav>
and <footer>
elements. Currently, the code in /wp-content/themes/twentyten/header.php
looks more or less like this:
<div id="header">
<div id="masthead">
<div id="branding" role="banner">
…
</div><!-- #branding -->
<div id="access" role="navigation">
…
</div><!-- #access -->
</div><!-- #masthead -->
</div><!-- #header -->
It doesn’t take a genius to see that we can easily make this HTML5-ready by changing some of those divs to include <header>
and <nav>
.
<header id="header">
<section id="masthead" >
<div id="branding" role="banner">
…
</div><!-- #branding -->
<nav id="access" role="navigation">
…
</nav><!-- #access -->
</section><!-- #masthead -->
</header><!-- #header -->
You can see that we’ve left the WAI-ARIA role of navigation
assigned to the nav
element—simply to offer the broadest possible support to all browsers and screen readers.
I have replaced the #masthead
div with a <section>
because all of the elements in this area relate to one another and are likely to appear in a document outline. It seems you could delete this section altogether and just apply 30 pixels of padding-top
to the header to maintain the layout. I’ve maintained the elements’ id
s in case more than one of each are on the page—multiple headers, footers and navs (and more) are all welcome in HTML5.
While we’re editing the header, we can introduce the new <hgroup> element. This element enables us to include multiple headings in a section of our document, while they would be treated as just one heading in the document outline. Currently, the code on or around line 65 in header.php looks like this:
<?php $heading_tag = ( is_home() || is_front_page() ) ? 'h1' : 'div'; ?>
<<?php echo $heading_tag; ?> id="site-title">
<span>
<a href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a>
</span>
</<?php echo $heading_tag; ?>>
<div id="site-description"><?php bloginfo( 'description' ); ?></div>
We can edit this to include the <hgroup>
tag, and also change <div id="site-description">
to an <h2>
element:
<hgroup>
<?php $heading_tag = ( is_home() || is_front_page() ) ? 'h1' : 'div'; ?>
<<?php echo $heading_tag; ?> id="site-title">
<span>
<a href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a>
</span>
</<?php echo $heading_tag; ?>>
<h2 id="site-description"><?php bloginfo( 'description' ); ?></h2>
</hgroup>
In /wp-content/themes/twentyten/footer.php
, we have:
<div id="footer" role="contentinfo">
<div id="colophon">
…
<div id="site-info">
<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
<?php bloginfo( 'name' ); ?>
</a>
</div><!-- #site-info -->
<div id="site-generator">
…
</div><!-- #site-generator -->
</div><!-- #colophon -->
</div><!-- #footer -->
We can easily edit this to include a <footer>
and another <section>
element:
<footer role="contentinfo">
<section id="colophon">
…
<div id="site-info">
<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
<?php bloginfo( 'name' ); ?>
</a>
</div><!-- #site-info -->
<div id="site-generator">
…
</div><!-- #site-generator -->
</section><!-- #colophon -->
</footer><!-- #footer -->
JavaScript and CSS
As mentioned, we should include an HTML5 shim or Modernizr.js to make sure that all of our new elements render correctly in Internet Explorer prior to version 9. I added the following line to header.php:
<script src="<?php bloginfo('stylesheet_directory'); ?>/js/Modernizr-1.6.min.js"></script>
A couple of things to note here. First, we no longer need type=“text/javascript”
because the browser will assume that a script is JavaScript unless it’s told different. Secondly, we have to use the WordPress bloginfo()
function to point the source URL to our theme directory.
Although we are including Modernizr partly to make sure that IE can deal with the new HTML5 elements, I am serving it to all browsers because of the CSS3-checking functionality it provides.
In style.css, we need to make sure that our HTML5 elements have a display: block
attribute, because some older browsers will treat them as inline elements. For our purposes, the following line at the top of the CSS file will do:
header, nav, section, article, aside, figure, footer { display: block; }
While we’re talking about CSS, remember that we can now remove type=“text/css”
from our <link>
tags. The simplified code looks like this:
<link rel="stylesheet" href="<?php bloginfo( 'stylesheet_url' ); ?>" />
That should be enough for now. Remember, though, that changing the structure of the page by replacing older HTML elements with new ones might require some additional CSS.
We should let the small minority of users know that we’ve stopped supporting browsers that have JavaScript turned off. A polite message just below the opening <body>
tag in header.php should suffice:
<noscript><strong>JavaScript is required for this website to be displayed correctly. Please enable JavaScript before continuing...</strong></noscript>
Add some very basic styling in style.css to make this message unmissable.
if ( 1 > (int) $width || empty($caption) ) return $content;
if ( $id ) $idtag = ‘id=“’ . esc_attr($id) . ‘” ‘;
return ‘<figure ‘ . $idtag . ‘aria-describedby=“figcaption_’ . $id . ‘” style=“width: ‘ . (10 + (int) $width) . ‘px”>‘ . doshortcode( $content ) . ‘<figcaption id=“figcaption’ . $id . ‘”>’ . $caption . ‘</figcaption></figure>’; }
First, we point the shortcodes for wp-caption
and caption
to our new function twentyten_img_caption_shortcode()
. Then, we simply copy the original function from media.php, and change the last few lines to include our <figure>
element. This now renders our boat.jpg example from above like so:
<figure id="attachment_64" style="width: 445px;">
<a href="https://localhost/wp-content/uploads/2010/07/boat.jpg">
<img title="boat" src="https://localhost/wp-content/uploads/2010/07/boat.jpg"
alt="Screenshot" width="435" height="288" aria-describedby="figcaption_attachment_64"></a>
<figcaption id="figcaption_attachment_64">Boat</figcaption>
</figure>
The Comments Form
One of the biggest improvements introduced in HTML5 is how form fields work and respond to user input. We can take advantage of these changes by using HTML5 form elements in the default WordPress comments form in three ways:
- We can set the text-input type to
email
andurl
for the relevant fields. This not only more accurately describes the input field, but also adds better keyboard functionality for the iPhone, for example. - We can add the boolean attribute
required
to our required form fields. This goes beyond WAI-ARIA’saria-required='true'
because it invokes the browser’s ownrequired
behavior. - We can add placeholder text to our form fields, a popular JavaScript method that is now handled in-browser. Placeholder text allows you to go into more detail about what information is required than a form label generally allows.
Before adding HTML, a typical comment input field might look like this:
<label for="email">Email</label> <span>*</span>
<input id="email" name="email" type="text" value=""
size="30" aria-required='true' />
After our HTML5 changes, it would look like this:
<label for="email">Email</label> <span>*</span>
<input id="email" name="email" type="email" value=""
size="30" aria-required='true'
placeholder="How can we reach you?" required />
To make these improvements in the code, we need to do two things. First, we need to change the HTML for the default fields (name, email address and website URL), and then we need to change it for the comment’s <textarea>
. We can achieve both of these changes with additional filters and custom functions.
To change the HTML for the default form fields, we need to add the following filter to the bottom of functions.php:
add_filter('comment_form_default_fields', 'twentytenfive_comments');
And then we have to create our custom function twentytenfive_comments()
to change how these fields are displayed. We can do so by creating an array containing our new form fields and then returning it to this filter. Here’s the function:
function twentytenfive_comments() {
$req = get_option('require_name_email');
$fields = array(
'author' => '<p>' . '<label for="author">' . __( 'Name' ) . '</label> ' . ( $req ? '<span>*</span>' : ’ ) .
'<input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' placeholder = "What should we call you?"' . ( $req ? ' required' : ’ ) . '/></p>',
'email' => '<p><label for="email">' . __( 'Email' ) . '</label> ' . ( $req ? '<span>*</span>' : ’ ) .
'<input id="email" name="email" type="email" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' placeholder="How can we reach you?"' . ( $req ? ' required' : ’ ) . ' /></p>',
'url' => '<p><label for="url">' . __( 'Website' ) . '</label>' .
'<input id="url" name="url" type="url" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" placeholder="Have you got a website?" /></p>'
);
return $fields;
}
You can see here that each element in the form has a name in the array()
: author, email and url. We then type in our custom code, which contains the new HTML5 form attributes. We have added placeholder text to each of the elements and, where required, added the boolean required
attribute (and we need to check if the admin has made these fields required using the get_option()
function). We’ve also added the correct input type to the inputs for author, email address and website URL.
Finally, we need to add some HTML5 to the <textarea>
, which is home to the user’s comments. We have to use another filter here, also in functions.php:
add_filter('comment_form_field_comment', 'twentytenfive_commentfield');
We follow this with another custom function:
function twentytenfive_commentfield() {
$commentArea = '<p><label for="comment">' . _x( 'Comment', 'noun' ) . '</label><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true" required placeholder="What's on your mind?" ></textarea></p>';
return $commentArea;
}
This is more or less the same as the default <textarea>
, except with placeholder
and required
attributes.
You can control exactly which fields appear in your form with these two filters, so feel free to add more if you want to collect more information.
Although relatively simple, these changes to the comment form provide additional (and useful!) features to users with latest-generation browsers. Look in Opera, Chrome (which doesn’t yet support required
) or Firefox 4 to see the results.
Header, Navigation and Footer
We finally get around to inserting the new <header>
, <nav>
and <footer>
elements. Currently, the code in /wp-content/themes/twentyten/header.php
looks more or less like this:
<div id="header">
<div id="masthead">
<div id="branding" role="banner">
…
</div><!-- #branding -->
<div id="access" role="navigation">
…
</div><!-- #access -->
</div><!-- #masthead -->
</div><!-- #header -->
It doesn’t take a genius to see that we can easily make this HTML5-ready by changing some of those divs to include <header>
and <nav>
.
<header id="header">
<section id="masthead" >
<div id="branding" role="banner">
…
</div><!-- #branding -->
<nav id="access" role="navigation">
…
</nav><!-- #access -->
</section><!-- #masthead -->
</header><!-- #header -->
You can see that we’ve left the WAI-ARIA role of navigation
assigned to the nav
element—simply to offer the broadest possible support to all browsers and screen readers.
I have replaced the #masthead
div with a <section>
because all of the elements in this area relate to one another and are likely to appear in a document outline. It seems you could delete this section altogether and just apply 30 pixels of padding-top
to the header to maintain the layout. I’ve maintained the elements’ id
s in case more than one of each are on the page—multiple headers, footers and navs (and more) are all welcome in HTML5.
While we’re editing the header, we can introduce the new <hgroup> element. This element enables us to include multiple headings in a section of our document, while they would be treated as just one heading in the document outline. Currently, the code on or around line 65 in header.php looks like this:
<?php $heading_tag = ( is_home() || is_front_page() ) ? 'h1' : 'div'; ?>
<<?php echo $heading_tag; ?> id="site-title">
<span>
<a href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a>
</span>
</<?php echo $heading_tag; ?>>
<div id="site-description"><?php bloginfo( 'description' ); ?></div>
We can edit this to include the <hgroup>
tag, and also change <div id="site-description">
to an <h2>
element:
<hgroup>
<?php $heading_tag = ( is_home() || is_front_page() ) ? 'h1' : 'div'; ?>
<<?php echo $heading_tag; ?> id="site-title">
<span>
<a href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a>
</span>
</<?php echo $heading_tag; ?>>
<h2 id="site-description"><?php bloginfo( 'description' ); ?></h2>
</hgroup>
In /wp-content/themes/twentyten/footer.php
, we have:
<div id="footer" role="contentinfo">
<div id="colophon">
…
<div id="site-info">
<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
<?php bloginfo( 'name' ); ?>
</a>
</div><!-- #site-info -->
<div id="site-generator">
…
</div><!-- #site-generator -->
</div><!-- #colophon -->
</div><!-- #footer -->
We can easily edit this to include a <footer>
and another <section>
element:
<footer role="contentinfo">
<section id="colophon">
…
<div id="site-info">
<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
<?php bloginfo( 'name' ); ?>
</a>
</div><!-- #site-info -->
<div id="site-generator">
…
</div><!-- #site-generator -->
</section><!-- #colophon -->
</footer><!-- #footer -->
JavaScript and CSS
As mentioned, we should include an HTML5 shim or Modernizr.js to make sure that all of our new elements render correctly in Internet Explorer prior to version 9. I added the following line to header.php:
<script src="<?php bloginfo('stylesheet_directory'); ?>/js/Modernizr-1.6.min.js"></script>
A couple of things to note here. First, we no longer need type=“text/javascript”
because the browser will assume that a script is JavaScript unless it’s told different. Secondly, we have to use the WordPress bloginfo()
function to point the source URL to our theme directory.
Although we are including Modernizr partly to make sure that IE can deal with the new HTML5 elements, I am serving it to all browsers because of the CSS3-checking functionality it provides.
In style.css, we need to make sure that our HTML5 elements have a display: block
attribute, because some older browsers will treat them as inline elements. For our purposes, the following line at the top of the CSS file will do:
header, nav, section, article, aside, figure, footer { display: block; }
While we’re talking about CSS, remember that we can now remove type=“text/css”
from our <link>
tags. The simplified code looks like this:
<link rel="stylesheet" href="<?php bloginfo( 'stylesheet_url' ); ?>" />
That should be enough for now. Remember, though, that changing the structure of the page by replacing older HTML elements with new ones might require some additional CSS.
We should let the small minority of users know that we’ve stopped supporting browsers that have JavaScript turned off. A polite message just below the opening <body>
tag in header.php should suffice:
<noscript><strong>JavaScript is required for this website to be displayed correctly. Please enable JavaScript before continuing...</strong></noscript>
Add some very basic styling in style.css to make this message unmissable.
/* A message for users with JavaScript turned off */
noscript strong {
display: block;
font-size: 18px;
line-height:1.5em;
padding: 5px 0;
background-color: #ccc;
color: #a00;
text-align: center; }
Still Not Convinced? A Cross-Browser Alternative
There is another option for those of you who absolutely must support users with JavaScript turned off, as suggested by Christian Heilmann. Simply wrap your HTML5 elements with divs which share the same ID name. For example:
<article id="post-123">
...
</article>
becomes
<div class="article">
<article id="post-123">
...
</article>
</div>
Then it’s just a case of adding .article
to your article CSS definition. It might look like this:
.article,
article { display: block; background-color: #f7f7f7; }
It’s worth noting that this adds another layer of markup to your code which isn’t needed for most users. I’d only recommend it if non-JavaScript users are a significant proportion of your users and/or sales.
Final Thoughts
TwentyTen was a huge step forward for WordPress; and as a piece of HTML, it is a beacon of best practice. By including some simple JavaScript, we can now open up the theme to the world of HTML5—and the additional meaning and simpler semantic code that it offers.
While we’ve addressed a good number of new HTML5 elements in this article, it really is just a starting point and you can add many more yourself. For example, you could add headers and footers to individual posts, or you might like to add the new <aside>
element. Let us know your ideas and how you get on with implementing them in the comments below!
Download TwentyTen With HTML5
To complement this article, I have created a new version of TwentyTen, with the HTML5 elements we have discussed. Download this theme from TwentyTen Five.
Other Resources
You may be interested in the following articles and related resources:
- HTML5: A Vocabulary and associated APIs for HTML and XHTML The HTML5 spec. An essential reference to bookmark.
- New Theme: Toolbox WordPress announces the sandbox theme called Toolbox, built entirely in HTML5.
- Toolbox Download the WordPress HTML5 theme Toolbox.
- Redesigning With HTML5 and WAI-ARIA Bruce Lawson takes an early crack at introducing some HTML5 into WordPress.
- Coding an HTML5 Layout From Scratch New to HTML5? This is a great place to start.
(al) (vf) (ik)