How to create a simple Drupal 7 theme from scratch

Drupal theming can seem complicated and overwhelming. Even basic starter themes are filled with confusing PHP and convoluted CSS. What's a designer to do? Never fear, it is completely possible to create your own theme from scratch. This article will demonstrate a step-by-step process for creating your own Drupal theme, including a .info file, page template, regions, and CSS.

This article will assume that you have some basic knowledge of Drupal setup, theming, and terminology. It will also assume that you know all the HTML and CSS you need to build your design. This article will only cover Drupal-specific coding techniques.

The previous version of this article can be found at How to create a Drupal 6 theme from scratch. As the title suggests, that article covers the same techniques for Drupal 6.

Why create a theme from scratch?

Instructions for creating a Drupal theme usually tell you to start with an existing theme or base theme and customize it for your needs. The first problem with this is that existing themes are usually very generic. They often include options to have left or right columns (or both), lots of regions, and all the CSS that makes it work. This is fine if that's what you need, but it also results in very convoluted template files and CSS that can be difficult to understand. Much of the CSS may not actually be used on your site, but you can't remove it if you aren't sure where it might be in use. Customizing a theme like this for your own site can be frustrating.

The other problem with this is that it doesn't help you to understand how Drupal theming actually works. Working from scratch will help you to gain a better understanding of the theming system.

You may believe – as I did – that creating a Drupal theme is so complicated that it would be too difficult to build one on your own. As I hope you'll find in this tutorial, that really isn't the case.

Creating your theme

1. Create the folder structure

First you need to create a folder for your new theme. This should go in sites/all/themes or sites/yoursite/themes depending on whether this theme should be available to all sites on your installation or just one site. Give the folder a unique name that describes your theme (no spaces).

I find that it’s also useful to include sub-folders for images and CSS files. This helps to keep things organized. A folder structure for a simple theme could look like this:

  • Theme name
    • css
    • images

2. Create the .info file

In Drupal 6 and above, all themes have a .info file that provide some basic information about the theme. Some of this information is used on the administer themes page, and it also determines which features are available in the theme settings.

This is a simple text file with a .info extension. The name you give this file will be used by Drupal internally (e.g. if you call the file yourtheme.info, the internal label will be "yourtheme").

Inside the .info file, we’ll need to enter information about our theme in key-value pairs. These include:

name - a human-readable name of your theme

description - a description of the theme

core - the version of Drupal this is designed for

engine - the templating engine your theme will use (probably phptemplate)

regions - the block regions available. The machine name of the region is included in square brackets (e.g. regions[sidebar_first]). This will be used to insert the region into your template files. The label you assign to it will be used in the admin interface when assigning blocks to your regions.

In Drupal 7 you must include the content region in addition to any custom regions you need for your theme. It is a good idea to use the standard Drupal names for sidebar regions ("sidebar_first" and "sidebar_second" in Drupal 7). This will enable Drupal to add classes to the <body> tag to indicate which sidebars are in use (no-sidebars, sidebar-right, sidebar-left).

features - theme components that can be turned on or off in in the Drupal admin interface (e.g. option to specify a site name, mission statement, logo etc.). Only the features listed in your .info file will be available on the admin page and as variables in your page template (more on that later). If you want to clear all features, leave it blank. To include all features, omit the features item entirely.

Using custom theme settings (features)

Some of these elements specified in the features key may not be necessary for your theme. By default, blocks are available for the standard navigation menus (main menu and secondary menu). The theme will pick up the shortcut icon (favicon.ico) file in your theme directory. If you're like me, you may prefer to code your logo image in your page template rather than uploading one through the admin interface.

Specifying these options is only necessary if you want to:

  1. code the variables into your template file and/or
  2. allow users to turn them on or off in the theme admin settings

In the case of the logo and the shortcut icon, upload forms will be made available in the admin page, which is only useful if you want to be able to change them easily.

Some of these options make much more sense for themes that will be used by a variety of different sites (e.g. the default Bartik and Garland themes). For a custom theme for an individual site they may not be as useful.

stylesheets - your CSS files. The media type is included in the first set of square brackets (e.g. stylesheets[all][] or stylesheets[print][])

