A peek at Drupal 7 theme system changes

[Update Feb 20, 2011: I presented an update of my Grok Drupal (7) Theming session at Drupal Design Camp Los Angeles. A write-up with links to the slides and video can be found in the post titled (naturally enough) "Grok Drupal 7 Theming - Update".]

[Update January 2011: Drupal 7.0 has been released!]

Every major release has introduced significant changes to the core API. There's no backwards compatibility between major Drupal releases; you can't run Drupal 7 modules on a Drupal 6 site, for example. In fact, if you tried you'd likely end up with the white screen of death. This willingness to let go of past thinking and old code has allowed Drupal to be as awesome as it is today.

For theming, fortunately, the changes have usually been rather incremental — a short checklist of minor changes to themes to get them working in the newer Drupal. And none was as radical or challenging as the change from Drupal 4.5's xtemplate theming engine to the phpTemplate engine that emerged around Drupal 4.6 and became part of Drupal 4.7's core. That "upgrade" actually required total reimplementation. You simply could not just tweak an xtemplate-powered theme into a phpTemplate-powered theme. Thankfully, since then it's been a much easier road for theme upgrades.

That is, until now.


PINGV Creative

Many Drupal themers will be daunted by the changes in Drupal 7. But we're excited about the new power.


50 (or so) drops make for an ocean of changes

Drupal 7 represents just about as big of a theme refactoring as possible, without replacing the core phpTemplate theming engine. I won't say this upgrade is harder than the xtemplate-phpTemplate migration. No, we're still working with the same basic concepts, same general approach — in theory, anyway. But the changes are still enough that anyone looking at doing a theme upgrade from Drupal 6 to Drupal 7 might seriously consider simply rebuilding it from the bottom up, rather than trying to apply this change here, that change there … for 50-something changes.

At my Drupal theming presentation at DrupalCon San Francisco, I touched upon some of the big things that D7 theming is introducing. There's video on the DCSF site. Here are my slides:

But a more detailed rundown of the changes are (mostly) detailed at drupal.org/update/theme/6/7.

There are 49 changes documented so far, as I write this (with Drupal 7 still in alpha), and a few others to boot. In fact, some changes are not documented there yet or are actually incorrect. (See bottom of this post for links to issues where I and others are working to correct some of these.)

So without trying to be comprehensive, here is an introduction to some of the Drupal 7 theming changes that will make Drupal theming even more powerful:

html.tpl.php

