Notes on Drupal Theming Created 12/14/08 Updated 01/17/09, Updated 02/03/09, Updated 10/25/09, Updated 06/21/10, Updated 03/22/11, Updated 12/19/11 Updated 01/20/12, Updated 04/21/12, Updated 09/19/12, Updated 12/14/13, Updated 05/23/14 Introduction These notes cover the “theming” aspects of Drupal. A theme allows you to change the appearance of an entire site. It is also possible to "theme" only certain sections of a site, select types of content, or even individual pages. For example, your theme could specify a different look for just the front page of your site. Some other things that you can do with a theme are: Change layouts, images or fonts Hide or display fields dependent on user role Dynamically respond to changes in the content or to user input Modify or replace text (for example the labels) and variables generated by modules Within the idea of “theming” is all of the final HTML generation for PHP arrays that contain the content generated by the blocks. The theming system goes far beyond the definition of CSS style rules, it includes all of the definition of a page, the regions on the page, the menu system, and the facilities to take the output of the blocks and create HTML. For instance, a block might create a table as an array of arrays of strings in the PHP, and the theme’s table processor will generate the TR, TD elements and have them reference the theme’s CSS rules for tables. The theming system uses a PHP template system for of its behavior This document is organized as: standard themes, starter themes, interesting themes, and theme development. This document is focused on version 6. There may be new features in Drupal 7, but we are not using that yet. Resources http://www.apaddedcell.com/how-create-drupal-6-theme-scratch Drupal theming guide: http://drupal.org/documentation/theme “Drupal 6 Themes: Create new themes for your Drupal 6 site” by Ric Shreves. Packt Press, September 2008. List price $39.99, Amazon price $36.35, used from $1.95. Rated 3.5 stars from Amazon.com, but the reviews were high scattered, and low used price reflects this. Standard Themes The standard theme is Garland in Drupal 6. This looks like: Page 1 of 10 Others include PushButton, Minnelli, and BlueMarine. Starter Themes Zen The ultimate starting theme for Drupal. If you are building your own standards-compliant theme, you will find it much easier to start with Zen than to start with Garland or Bluemarine. This theme has fantastic online documentation and tons of code comments for both the PHP (template.php) and HTML (page.tpl.php, node.tpl.php). Foundation This is a liquid layout theme that handles layout for one, two, and three columns based on which blocks are enabled using CSS, not tables. There is not a lot of "pretty" in this theme as it is meant to be used as a starting point for theme creators to customize with their own look and feel. Hasn’t changed since 2008. Finished Themes Aquia Marina One of the first distributed themes from Aquia, this looks quite clean and well-laid out, with very corporate blues and greens. Nitobe Nitobe is a fixed-width, content-first theme based on the 960 Grid System. It supports two or three column layouts in several configurations and a header image area that can be set to a fixed image or selected at random. In addition to the column region, a four region area spanning the width of the layout is provided between the content and footer regions. Current version is from late 2010. Baron I used this during early 2008. Insignia I created this during early 2008. Developing Themes in Drupal The theme development process begins with the primary files in a theme, particularly template.php, style.css, and page.tpl.php. As of release 6.0, there is a .info file which is also required. The resulting output is created by using the different tpl files in combination. A diagram is shown below: Page 2 of 10 The now-standard template engine is called PHPTemplate, and is described below. Earlier versions of Drupal didn’t use a standard template engine, and so each template was a bit more complex and ad-hoc. The .info file defines the metadata such as the theme name, author, version, features, regions, stylesheet(s), etc. and use of the template engine. The internal name of the theme is also derived from this file. For example, if it is named "drop.info", then Drupal will see the name of the theme as "drop". An example .info file would be: name = Barron description = XHTML & CSS based two column theme core = 6.x version = 1.0 engine = phptemplate regions[sidebar] = Sidebar regions[content_top] = Content Top regions[content_bottom] = Content Bottom regions[footer] = Footer features[] = logo features[] = favicon features[] = name features[] = slogan features[] = mission features[] = node_user_picture features[] = comment_user_picture features[] = primary_links features[] = search stylesheets[all][] = html.css stylesheets[all][] = layout.css stylesheets[all][] = style.css stylesheets[print][] = print.css This defines a theme named ‘Barron’ to be written for Drupal 6.x, be currently in version 1.0, use the phptemplate engine, and have the additional regions Sidebar, Content Top, Content Bottom, and Footer. The features provided include logo, name, slogan, mission, primary links, search, etc. There are a set of stylesheets used, which are organized as: html.css – contains overrides for the basic HTML elements layout.css – defines positioning rules for the container and other geometric elements style.css – defines the font, text, color, and border information for visual elements Page 3 of 10 print.css (only for the printable representation of the site) – defines overrides for the printed output. This splitting of CSS into html, layout, and style is a common recommendation in CSS design. Even if these rules are all in one file, the file should be in sections that are organized this way. The standard template files are: page.tpl.php, node.tpl.php box.tpl.php block.tpl.php comment.tlp.php There is also a separate style sheet for the printed version of a page, called print.css. The process of defining a region for a theme is quite simple: just add it to the .info file, and then place a print of a variable with the same name in the appropriate place of the theme’s page.tpl.php file. Content is assigned to regions through the block system and also through drupal_set_content() calls. For example, drupal_set_content('left', 'Hello there.') would assign the text 'Hello there.' to the left region (usually implemented as a left sidebar). Any regions defined in the theme are available on the block configuration page. The file called “template.php” contains the implementation of PHP functions that are used by the theme, as well as some initialization and parameter-defining scripts. In general, the themes for Drupal seem less complex than the themes for WordPress. However, the themes are better thought of as the wirework for a presentation, rather than having the detailed formatting guts of what information is shown for a posting, what is done conditionally, how are single posts shown differently than multiple posts/archives, etc. Elements of the Foundation Theme Page 4 of 10 Elements of the Zen Theme Elements of the Nitobe theme Header image size: 940x118 Template Files page.tpl.php page-front.tpl.php: can be used to specify a custom front page. node.tpl.php node-<nodetype>.tpl.php: optional template file to configure nodes of a specific type. block.tpl.php block-<region>.tpl.php: optional template file to configure blocks in a specific region. The PHPTemplate Facility This is used in most of the themes and is the standard template engine as of release 5.x and later. A .tpl file is one that is run for each element of a set of objects, such as each node. It appears that the main code handles the looping and runs the .tpl files. For instance, since a page has a collection of nodes, the code will run the node.tpl php file over and over. The engine avoids having loop structures be visible in the PHP files. Page 5 of 10 A template is a file designed to be friendly to the typical administrator. There is not supposed to be any complex PHP code there, because that would scare off many a user. In fact the only PHP code that typically appears is “if” statements. They're lightweight files used to generate the equivalent of what a theme_function does. Values can be passed into the template through variables by use of a PHP function, template_preprocess_<template_name>(). The structure of a .tpl file is actually standard PHP. There is a context object provided in the global namespace. For instance for block.tpl.php it is $block. Structure of page.tpl.php This paints the outer portion of the page, working from variables such as $head, $content, $site_name, $site_slogan, $closure, $primary_links, and $language. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>"> <head profile="http://gmpg.org/xfn/11"> <title><?php print $head_title ?></title> <?php print $head ?> <?php print $styles ?> <?php print $scripts ?> <style type="text/css" media="print">@import "<?php print base_path() . path_to_theme() ?>/print.css";</style> </head> <body> <div id="page"> <div id="header"> <div id="headerimg"> <?php if ($logo) { ?> <div style="float:left; margin: 5px;"> <img src="<?php print base_path() . path_to_theme() ?>/images/headericon.gif"> </div> <?php } ?> <div><h1><a href="<?php print check_url($base_path); ?>"> <?php print check_plain($site_name); ?> </a></h1></div> <div class="description" style="margin 5px;"><?php print check_plain($site_slogan); ?></div> </div><!-- headerimg --> <?php if (isset($primary_links)) : ?> <?php print theme('links', $primary_links, array('class' => 'nav')) ?> <?php endif; ?> </div><!-- header --> <div id="content"> <?php if ($breadcrumb): print $breadcrumb; endif; ?> <?php if ($mission): print '<div id="mission">'. $mission .'</div>'; endif; ?> <?php if ($tabs): print '<div id="tabs-wrapper" class="clear-block">'. $tabs .'</div>'; endif; ?> <?php if (isset($tabs2)): print $tabs2; endif; ?> <?php if ($help): print $help; endif; ?> <?php if ($messages): print $messages; endif; ?> <?php if ($title): print '<h2'. ($tabs ? ' class="with-tabs"' : '') .'>'. $title .'</h2>'; endif; ?> <?php print $content ?> <div class="navigation"> <span class="previous-entries"></span> <span class="next-entries"></span> </div> </div><!-- content --> <div id="sidebar-right"> <ul> <li> <?php if ($search_box): ?> Page 6 of 10 <div class="block block-theme"><?php print $search_box ?></div> <?php endif; ?> </li> <?php if ($right): ?> <?php print $right ?> <?php endif; ?> </ul> </div><!-- sidebar --> <hr class="clear" /> </div><!-- page --> <div id="footer"> <php print $footer_message ?> </div><!-- footer --> <?php print $closure ?> </body> </html> In this file, there is no block object to work from, but global vars are used. $content contains the result of expanding all of the lower level objects using their template. Structure of block.tpl.php In this case the input block is $block, and the subcontent is located in $block->content. <div id="block-<?php print $block->module .'-'. $block->delta; ?>" class="clear-block block block-<?php print $block->module ?>"> <?php if ($block->subject): ?> <h2><?php print $block->subject ?></h2> <?php endif;?> <div class="content"><?php print $block->content ?></div> </div> This basically just emits the block subject and the block content. Structure of node.tpl.php In this case, the input is $node, and the subcontent is $content. <div class="node<?php if ($sticky) { print " sticky"; } ?><?php if (!$status) { print " nodeunpublished"; } ?>"> <?php if ($picture) { print $picture; }?> <?php if ($page == 0) { ?><div class="nodeTitle"><a href="<?php print $node_url?>"><?php print $title?></a></div><?php }; ?> <span class="submitted"><?php print $submitted?></span> <?php if ($terms) { ?><div class="taxonomy">Tagged: <?php print $terms?></div><?php }; ?> <div class="content"><?php print $content?></div> <?php if ($links) { ?><div class="nodeLinks"><?php print $links?></div><?php }; ?> </div> If there is a taxonomy, then the terms are listed before the content. If there are links, they are listed before the content on page 0, but after the content on page 1. $page is equal to 0 for all front-like pages (including nodes classified under a specific taxonomy term etc). Otherwise it is equal to 1. Front-like pages display a list of nodes, while pages with $page == 1 are displaying a single node. There is also a variable called $is_front that can be checked. The way that the header and footer meta data for a node is rendered is that these are treated as links on the node and are located in the global variable $links. Hence they are not specified through the node formatting (which was Page 7 of 10 typical of the fine-grain decoration through a theme as used in WordPress) but are controlled through the variable population function. If you want more detail regarding the presentation of fields within a node (these include the dates, the image attachments, etc.), see http://jodyhamilton.net/node-theming-field-deconstructing-node-content-drupal-5 Though this was written for Drupal 5, it still applies (at least) to Drupal 6. Using body_classes for dynamic layouts Drupal 6 provides a new tool for dynamic layouts called $body_classes. This enables the CSS rules of your theme to be told what sidebars are active, and hence you can have different layout rules for when there is one sidebar, two sidebars, etc. The previous approach to handling this relied on tables that conditionally rendered content, etc. To use this, just add $body_classes to the body tag of your page.tpl.php file, as in the following: <body class=”<?php print $body_classes; ?>”> The values of $body_classes include the following: . Condition no sidebars one sidebar left sidebar visible right sidebar visible two sidebars front page not front page logged in not logged in page visible node visible Class Available .no-sidebar .one-sidebar .sidebar-left .sidebar-right .two-sidebars .front .not-front .logged-in .not-logged-in .page-[page type] .node-type-[name of type] Note that this also enables you to have different layouts for front page and not front page, logged in and not, etc. Using the template.php file If you want to override a theme function not included in the basic list (block, box, comment, node, page), you need to tell PHPTemplate about it. To do this, you need to create a template.php file in your theme's directory. This file must start with a PHP opening tag <?php but the close tag is not needed and it is recommended that you omit it. Also included in the file are stubs for the theme overrides. These stubs instruct the engine what template file to use and which variables to pass to it. Using JavaScript in themes Starting from a basic Drupal 6 installation, the Garland theme is used. That has the following headers/includes: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="shortcut icon" href="/dev/misc/favicon.ico" type="image/x-icon" /> <title>Themes | therisbergfamily.com</title> <link type="text/css" rel="stylesheet" media="all" href="/dev/modules/node/node.css?i" /> <link type="text/css" rel="stylesheet" media="all" href="/dev/modules/system/admin.css?i" /> <link type="text/css" rel="stylesheet" media="all" href="/dev/modules/system/defaults.css?i" /> <link type="text/css" rel="stylesheet" media="all" href="/dev/modules/system/system.css?i" /> Page 8 of 10 <link <link <link <link type="text/css" type="text/css" type="text/css" type="text/css" rel="stylesheet" rel="stylesheet" rel="stylesheet" rel="stylesheet" media="all" href="/dev/modules/system/system-menus.css?i" /> media="all" href="/dev/modules/user/user.css?i" /> media="all" href="/dev/themes/garland/style.css?i" /> media="print" href="/dev/themes/garland/print.css?i" /> <script type="text/javascript" src="/dev/misc/jquery.js?i"></script> <script type="text/javascript" src="/dev/misc/drupal.js?i"></script> <script type="text/javascript" src="/dev/misc/tableheader.js?i"></script> <script type="text/javascript"> <!--//--><![CDATA[//><!-jQuery.extend(Drupal.settings, { "basePath": "/dev/" }); //--><!]]> </script> <!--[if lt IE 7]> <link type="text/css" rel="stylesheet" media="all" href="/dev/themes/garland/fix-ie.css" /> <![endif]--> </head> <body class="sidebar-left"> With the Foundation theme, it was similar. The key is that there are two variables to be rendered in the theme $styles, and $scripts. These variables contain the full list of styles and scripts registered with the Drupal environment. The registration is typically done by the module. For instance suppose that module X wants a CSS file. The module would call drupal_add_js(<pathname>) such as: drupal_add_js('misc/collapse.js'); Through this approach, a module could request that the scripts and css generate referenced in the header of page, not matter which theme is being used. The typical use is module-centric, since presumably only module developers (not page content developers) know how to use jQuery, for instance. The sources of the scripts or css can be in the modules or themes directories. Also, it can be in the “libraries” directory, which was added more recently. Examples are the population of jQuery libraries, which can be referred to by modules. However, a theme could also make these calls. For instance our changed copy of Foundation, we have: <?php // $Id: template.php,v 1.3 2008/06/23 12:08:02 add1sun Exp $ drupal_add_js('sites/all/libraries/js/jquery-1.6.4.min.js'); drupal_add_js('sites/all/libraries/js/jquery-ui-1.8.16.custom.min.js'); drupal_add_css('sites/all/libraries/css/redmond/jquery-ui-1.8.16.custom.css'); Does this depend upon the JQuery UI module? Actually, no. That module was a way of understanding the above, and is a helpful way for developers who don’t know the structure of jQuery UI as clearly to carry out the above. It also provides a module name to mark as a dependency if you were writing a module that wanted to depend upon jQuery UI being provided. Hence, we have taken the jQuery UI module out of our Drupal 6 developer area. For Drupal 7, this is greatly simplified, since a closer-to-current version of jQuery is provided in core (Drupal 6 has 1.2.6). Responsive Theming James Jonas just spun up another test website for a project called http://cafe.us. I was curious so I took another look at responsive themes for mobile phone. (1) Omega was the correct theme. (2) It needed a subtheme: http://drupal.org/project/respond (7.x) Page 9 of 10 This is still pretty ugly, and needs some work, but it does provide a little better look and feel inside a mobile phone. Here is a screen shot: Next test - jQueryMobile - Services 3.x against iOS5 (iPhone) Page 10 of 10