Barcelona Pavilion, Mies van der Rohe
kalidoskopika on flickr

On Wednesday March 4 I’ll be giving a presentation at DrupalCon DC titled Limitations of the Drupal Theme Layer. This post outlines the major points of my presentation.


While the Drupal theme layer has seen a lot of important changes and improvements over the last few releases, its core concepts and assumptions remain the same. This presentation will examine the current situation with the following goals.

First, I hope to get you to recognize these assumptions and understand their limitations. This will make you a better designer when working with Drupal and a better themer when implementing someone else’s design.

Second, I want you to start thinking about new ideas and paradigms for the Drupal theme layer. One of the Drupal community’s major goals is to be a friendlier platform to designers and themers — and that means making real improvements to the guts of our system.

Here is a quick outline of this presentation:

1. Designer errors: three examples of what I call “designer errors” — decisions you’ll see in a comp or mockup that Drupal can’t do well.

2. The problem: the core concepts in Drupal’s current theme system and how these lead to the problems in Part 1.

3. The solution: the requirements for a stronger theme system that solves these and many other problems.

4. Next steps: my 2 cents on the next steps for Drupal core and the community for actually getting to a better theme system.

Part 1. Designer errors

In the DS office we have a term “designer error.” This is basically what we tell a client when we start building a project and someone finds something I’ve drawn in a design that is either:

1. Hard to do

2. Hard to maintain (easy to do but requires the “wrong kind” of code)

3. Can be done with a Drupal-friendly alternative (easy to do, easy to maintain)

Whenever we find a designer error, our approach is to come up with an alternative that will fit the client’s design/UI needs but will also work well with the “grain” of Drupal. That is, we try to move any items in categories 1 and 2 into category 3.

Designer error 1: Moving something out of its vertical stack.

“Node links in the sidebar, or the page header, or anywhere but … within the node div.”

Designers sometimes think that Drupal gives you have a bunch of little UI building blocks that you can move around as you wish. This is half-true, but it’s the other half that’ll burn you: the building blocks are actually stuck together and isolated in little piles. In Drupal, you have the node stack which is all the pieces of a node teaser or page but they are all pretty much stuck together. You have the block stack. The view field and view style plugin stack. The form stack. And so on.

Within one of these stacks you have some freedom, but try to move something from inside one to another. For example, say you want to move that “View more” link from the view listing inside a block into the block title. Good luck.

Designer error 2: Changing the formatting of a core element.

“Why can’t that blog archive block be a dropdown? Why can’t the pager have a text input field? Why can’t multiple blocks be put behind a tab interface?”

All three of these requests fall into the “very possible” range of requests. But as soon as the request is for a specific instance of theme_item_list(), or theme_pager(), or a single block region, you’ve suddenly left the beautiful land of generalized theme overrides and into the land of custom hack and slash. Are you going to explain to the designer that the archive block theming will affect all lists of links in Drupal or are you going to come up with some context/global variable based hack for this one case?

In short, if the change requires serious modification, restructuring, or wholesale rewriting of markup that is shared by many players in Drupal, you’re in trouble.

Designer error 3: Expecting visual consistency across different modules.

“Why doesn’t this node/profile/term/view item look the same as this other node/profile/term view item?”

This is a task that is very feasible to accomplish. But how many people have spent too long hunting down every last div and every last class generated by Drupal to make sure that module A’s component looks like B, C, D, and E? Visual consistency is a very difficult problem for a modular project like Drupal, and it starts with the difficult problem of consolidating markup. With markup spread out over dozens of theme functions implemented by as many modules, consolidating markup can be a daunting task.

Observation 1:

Technically all of these designer errors are 100% possible in Drupal. You can do these things. But it is often not wise to do them. You will have on your hands a bloody mess of templates, a bad override, or much much worse. They are designer errors not because Drupal can’t do them but because it’s not easy to build and sustain these design decisions in Drupal.

Observation 2:

It’s worth pointing out that designer errors often have nothing to do with the complexity or sophistication of individual elements themselves like dynamic menus or drop shadows. They are often much more benign things. They can be as dumb as putting the Print this page link in the wrong place on the page, or expecting a component to show up by being contextually aware of some other element on the page.