scripts - any scripts required by your template (remember that Drupal comes with jQuery, so you don't need to include it here)

The scripts and stylesheet values are relative to the theme directory (i.e. the /sites/yoursite/themes/yourtheme path is automatically included). If you want to specify a file outside this directory you need to enter the path relative to the theme directory. See the comments on the .info documentation page for instructions on including an external script. 

Here is a sample .info file:

name = My Cool Theme
description = Custom theme for my site
core = 7.x
engine = phptemplate

regions[header] = Header
regions[sidebar_first] = Right sidebar
regions[content] = Content
regions[footer] = Footer

stylesheets[all][] = css/style.css
stylesheets[print][] = css/print.css

features[] = name
features[] = main_menu

More information about .info options, including default values and some additional optional keys, is available at drupal.org.

3. Understanding Drupal template files

Drupal themes are based on template files, defined with the extension .tpl.php. These files contain the html for your theme, as well as some variables that tell Drupal where to put things.

If you want to create a very simple theme, you actually don't need to have any template files at all. Your theme could be just CSS. Drupal comes with default template files for all of the code it outputs. These defaults are included with the module that generates the content for a particular element. For example, the html mark-up for a node is included with the node module as node.tpl.php. The html code for a block is included with the block module in the block.tpl.php file.

You only need to create your own version of a template file if there is something you want to change from the defaults. Otherwise, Drupal will simply use the default file. It is never a good idea to make changes to the default ("core") files in Drupal. Always use the proper method for overriding code in your theme. 

To create your own version of a template file, copy the default version from the module folder that created it, and place it in your theme folder. Sometimes it could be difficult to determine which module created a particular piece of code. If you're really having trouble, try the theme developer module.

The most important template file, and the one you will most likely want to change, is the page.tpl.php. This template file contains the code for the body of the page. The default page.tpl.php file is contained in the system module. The default does contain a lot of extra options that you might not need, so for this tutorial, we will write our own page.tpl.php from scratch.

In Drupal 7 there is also an html.tpl.php template, which includes code for the overall structure of the document (the doctype, the <head> section, and opening and closing <body> and <html> tags). We won't look at this file because there's not very much to worry about there. As always, if you want to change something you can simply copy the default from the module that created it (in this case, the system module) to your theme folder.

3.1 Create the page.tpl.php

To begin building your theme files, start by creating a new, empty text file called page.tpl.php. In here will go all the code for the main <body> section of your theme. This file consists of three elements:

  1. The HTML mark-up for your theme (containing div's, and any other major structural elements)
  2. Region definitions
  3. Variables for other content items (e.g. title, navigation tabs)

Start by creating the basic HTML structure for your page. This would include any containing div's or sections you need to create your site. For our sample theme, we'll start with a very basic structure with header, content area, sidebar, and footer:

<div id="header">
 
</div>

<div id="wrapper">

  <div id="content">

  </div>
    
  <div id="sidebar">

  </div>

</div>

<div id="footer">

</div>

 

3.2 Create your regions

Any part of your page that you want to be editable in the Drupal interface (via Blocks) needs to become a region. In our sample site, this includes header, right sidebar,  content area, and footer. Don't forget that all of your regions need to be introduced in the .info file first.

You don't actually have to define any custom regions – Drupal will provide a set of default regions if you don't specify any. These include header, highlighted, help, content, sidebar_first and sidebar_second.

In Drupal 7 variables, including regions, are inserted using Render Arrays. You don't need to know much about this right now, except that it is a way of ouputting regions and other elements into your page template. I will try to explain this in more detail in a future article.

With the exception of the content region, there is a chance that the region will be empty (e.g. if there are no blocks placed in a given region). You probably don't want anything to be printed if the regions are empty. These variables need to be surrounded by conditionals, checking to make sure they exist.

The following code creates a conditional to check that the footer region has something in it, then prints out the contents:

<?php if ($page['footer']): ?>    
  <?php print render($page['footer']); ?>
<?php endif; ?>

Repeat this code for each of your regions, replacing the $page['footer'] variable with the machine names of your regions (the ones in the square brackets in your .info file). The content section is an exception, in that it does not need to be surrounded by a conditional statement (there will always be something in the content region).

Whether you want the containing div's to be inside or outside of the conditional depends on the design of your site – do you want the <div> to be there if the region is empty?

Here is what our sample page.tpl.php file looks like at this stage:

<div id="header">

</div>

<div id="wrapper">
 
  <div id="content">
    <?php print render($page['content']); ?>
  </div>

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

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

3.3 Add variables for basic page elements

There are other parts of the page that exist outside of regions. At this stage we will add variables for key page elements, including the page title and Drupal navigation tabs. As described on the Drupal documentation page, there are many variables available to page.tpl.php. What you include depends on what functionality you want your theme to have. Do you want breadcrumbs? If so, put in the $breadcrumbs variable.

Some of the most common are:

  • $site_name
  • $logo (the logo uploaded through the theme settings; only useful when you're implementing the logo theme feature)
  • $title (the page title)
  • $main_menu
  • $secondary_menu
  • $breadcrumbs

There are also some variables associated with Drupal administration:

  • $tabs (menu used for edit/view admin menus, among other things; often used by modules)
  • $messages
  • $action_links

And some other useful  variables:

  • $base_path (the path to your site root)
  • $front_page (the path to the site front page)
  • $directory (the path to your theme)

Variables are inserted using the Render API, like this:

<?php print render($tabs); ?>

A note on menus and theme settings

Blocks for the standard navigation menus (main menu and secondary menu) are provided by default but they are also available as variables. You could use either method for inserting the links into your page template. If you want to be able to move them around easily, they should be blocks. Otherwise they can be hard-coded into the template using variables if you prefer.

Similar logic can be used for determining which way to display other common site elements such as the logo. Do you want to be able to easily change the logo through the admin interface or turn the main menu off? If so, use the theme settings and associated variable. If not the variable can be placed directly into the page template.

The desirability of being able to manipulate these elements within the admin interface depends on the purpose of your theme. If this is a generic theme, to be used by many different sites, it is probably a good idea to make it easy to change the placement of navigation menus, or to upload a new logo. If you are designing a site for a client, on the other hand, you might not want them to change the logo or move around the navigation menus.

Another important point is that menu links are returned as an array. When you include this in your page template, you need to expand it by sending it through theme() which will expand the array:

<?php if ($main_menu): ?>
  <?php print theme('links__system_main_menu', array('links' => $main_menu, 'attributes' => array('id' => 'main-menu'))); ?>
<?php endif; ?>

For this example, I have included the logo as a simple <img>, rather than using the $logo variable, since I don't need or want to be able to upload that through the admin interface. This also demonstrates the $base_path, $directory, and $site_name variables.

Note that in Drupal 7 there are now $title_prefix and $title_suffix variables. Modules may make use of these items, so it is important to include them in all themes.

Also note that some variables need to be displayed using the render() function, while others can simply be printed. How do you know the difference? If there variable is an array (as listed on the page.tpl.php reference page), you need to use render(). If not, you can just print the variable (<?php print $variable; ?>). If you're having trouble you can also check the default page.tpl.php and see how they did it there.

This is what our page.tpl.php file looks like now:

<div id="wrapper">

  <div id="header">
    <a href="<?php print $front_page;?>">
      <img src="/<?php print $directory;?>/images/logo.png" alt="<?php print $site_name;?>" height="80" width="150" />
    </a>
 
    <?php if ($main_menu): ?>
      <?php print theme('links', $main_menu); ?>
    <?php endif; ?>

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

    <?php print render($messages); ?>
    <?php if ($tabs): ?><div class="tabs"><?php print render($tabs); ?></div><?php endif; ?>
    <?php if ($action_links): ?><ul class="action-links"><?php print render($action_links); ?></ul><?php endif; ?>

    <?php print render($page['content']); ?>
  </div>

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

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

</div>

4. Build your CSS

This step works pretty much the same way as it would on any other site. You probably have your own techniques for this, so go to it! 

5. Try it out!

Now that your page template is created, you can enable your theme and see how it works. If you make some changes and don't see them reflected on your site, try clearing the theme registry (cache) by going to Site Configuration > Performance and clicking the button to clear cached data.

This is almost always the solution to problems with changes to your theme not appearing. If you haven't already, click the little "+" button next to the title on the Performance page to add this to your custom toolbar.

6. Create your screenshot

In the Drupal admin interface a screenshot will appear next to your theme name. To make this image, simply create a screenshot of your finished site, resize and crop to 150x90, and place it in your theme directory. It should be named screenshot.png

Drupal does have some guidelines on creating a screenshot file, but these only need to be followed if you intend to contribute your theme to the Drupal theme repository.

Download theme files

Download the theme files created for this tutorial: mycooltheme.tar.tz.

For this example I have included a very simple stylesheet to show the arrangement of the basic elements. If you want to use this theme as a starting point for your own design you can easily remove this CSS.

What’s next?

This is just the tip of the iceburg as far as Drupal theming goes. There are many, many other things you can do with your theme. Hopefully this is enough to get you started with your own theme, and to gain a better understanding of the Drupal theming system. Check back at A Padded Cell soon for more articles on Drupal theming techniques.

Discussion

To discuss, ask questions or comment on this article please see the Webmaster Forums discussion on How to create a Drupal 7 theme from scratch. Please post there if you find anything in this tutorial to be confusing.

Resources

AttachmentSize
mycooltheme.tar.gz7.76 KB
Megan McDermott's picture

About the Author

Megan is co-founder and editor of A Padded Cell and administrator at The Webmaster Forums. She has been designing websites since 1997, with expertise in design, information architecture, usability, HTML/CSS, Drupal theming, and more. She is available for short-term or ongoing freelance work in any of those areas. Read her web design blog at MeganMcDermott.com or check out her portfolio.