As I write this, nothing about html.tpl.php is documented in "Convert 6.x themes to 7.x" in the Drupal.org handbooks (and this is something I'm working to change). html.tpl.php is now the new page wrapper for your theme. Here you have the DOCTYPE declaration, the <head> info, and page closer.

modules/system/html.tpl.php contents

Here's a marked-up image of what you find in html.tpl.php:


Breakout of what is new

The html.tpl.php template in Drupal 7 takes over the webpage container duties previously managed by page.tpl.php in Drupal 6.


The html.tpl.php template in Drupal 7 takes over the webpage container duties previously managed by page.tpl.php in Drupal 6. In Drupal 7, the latter template contains pretty much all of what you see on the screen; the <head>, <html> and <body> tags, as well the DOCTYPE declaration, are all moved to the html.tpl.php file. The page.tpl.php template is actually called from within the html template. Note: You can always find the base templates Drupal is sending your way by finding the *.tpl.php files within Drupal core's modules' folders, and in most of the contributed modules.

As you can see, this template has taken up the markup and renderings for pretty much all of the meta wrapping around the actual page that gets displayed, including new items $page_top and $page_bottom. page.tpl.php gets called this way:

<?php
print $page;
?>

This template is pretty straightforward, and something most themers won't even have to deal with. But if you forget this template, you're going to run into some problems. So remember to include it. But if you include it for the purposes of override, be careful or you could run into problems. [Thanks to Pasqualle's comment for correcting me on this!]

More: api.drupal.org documentation on html.tpl.php and an issue for documenting html.tpl.php implementation and changes.

region.tpl.php


diagram of nested templates

Regions templates contain the content area, sidebars, footer, etc. By default, they follow Drupal core's <strong>region.tpl.php


Yes, regions now have templates. The active code Drupal 7 core provides for region.tpl.php is this:

<div class="<?php print $classes; ?>">
  <?php print $content; ?>
</div>

As usual, you declare your regions in the theme's .info file. If you don't declare regions, you will get these by default:

The various regions are called out and rendered by code in the page template, page.tpl.php, by using a syntax identifying the region as a variable, e.g.:

<?php print render($page['help']); ?>
<?php print render($page['sidebar_first']); ?>
<?php print render($page['sidebar_second']); ?>

Of course, to conditionally print the region only if it has stuff to display, it might look something like this:

<?php if ($page['highlight']): ?><div id="highlight"><?php print render($page['highlight']); ?></div><?php endif; ?>

More: api.drupal.org documentation on region.tpl.php.

Hidden regions

This is a neat little one. It's a way to define regions that are available for modules but are not available in the blocks administration area. Why would you want to do that? The Drupal.org Handbook's theme update page explains a use case:

The page_top and page_bottom regions are hidden, which means they will not show up on the blocks administration interface. When doing site-specific themes, it might also be useful to add more hidden regions (to provide ways for modules to add output to more places in the theme without defining blocks showing up on the blocks interface), you can do that via the regions_hidden[] .info file array which is new to Drupal 7:

theme .info file extract:

regions[content] = Content
regions[help] = Help
regions[page_top] = Page top
regions[page_bottom] = Page bottom
regions[indicators] = Indicators
regions_hidden[] = indicators

In this above example, the region named "indicators" is additionally defined as a hidden region.

Two ways of hiding elements with CSS

Speaking of hiding things, there are now two standard ways to hide content using CSS:

  1. .element-hidden — to hide an element from all users. ("An example would be a collapsible fieldset that will be expanded with a click from a user. The effect of this class can be toggled with the jQuery show() and hide() functions" — handbook.)
  2. .element-invisible — to hide an element visually, but keep it available for screen-readers. ("This class should be used for information required for screen-reader users to understand and use the site where visual display is undesirable.... This class must not be used for focusable elements (such as links and form elements) as this causes issues for keyboard only or voice recognition users" — Ibid.)

It's extremely handy to have these predefined, especially as browser-viewed design patterns get increasingly complex and specific to particular use cases. All themers have to recognize that they are implementing an application interface, not a "web page" as conceived by many since the advent of Netscape in 1994.

All titles in templates get prefix and suffix

Thusly:

<?php print render($title_prefix); ?>
<?php if ($title): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
<?php print render($title_suffix); ?>

Why?

One reason is to generate contextual links — such as a handy block edit link right there where the block is displayed, visible to and accessible by users with appropriate permissions.

Granular display of $content info in node.tpl.php

Now this part is really cool. It no longer requires preprocess to break apart node $content output. Of course, you could print each field on its own, but what a pain in the ass!

But in Drupal 7, you now have two new functions for printing out content in a node or user profile:

  • render()
  • hide()

Let's look at an example taken from node.tpl.php:

<div class="content">
  <?php
   
// First we hide the comments and links now so that we can render them later.
   
hide($content['comments']);
   
hide($content['links']);

   

// Then we print the content. Comments and links end up not included here.
   
print render($content);
 
?>

</div>
    // Then we print the links and comments separately

<?php print render($content['links']); ?>

<?php print render($content['comments']); ?>.

What this means is that you can pull components or fields out of your template's big $content blob and display them in a more controlled way — e.g., to split them out of the "content" div, as illustrated above.

Fields are in core


fields are in core

With fields now in core, if you weren't CCK-prolific, you have more theme components to account for by default.


This isn't so much a theming-specific thing, but it's really cool! The field.tpl.php template is pretty straightforward:

<div class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
  <?php if (!$label_hidden) : ?>
    <div class="field-label"<?php print $title_attributes; ?>><?php print $label ?>:&nbsp;</div>
  <?php endif; ?>
  <div class="field-items"<?php print $content_attributes; ?>>
    <?php foreach ($items as $delta => $item) : ?>
      <div class="field-item <?php print $delta % 2 ? 'odd' : 'even'; ?>"<?php print $item_attributes[$delta]; ?>><?php print render($item); ?></div>
    <?php endforeach; ?>
  </div>
</div>

In there, like in Drupal 6's filefield module template, it's pretty clear where the <div>s are, so you can get at your markup output.

The box is dead

Ding dong! box.tpl.php is no more. What was the box for? Good question! Moving on….

Search in the theme is dead! Long live search in a block!

Because it seemed silly to have search live in both places, the search form call in page.tpl.php is no more. You can place the search form using its available Drupal block.

Template suggestions' delimiter change

Template suggestions now use a '--' delimiter rather than the '-' that was used in Drupal 6. To illustrate:

  • page--front.tpl.php
  • node--blog.tpl.php
  • node--event-performance.tpl.php

Note that word separations within an element remain '-'. This allows for cleaner logic in handling instances of hyphenated elements, so the double hyphen always indicates a more targeted override of what comes before the '--'.

preprocess, process and process_html

This is a change that really deserves its own blog post (and a long one at that). Suffice to say that preprocess no longer stands alone as a high-power tool for themers.

There are now two sets of variables process functions. The first are the existing "preprocess" functions. The second are "process" functions which are run after preprocessors. All the various prefixes and suffixes apply to this second phase in the exact same way. This is useful when certain variables need to be worked on in two phases.

For example, adding classes into an array for the "preprocess" phase then flattening them into a string in the "process" phase so it's ready to print within a template.

Also there's now preprocess_html. Documentation issue: drupal.org/node/579698

This alone should be enough to inspire the dedicated Drupal themer to brush up on her PHP chops.

RDFa


Robots kissing

"Oh Daemon! I'm so happy you're here!"


RDFa is here to stay. Robots will love it. You should just stay out of the way.

Providing extra rich robot food in every pageload is one of Drupal 7's big improvements, and that means new RDFa requirements for themers so that this layer of robot food gets loaded into the page. Here's what you want to make sure you do in your html.tpl.php template :

  1. Declare the appropriate "XHTML+RDFa" doctype:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
      "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
  2. Print appropriate <html> info, language attribute, and RDF namespaces:
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" version="XHTML+RDFa 1.0" dir="<?php print $language->dir; ?>"
      <?php print $rdf_namespaces; ?>>
  3. Print the GRDDL profile to facilitate consumption of this RDFa metadata:
    <head profile="<?php print $grddl_profile; ?>">

    See: Wikipedia info on GRDDL.

Yes, I know, this may not seem like it's relevant to a themer, but this is the future. Drupal is leading the way in CMS adoption of RDFa, which will lead to a future where the web increasingly behaves like one big machine with one big database of information. So feed the robots.

A couple of open issues

Conditional stylesheets

This is a big issue for me, and I believe for all Drupal themers: There's a question whether themers should be able to declare conditional stylesheets in the theme's .info file.

The alternative, placing code in a preprocess function, requires a modicum of PHP knowledge, which is not ideal for front-end development. Yes, there is a lot in Drupal 7 theming where PHP knowledge is extremely helpful, I know.

But conditional stylesheets, in this day and age of multiple platforms, are such a common use case it would be great not to have to bury that implementation in preprocess. Conditional stylesheets should be easy peasy to declare!

If you think so, too, please let it be known on this issue.

Documenting all of this

This blog post is I hope a helpful read for Drupal themers and front-end developers interested in working with Drupal. But it is not documentation. The documentation on Drupal.org is incomplete and inaccurate in places. Here are some issues where you might help.

This is only a start

In the coming weeks, I hope to find the time between client work and company business (and helping with testing and documentation of Drupal 7) to follow up on this post with more highlights and tips on Drupal 7 theming. If you've come across other really cool bits, please share them here.

We want to work with you!