Part 2. The underlying problem, or the story of theme()

The Drupal theme system has evolved quite a bit but its core driving concept is the separation of content generation from content presentation. This has manifested itself in code in a very simple and straightforward way. Consider this code snippet that in some other PHP projects might be absolutely acceptable:


// Generates a list of recent posts
function recent_posts() {
  $result = db_query(" ... ");

  $nodes = array();
  while ($row = db_fetch_object($result)) {
    $nodes[$row->nid] = array(
      'title' => $row->title,
      'body' => $row->body,

  $output = '';
  foreach ($nodes as $node) {
    $output .= "
      <div class='node'>
        <div class='body'>{$node->body}</div>

  return $output;

The Drupal philosophy says this is bad and brittle — the markup is written right into the function that generates the list of posts. If someone ever wanted to change the HTML output for this list they would have to rewrite the entire function. The solution is a very simple abstraction layer provided by the theme() function:


// Generates a list of recent posts
function recent_posts() {
  $result = db_query(" ... ");

  $nodes = array();
  while ($row = db_fetch_object($result)) {
    $nodes[$row->nid] = array(
      'title' => $row->title,
      'body' => $row->body,

  return theme('recent_posts', $nodes);

// Overriddable theme function for a list of recent posts.
function theme_recent_posts($nodes) {
  $output = '';
  foreach ($nodes as $node) {
    $output .= "
      <div class='node'>
        <div class='body'>{$node->body}</div>

  return $output;

theme() essentially acts as a layer that makes the presentation of this particular list overridable and customizable without having to reproduce the groundwork of retrieving the raw content. Since its introduction, phptemplate has also provided a standard way of generating output through template files rather than functions. Regardless, these following observations remain true of the Drupal theme layer:

1. The theme() convention is easy for new developers to understand

It is very easy for a modest PHP developer to pick up Drupal’s content generation/presentation paradigm. It leaves many of her basic assumptions alone and is of minimal burden upon the developer to further concept or abstract data from presentation. Since the developer controls both ends of this process—the content generator function and the content presentation function—it is also at her whim to design the interface between these two: Is it an array of objects? A string? How much information and context will the theme function get?

While this makes theme() easy for developers to get started with quickly, it also means that the number of content generator-theme function pairs has exploded in Drupal. It is much easier to write a new theme function whose interface is tailored to your data generator than finding an existing one that suits your needs. In fact, the above example above is an exemplar of generator-theme function pairs — the two tend to express a single continuous thought process that has taken a detour via theme(). This is one important cause of inconsistent markup in Drupal. Drupal’s modular architecture is not helped by a theme layer that invites the creation of new data-presentation pairs with little incentive for reuse or abstraction.

2. Theming is a command

Theming follows a command based paradigm. The code that generates the content also determines how its content should be presented by pushing it through a theme function. The theme function is then another series of commands. Chained together in Drupal, you can see that for any given page there exists a huge tree of markup commands:

  • theme_page
    • theme_views_view
      • theme_views_view_list
        • theme_node
          • theme_username
          • theme_user_picture
          • theme_links
        • theme_pager
    • theme_blocks
      • theme_block
        • theme_comment_block
          • theme_item_list
      • theme_block
        • theme_menu_tree

… We are just getting started.

In short, here is the source of the vertical stack problem. At any place in the tree of commands, a theme function has no access to information available to its parent and no way to change what its children have handed it. For example, theme_user_picture will pass theme_node an image of the user, not a set of parameters that theme_node might want to modify. And so on up the tree. Who as a designer or themer has not encountered the problem of wanting to modify information generated downstream on the basis of some condition or contextual information upstream?

3. theme() exists primarily as override infrastructure

theme()‘s primary purpose is to allow site builders and themers to override the default output of some part of Drupal. This is even more evident from the fact that theme functions are sometimes used to customize much more than markup — you can see any number of instances where database queries themselves are called from a theme function (see theme_comment_block(), theme_blocks() or of course theme_page() for examples of this). Drupal module developers piggyback off the override infrastructure of theme() quite often saying to themselves, "This behavior should be overridable… why don’t I put it in a theme function?" (I admit to doing so myself).

theme() is not an earnest effort to separate content generation from presentation. The fact that there is serious confusion as to who should be generating the data—for example, theme_comment_block() takes no input arguments and yet spits out the most recent comments on your site—is a key indicator of this fact. Which function is supposed to call the other? Should the theme function call the content generator or the content generator call the theme function? You will see this question answered arbitrarily one way or the other throughout Drupal.


The basic idea behind theme() has been very successful because it is overridable and easy to understand. But it has led to many other consequences that are not so positive. To get a better understanding of the problem in the abstract, let’s take a detour to examine two paradigms for approaching the split between content and presentation.

Historical interlude: Remember 1997?

Think back through your web geek past and recall the font tag. One of the first HTML tags you learned was probably like the font tag — it was easy to understand because it was a direct command. Any time you needed to center text, color it red, or make it blink, all you had to do was take your content and slam some tags around it. 1997 was a great time to be alive.

More abstractly, HTML utilized a command-oriented presentation system. You issued individual commands through tags when you wanted something to get presented a certain way, each time, every time. This gave you a lot of fine-grained control over your web page but it also made for a lot of work. Especially if you wanted to do anything drastic like change how your entire site looked.

Rule based presentation

Then someone taught you about CSS and semantic markup. At first you were skeptical — “You want me to write HTML without thinking about what the page should look like?” But after your first couple CSS projects it began to make sense. You wrote your XHTML (largely) independent of the site’s design and had the freedom to alter the presentation quickly and easily.

The real separation between content and presentation in CSS/HTML is not in the fact that the markup lives in one file (.html) and the CSS in another (.css). The real difference is that CSS/HTML represents a rule-based presentation paradigm, where the content is (ideally) represented independent of any intentions about its presentation and the presentation acts upon that content based on rules, not commands.

Command based presentation Rule based presentation
Issue commands for each instance of content Define a set of rules that apply across all instances
Content is generated with final presentation in mind Content is generated “semantically” and without presentation in mind
Easy to understand Requires abstraction and conceptual thinking
A lot of work to maintain (there are usually a lot of commands) Not much work to maintain (there are a lot less rules than commands)
Hard to achieve consistency Easy to achieve consistency

As I described it, Drupal’s theme markup layer sits firmly in the command-based paradigm. We can imagine what the recent_posts() function above would return in an alternate universe where Drupal used a rule-based system: A structured data object with additional attributes that could be leveraged to define markup rules — think of CSS id’s and classes. These rules would be loosely coupled or completely independent of the content generator and probably encompass the data provided by many other modules.

Part 3. The solution is drupal_render()

There is actually a second and quite different system for abstracting content from presentation in Drupal. It’s called drupal_render() and it is a driving force behind the Form API — to get a sense of how drupal_render() works its best to look at a sample form definition in Drupal:


function my_profile_form() {
  $form = array(
    'name' => array(
      '#type' => 'textfield',
      '#title' => t('Your name'),
      '#value' => 'Jane Doe',
    'birthday' => array(
      '#type' => 'select',
      '#title' => t('Your year of birthday'),
      '#options' => array(
        1975 => 1975, 1976 => 1976, 1977 => 1977, ...
    'about' => array(
      '#tree' => FALSE,
      'favorite_band' => array(
        '#type' => 'textfield',
        '#title' => t('Your favorite band'),
      'biography' => array(
        '#type' => 'textarea',
        '#title' => t('Your life story'),
  return $form;

Form theming is quite different from other aspects of Drupal. A form function does not hand HTML back to you — you don’t define a form array and return it wrapped in a theme function. Instead, you define your form abstractly — as a set of elements with certain types and properties. This lets Drupal pass your form around to other players in the stack — say another module wants to add a new field to your profile form — before it is finally rendered into HTML.

To do the final markup, Drupal leverages drupal_render(). It can be very bluntly characterized as a function that travels down your tree of objects and renders them one by one based on their #type or their optional #theme override. This allows forms and a few other elements in Drupal to begin to solve some of our problems:

1. drupal_render() allows elements to have consolidated markup on the basis of their type
2. drupal_render() allows elements to be modified, rearranged, etc. freely outside of their “vertical stack”

If drupal_render() sounds good to you, you can look forward to more of it in Drupal — a patch from Moshe Weitzman to Drupal 7 allows the main page content of Drupal to go through drupal_render() as well. It will give you the same kind of flexibility with the page content that you currently have with forms.

drupal_render() is small fry

While drupal_render() is a good first step towards powerful rule based theming it barely dips into the possibilities of rule-based markup. To get a sense of what’s possible, let’s take a look at a few rule based systems that Drupal leverages every day: CSS/HTML and jQuery. As I described earlier, CSS/HTML is one of the most well-know rule-based presentation paradigms on the web. On the other hand, jQuery is not strictly for presentation — it is meant to enhance and manipulate a web page dynamically. However, its extensive use of XPath and the DOM tree make it an illustrative example of how powerful rule-based architectures can be.

Consider how simple certain tasks are in CSS/HTML and jQuery. With just a few lines of code, and more importantly, with some basic clear ideas of what you want, you can

  • Style all elements of a certain type
  • Target elements by their context, not just their own properties
  • Attach dynamic handlers to very specific items with a great deal of confidence that a new item that matches your criteria will be affected

Though not exact equivalents, you can imagine some parallel tasks in Drupal markup:

  • Alter the theming of all elements of a certain type
  • Alter the theming of elements based on some combination of their own properties and their context
  • Take a page served by Drupal and return its core content in XML, JSON, RSS, CSV, or RDF while excluding supporting elements that are peripheral
  • Define markup rules that you are confident will serve not only your particular content and use case but any future content that fits your criteria, regardless of who generates it or how it is generated

drupal_render() gives us the tools to do all of these tasks but there is a difference between getting them done and doing them well. Looking at more advanced rule-based systems we can make some statements about what would make a killer templating system:

1. Rule definition should be elegant, powerful, and both leverage and transcend content structure

Suppose you hand me a form array nested several levels deep. It is straightforward to alter any single element in it, given you know its exact location in the tree. It is annoying but not impossible to write a recursion that will alter all elements of a given type in the array. And it will take you your whole afternoon to write an alteration that will affect all elements of type A without any children of type B, or all elements of type A that are 2nd-level descendants of type C.

In other words, tree arrays are best altered by PHP in small, specific portions. Making alterations to them wholesale to capture sweeping rules is not a trivial task.

On the other hand, jQuery and CSS selectors leverage aspects of DOM structure that make it easy to capture complex rules. You do not capture sets of items on the basis of their exact position or “vertical stack” in a tree. Instead, you can pick any slice you want from the DOM. Grabbing all items of a given class is a cinch in jQuery or CSS, and it’s not much harder to define far more complex rules in a jQuery selector like

div.view ul.item-list > p[@class!=foo]

This would give you all paragraphs in a list view that don’t have a certain class. Hey drupal_render(): do you want your afternoon back now?

2. Multiple attributes can be combined to express complex rules

The only tool for creating real rules through drupal_render() is the #type key. It is the main player in generalizing markup for form elements like text boxes and fieldsets. But drupal_render() runs out of ideas here — more complex rules can’t be captured.

In CSS, the most common tools at your disposal for defining rules are element names, classes, and IDs. This makes for 3 standard facets (and that leaves out many more) that can be combined in all sorts of ways to define the conditions of your rules. jQuery, which leverages XPath, allows you to use the same facets but also a far greater number of attributes (in fact, any DOM attribute) as well as an element’s position in a set, current state, etc. Both of these examples put drupal_render()’s single #type facet to shame.

You can imagine what just a few additional facets in drupal_render() might allow — for example, marking each element as “content”, “navigation”, or “metadata” would let you easily exclude all navigation from print friendly pages or CSV exports.

3. Gather all your content into a stable representation and leave it alone

There is a good reason to collect all your content into one place before beginning any presentation logic. It allows for a clean break between your content and presentation and greater interoperability — you can swap out different CSS stylesheets for the same HTML content. It also makes for compartmentalized debugging — you know that there’s a CSS issue if an element is present in your HTML source but isn’t appearing where it should.

If you can get all your page’s content into a representational structure before beginning any presentation logic in Drupal, you win big. Like the HTML/CSS split, this would let Drupal take structured content and push it into different output formats. And like the HTML/CSS split, it would make debugging much simpler — no more debug_backtrace() to discover whether things are getting screwed up in the theme layer or in a module.

To summarize, drupal_render() is the beginnings of a rule-based presentation layer. Examining other examples of rule-based systems, we can make our eventual goal to be a system with

1. Rule definitions that are elegant, powerful, and can transcend content structure

2. Multiple attributes / or a schema of attributes that can robustly describe Drupal’s content

3. A clean break point in processing between content generation and content presentation, with a midpoint where all content is consolidated and represented

Part 4. What’s next?

Progress in the Drupal theme layer means progress on two fronts:

Task 1: Improve the code and concepts that drive the theme layer, like theme(), drupal_render()

The first task is to improve our code. We need to continue switching the major portions of Drupal theming from nested theme() calls to structured arrays that can be altered and pushed through drupal_render(). This is a change that’s happening in Drupal 7 development now and while it may suffer from some of the limitations of drupal_render() I think it will be well worth it. More structured content representation, even if it be through basic nested arrays, will position Drupal for more ambitious changes in the future.

The next technical task on our hands is to begin vetting and discussing possible technologies that can improve Drupal’s rule based theming. To give some examples:

  • XSLT is an XPath-based standard that was designed specifically for templating. If you can get your content or data reprsented in XML, XSLT is waiting there for you to transform it into HTML, RSS, RDF, or any other format. It benefits from being able to do DOM processing at the bytecode level (PHP bindings with libxml / libxslt) while it has the clear downside of requiring rigorous XML markup as the intermediary stage of content representation.
  • While preparing for this presentation I ran across fQuery, a module by Steven Wittens that is a frightening proof of concept. As described by Steven: “fQuery is a PHP tool to help you deal with Drupal Form API arrays. Inspired by the jQuery library, it lets you find form elements using a simple and intuitive selector scheme, based on CSS.” Some of the example query expressions he provides are:
// Try these queries
$queries = array(
  'textfield:not([#title=Authored on])',
  'fieldset[class=foo bar]',
  '[#type=fieldset] > textfield:last-child',
  'fieldset + fieldset',
  'checkbox#sticky, checkbox#status, checkbox#promote',

Might the next generation of drupal_render() begin blurring the lines between CSS/jQuery selectors and Drupal FormAPI / structured content?

Task 2: Challenge Drupal developers to work towards more robust representations of content

Even if we find the perfect technical framework for implementing rule-based markup, we will need to come to a consensus as a community on how we will structure and represent content in Drupal. Earlier I gave the example of flagging elements generated by Drupal as either “content”, “navigation”, or “metadata”, but you can imagine any multitude of facets or attributes we might use to make up a semantic framework for Drupal’s content. Designers will have a special role in these conversations as they will be the ones who will push the possibilities of the schema. Can a rule be defined to group elements of X, Y, Z conditions together? Can a rule be defined for all cases of A when not B?

As Drupal core moves toward more structured representations, developers need to challenge themselves to take structured content seriously. Ending bad habits like designing your API and form data structure around what you want the user input form to look like will go a long way. Similarly, moving away from theme functions and towards template files is good when it encourages more rigorous data representation. But template files don’t always imply better concepting — I fail to see how user-picture.tpl.php, for example, challenges the developer to think substantively about how this data should be represented:

<div class="picture">
  <?php print $picture; ?>


I’ve shown that Drupal’s current theme system is based on a paradigm of command-based presentation that brings with it serious limitations. However, Drupal is increasingly moving toward structured representations of content that can be themed based on rules, not commands. As we make this transition it’s important that the Drupal community better understand the ideas behind rule-based systems and emulate the best aspects of other systems if not stealing their ideas outright.

We're a team of engineers and designers working on big projects. We believe in open source and in building for lasting impact.

Learn more