How to restyle and restructure your site with minimum effort

More a cheat-sheet with some explanatory notes than an in-depth tutorial, this page serves to remind you how to quickly customize aspects of the look-and-feel of your Drupal 6.x site, rather than comprehensively overhauling it.
This is not a course on HTML, CSS or Drupal's “hooks” – some knowledge of all is assumed.

Let's get cranking. Allow yourself a few seconds to create a subtheme. Why not a theme? Because a theme requires you to specify what you want, whereas a subtheme allows to specify what you want differently. Subthemes inherit all style sheets, javascript and templates (.tpl.plp files) from the theme they declare as their base. That makes using them nice and quick – and all this without hacking into existing theme code, which means that when a new version of the base theme comes out, you automatically inherit all the improvements!

  1. Dream up a name for your subtheme. From now on we'll refer to it as <subthemename>. Then create a folder by the name of your subtheme in /sites/all/themes (not in /themes !).
  2. In this folder create a file named <subthemename>.info, with the following contents:
    name = <your subtheme's display name>
    description = <whatever works for you>
    core = 6.x
    base theme = <(folder)name of any core or contributed theme or subtheme, e.g. bluemarine>
    stylesheets[all][] = <subthemename>.css
    
  3. Still in your subtheme folder, create an empty file named <subthemename>.css
  4. Unlike just about everything else, the base theme's logo does not get inherited by the subtheme. So to fill that hole in the upper left corner of your site you may want to copy the logo.png file from the base theme folder to your subtheme folder. Or you can create your own logo.png file. Drupal will pick it up automagically.
  5. There! With your subtheme folder and its three files, you've created the work horse for all your future theming efforts. Go to Administer >> Site building >> Themes to open your subtheme for business. Select the theme from the list by ticking the “Default” radio-button, then press “Save configuration”.

Done. You are now fully set up to restyle and restructure page elements, such as menus, tabs, tables, blocks. Must have taken you all of 1 minute -- told you this wouldn't hurt a bit!
As a final preparation, go to Administer >> Administration theme and save <System default> (as opposed to Garland). Using a single theme throughout (for now) will make the majority of modifications immediately obvious.

Note: if you want, you can create a screenshot.png (150 x 90 pixels) to put in your subtheme folder. If you do, it will be displayed on the Side building >> Themes page, otherwise the screenshot.png of the base theme will be inherited and shown on this page instead.

Now, let's get to the meat in the sandwich via four straightforward examples, each covering a different subtheming technique:
EXAMPLE 1: overriding a style
EXAMPLE 2: overriding a template (.tpl.php)
EXAMPLE 3: overriding a theme_hook() with a function
EXAMPLE 4: overriding a theme_hook() with a template

Restyle vs. Restructure or: CSS vs. templates

I use the word “restyling” when talking about changing colours, text decorations, auto-capitalization, etc. via style sheets (.css files). I use “restructuring” when talking about rearranging the layout inside blocks, tables, bread crumbs, submenus ... those sort of things. I'm not talking about rearranging these units in relation to their neighbors – that's theming, more so than subtheming (although there is no fundamental difference).

To restyle your site, you edit the .css file in your subtheme folder.

EXAMPLE 1a: overriding a style (generic)

To change the striping on (most) tables on your site, add this to your .css:

  tr.odd { 
    background-color: pink; 
  } 

The change will be immediately apparent on pages like Content Management >> Content or User management >> Users.
If you want to mark-up specific HTML elements on a specific page, then you'll have to rely on the developer having attached CSS style classes and/or id's to the relevant HTML elements, for example <table class=”table-nodes”>.... . You can pinpoint the CSS responsible for the appearance of an HTML element by analyzing the HTML page source in your browser, figuring out the sometimes complex hierarchy of CSS selectors that are involved. More convenient are tools like Firebug (for Firefox) that show you a full hierarchy of all CSS styles that apply to the HTML element that you hover your cursor over (nice!).

EXAMPLE 1b: overriding a style (specific)

If you have the (core) Search module enabled and a block configured to contain the search form (via Site building >> Blocks), then the following addition to your .css file will really make it stand out:

  .block-search {
    background-color: lightblue;
    border: 1px solid blue;
  } 

CSS is powerful but. can be complex, especially when you have lots of nested HTML elements. An in-depth explanation is beyond our scope, but here are a couple of handy links (with tutorials) that may help: w3schools - css, http://css.maxdesign.com.au.
See also: HTML and CSS techniques and tools.

Let's move on to do some restructuring... If your page is properly templated, the elements you want to change are rendered by .tpl.php files living, errh... somewhere. Places to look are the /modules subfolder of the module that you suspect may have something to with the page you're looking at and the /themes folder of the theme you're using. There's also a great Drupal tool: the Devel module. Similar to Firebug it recommends places to look for templates that apply to the area on the screen that you hover your cursor over.
Once found how do you then override the template? Simple: copy the .tpl.php over to your subtheme folder (don't change its name) and edit it to your heart's content!
Note: for template overriding to work, your base theme must be driven by a template engine (check the .info file of the base theme). So, Chameleon and Marvin won't work in this respect, although you can still override their styles, in the fashion demonstrated in example 1.

EXAMPLE 2: overriding a template (.tpl.php)

Let's say you want to compress the way search results are presented, by showing the title and search-snippet, but not the information regarding the type, author and modification date of the found posts. Each individual item in the search results list is rendered via a template: modules/search/search-result.tpl.php Just copy it to your /sites/all/themes/ folder and edit the file to your liking. For instance delete the 3 lines preceding the </dd>.
Refresh the theme cache, for instance by going to Administer >> Site building >> Themes and pressing the Save button. Then in the search panel, enter a word to search for and observe the results.
Once the file is in your subtheme directory, further edits to it are effective immediately, you don't have to refresh the cache again. How easy is that?

Templates vs. theme_hook() functions

Templates and theme_hook() functions are both referred to as theme hooks and aim at the same thing: to wrap HTML around some piece of dynamically generated content. Web designers prefer templates as they're predominantly about HTML, with very little PHP code. From a web designer's point of view a theme_hook() is a PHP function that wanted to be a template (.tpl.php file).

Why isn't all (sub)theming in Drupal done via templates?

  1. theme_hook() functions execute faster (although often not noticeable to the viewer)
  2. some programmers prefer writing a single function to splitting the code in a template “fed” by a preprocessor function that sets up the data ($variables) for the template to display; the latter approach may at first look a bit more complicated – we'll demonstrate it in EXAMPLE 4.

EXAMPLE 3: overriding a theme_hook() with a function

To do this, first create in your subtheme folder a file template.php that is empty bar the <?php at the top. Then identify the theme_hook() function responsible for rendering the HTML that you want to change. Let's say you want to make the way the placeholder theme applies emphasis a bit stronger. theme_placeholder() lives in includes/theme.inc. Copy the code to your template.php file, change the function name to <subthemename>_placeholder() and alter its body to what you want it to be, for instance:

<?php
  function subthemename_placeholder($text) {
    return '<strong><em>'. check_plain($text) .'</em></strong>';
  }

Refresh the theme cache by going to Administer >> Site building >> Themes and clicking the save button. Now visit, say, the Revisions tab (.../node/%/revisions) and you'll see that title and “current revision” are strongly emphasized (assuming the node has more than one revision).
As with the other files in your subtheme folder, further edits to existing functions in template.php are effective immediately, you don't have to refresh the cache again.

We mentioned above that templates are usually preferred over theme_hook() functions. Let's see if we can turn a theme_hook() ugly duckling into a template swan...

EXAMPLE 4: overriding a theme_hook() with a template

The easy part is very much like example 2 and is based on the rule that any <theme>.tpl.php file in your subtheme folder automatically overrides any existing theme hook, regardless of whether the hook is implemented through a function or through a template. So to override the theme_username() function (/includes/theme.inc) and break out the HTML in a more designer-friendly way, you just have to create in your subtheme folder a file named username.tpl.php. Give it the content below and refresh the cache.

  <span style="color:orange">*author goes here*</span>

You won't have to go far to see the effect of introducing your template, e.g. visit Content management >> Content.
Now comes the tricky part... to retrieve the author name from the PHP context in which the username theme hook is used and drop it into the HTML snippet above.
This is where preprocessor functions come in. Add the following to your template.php file (don't forget to change the subthemename-prefix to match your subtheme).

  /**
   * Implementation of <subthemename>_preprocess_hook().
   * Overrides /includes/theme.inc/theme_username($object).
   * Gets called before the template is rendered, thus allowing us to
   * set up variables to be used in the template.
   *
   * @param $variables, contains the user object that is to be rendered
   * @return nothing
   */
  function subthemename_preprocess_username(&$variables) {
    $user = $variables['object'];
    // … further logic and variable manipulation, if desired
    $variables['username'] = check_plain($user->name); 
  }

The last line makes a $username variable available in the template, so you can take advantage of it in your username.tpl.php file as shown below.

  <span style="color:orange">*<?php print $username ?>*</span>

Refresh the cache once more (Site building >> Themes) and return to the Content page. Or, for a change, visit User management >> Users.

There you go. The theme_hook() that mixed business logic with presentation in one hard to decipher function now has its concerns divided into more manageable PHP and HTML parts that may be maintained separately by designers and developers.

Comments

martende’s picture

I think that overriding of base theme is not complete. Is it bug or feature ?

to work with regions i should overwrite them in child theme.

iv tried on "abarre" theme simple overriding - and my regions were empty.

base theme = abarre

but this work:

base theme = abarre
; regions from base theme in child 
regions[sidebar_left] = Left sidebar
regions[content] = Content
regions[content_top] = Content Top
ramones79’s picture

if you add custom region(s) in a sub-theme you need to redefine all of the basic regions too or you won't get them.

I've read this somewhere in the Docs, but can't exactly find it now.

michellezeedru’s picture

Correct - found that info here http://drupal.org/node/225125 where it says that regions are not inherited from the base theme.

Thanks for this cheatsheet whoever wrote (and added) to it! Very helpful.

I also found one other little nuance in my subthemes. The title for default block "Primary Links" was changed to in my base theme, but in each of my subthemes, I had to go in and change it again. All of my other block titles were fine. I suppose it comes down to default blocks and custom blocks, as "Primary Links" is probably the only default block I used. A small concern, but a little annoying.

joelbox-Mondial-IT’s picture

Open the basetheme .info file and copy the regions into the subtheme .info file. Next is to goto admin/blocks and put the content blocks into the right regions.

wobblefoot’s picture

Furthermore, not adding regions to the subtheme's .info file may bring "undefined index in page.tpl.php" errors, where the page template (or any template looking for the region) can't find the regions' index definitions in template.php.

Make sure any regions your templates are expecting are referenced in your subtheme's .info file. If you still have problems, check they're defined properly in template.php.

This fine document should be amended-where's a wiki when you need one, eh?

martende’s picture

Its seems doesn't work to.

I have file with name
node-game.tpl.php
to override node for content named "Game" .

node-game.tpl.php:

this file works when it stays in parenttheme folder. in child theme order it not work

dankh’s picture

In order to make node-game.tpl.php work you have to place node.tpl.php in the subtheme folder as well. Just copy it from the main theme folder.

Thalassa’s picture

I have reposted this comment several times because several times I thought I had gotten a subtheme of Minnelli to work in sites/all/themes/subtheme folder. But upon closer look, I failed each time.

Now at least, I've gotten a copy of Minnelli to run in sites/all/themes/subthemename. This is what I did:

1. Copy the following files & folders from Garland to sites/all/themes/subthemename
a. images folder
b. color folder
c. style.css

2. Make a subthemename.info file with the following lines and put it in your subthemename folder:
name = subthemename
description = whatever
core = 6.x
base theme = garland
stylesheets[all][] = style.css

3. Make a SUBsubthemename folder inside your subthemename folder
4. Copy contents of minnelli folder to this subsubthemename folder
5. Rename minnelli.css to subsubthemename.css
6. Rename the minnelli.info file to subsubthemename.info and edit so it looks like this:

name = subsubthemename
description = Tableless, recolorable, multi-column, fixed width theme.
version = VERSION
core = 6.x
base theme = subthemename
stylesheets[all][] = subsubthemename.css

7. Then I cleared my cache (Administer>Performance>Clear Cache -- not sure if this is important)
8. Enable the theme, save and configure colors.

That worked for me to get the color module working. My efforts at changing the relative paths in the color file failed, but this method worked.

vivianspencer’s picture

I got this working using the patch here: http://drupal.org/node/466268 and the color.inc example found here: http://drupal.org/node/362679

davidlark’s picture

Could someone update this guide for version 7.x . I followed the instructions and am getting multiple Undefined index, Undefined variable, and Trying to get property of non-object errors. I'll continue searching.

UPDATE: 11/1/2001 - Upgrading to D7.9 fixed all but one undefined index error, and clearing the caches seemed to fix that. Also see http://drupal.org/node/870100 . Works good now.