Creating basic a sub-theme

by Gisle Hannemyr

This chapter introduces the built-in theme engine for Drupal 7 (PHPTemplate), how to create a sub-theme, intercepts, and overrides. Please note that the goal of this chapter is to provide a tutorial about the fundamentals of sub-theming, not to teach you how to build a full-featured sub-theme for a production site.

Table of contents

Introduction

Contributed themes that can be downloaded from the theme repository at Drupal.org has already been mentioned in the chapter introducing themes. In this chapter, themes will be described from the perspective of the site builder.

Designing and changing themes to give a Drupal website a specific look are sometimes referred to as “theming”. How to do this is the main subject of the this chapter.

The default template engine for Drupal 7 is called PHPTemplate, and uses PHP itself as a template engine. PHP actually started out as a template engine for Personal Home Pages. However, it has since evolved into a generic programming language. When PHP is used to create templates, most front-end designers prefer to use PHP's alternate control statement syntax This is the contol statement syntax you will find in most Drupal PHP templates.

tipIn Drupal 8, PHPTemplate is replaced by Twig. This is a computer language with a template oriented syntax that is being converted into PHP code on the fly. For those who want to work on the bleeding edge, there exists a very experimental implementation of Twig for Drupal 7.

Sub-theming

While it is possible to build a Drupal theme from scratch, the most common approach to theming a Drupal website is to create a sub-theme that reuses the main features of an already existing theme. This may be a full theme that you just modify slightly, or a base theme that is designed to be used as a foundation for a sub-theme.

The illustration below shows the files that are found in a typical theme and sub-theme.

Regions. Source: Drupal.org.

Only the .info file is required, but most themes and sub-themes will use other files as well.

The rationale behind sub-themes is to let you customise existing Drupal themes to suit you, without changing the code of the base theme. By putting the sub-theme in its own sub-directory, it will survive upgrades of the base-theme.

As an example, I'll go through all the steps required to create a sub-theme of the Bartik theme that ships with the core of Drupal 7.

See alsoThe version of Bartik that ships with Drupal 7 is not a responsive theme, so it is not a good choice for a base theme for an actual site. It is used here to make this tutorial about sub-theming simple. For a real project, you may want to use a responsive base theme such as Bootstrap. For more detailed information about sub-theming, see also the community maintained documentation that can be found at Drupal.org. Recommended reading are: Creating a sub-theme, Writing theme .info files, and Theme settings (features).

To make a minimal sub-theme, the steps are a as follows:

  1. Create the new theme directory.
  2. Copy the .info file from the base theme into this directory.
  3. Modify this file.
  4. Retain the region and settings keys from the base theme.
  5. Delete keys added by the packaging script.
  6. Create a minimum sub-theme framework.
  7. Install and enable your new sub-theme.

Below, each step is expanded with a full explanation and example of the CLI commands that you may use to create this sub-theme.

Step 1: First, go to the directory sites/all/themes below your webroot and create a sub-directory. The name of this sub-directory will be the machine name of the theme. The machine name should start with an alphabetic character. It can contain numbers and underscores, but not hyphens, spaces, special characters or punctuation. It should only use lower case characters. Do not reuse the exact name of an existing theme or module. In this example, I shall use the machine name “my_bartik“. Make this directory your working directory.

$ cd drupalroot/htdocs/sites/all/themes
$ mkdir my_bartik
$ cd my_bartik

Step 2: Copy the .info file.

Every theme or sub-theme folder need to have a .info file, named with the machine name of the theme, with .info as the file type. In this example, it will be named my_bartik.info.

$ cp drupalroot/themes/bartik/bartik.info my_bartik.info

Step 3: Modify the .info file.

You need to modify this file to fit the sub-theme framework we're going to create.

Start by changing the human readable theme name and description, and to add a line at the top with a key to declare its base theme. This means that the first three lines of my_bartik.info may look like this after the change:

base theme = bartik
name = My Bartik
description = Sub-theme of Bartik, created for the website example.com.

In the original Bartik theme, there is a key (package) that say that the theme is part of the core. Delete it in sub-theme.

There is also an optional version key, that should be omitted if the theme is uploaded to Drupal.org as a full project. If your theme will not be placed in a repo on Drupal.org (e.g. you are creating a custom theme), you may want to use this key for version tracking.

However, you must place a key in my_bartik.info file to declare what version of the core the sub-theme is compatible with. The core key is required.

This pair of keys may look like this:

version = 0.1-alpha
core = 7.x

In the stylesheet section of the the .info file, we'll need the following two lines. Delete all other stylesheets keys.

stylesheets[all][] = css/local.css
stylesheets[all][] = css/colors.css

After going through all the steps outlined above, the first nine lines of my_bartik.info may look like this:

base theme = bartik
name = My Bartik
description = Sub-theme of Bartik, created for the website example.com.

version = 0.1-alpha
core = 7.x

stylesheets[all][] = css/local.css
stylesheets[all][] = css/colors.css

Step 4: Retain the regions and settings keys.

If you've worked with a copy bartik.info, the next lines of my_bartik.info will now contain a number of keys for the regions and settings arrays. You shall just keep these unchanged.

Step 5: Delete keys added by the packaging script.

Near the end of the file, there will be a comment (starting with a semicolon) saying: “; Information added by drupal.org packaging script on date” Delete this comment and everything below it.

Step 6: Create a minimum sub-theme framework.

A sub-theme will inherit many of the properties of the base theme. There is a detailed breakdown of what is inherited below. However, at this stage I will just demonstrate the steps necessary to create an minimal sub-theme from Bartik.

First create a sub-directory to hold the style-sheets of the sub-theme:

$ mkdir css

You must also have at least one style-sheet file referenced for style-sheets to be inherited from your base theme. This file may be empty, so it is sufficient to do the following:

$ touch css/local.css

Bartik make use of the core Color module. If you want the sub-theme to support it, you also must copy the color folder, the logo.png, and the colors.css style-sheet from the Bartik base theme to the sub-theme. I.e.:

$ cp -R drupalroot/themes/bartik/color .
$ cp drupalroot/themes/bartik/logo.png .
$ cp drupalroot/themes/bartik/css/colors.css css/

Step 7: Install and enable your new sub-theme.

At this point, you may install and enable the My Bartik sub-theme. Go to the setting panel for the sub-theme and change colour to generate the actual colour scheme and a coloured version of the logo.

Layout

PHPTemplate produces the layout of a Drupal 7 website by processing something.tpl.php-files. These files specifies the HTML and CSS framework that are used to assemble a web-page. Template files may be specifies for compontents on all levels, from the top level html.tpl.php (for generic site layout), down to templates for an individuals field that is part of an entity. Drupal ships with a few template files that are used if no matching template for a component is found in the site's theme or sub-theme.

Site builders usually only work with template file overrides and associated CSS. For developers, Drupal 7 provides themable functions. Such functions may be placed in projects, and is used to process templates to provide functionality and effects beyond what is possible with HTML and CSS.

For instance, to display the value of a field named “foo”, there may exist a template file named foo.tpl.php and a themable function named theme_foo().

Themable functions for core site elements are documented in the Drupal API.

Changing the page template

If you navigate to Structure » Blocks » Demonstrate block regions you can inspect the blocks and regions of the default page template (page.tpl.php). A site can have has many page templates it want, but only the default page template can be inspected in this way.

A page template is made up of two components:

The advantage of using blocks is that their default content is configured in the Drupal GUI. The standard practice for site builders is to first create a default page template that capture all the blocks a site will hold, and populate those using the GUI. Most sites will have more templates than the default.

Render the content block:

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

HTML markup to output the content of variable $site_slogan:

<?php print $site_slogan; ?>

Normally, you start out theming by creating a wireframe of your front page in MyBalsamiq or other suitable drawing tool, and then use this layout to set up the HTML for your subtheme's page.tpl.php.

The theme .info file

The theme .info file is a text file used to store information about a theme. It consists of lines with key-value pairs. The key is on the left, the value on the right, with an equals sign between them (e.g. name = my_bartik). Semicolons are used to indicate that a line is a comment. Some of keys are associative arrays, where square bracket is used for named indices in the array.

noteThe Drupal 7 theme system caches which template files and which theme functions should be called. This means that if you add a new theme, pre-process or process function to your template.php file or add a new template (.tpl.php) to your theme or sub-theme, you will need to rebuild the theme registry by clearing the cache. Drupal 7 also stores a cached copy of the data in the .info files. If you modify any lines in your theme or sub-theme's .info file, you must refresh this information by visiting the Appearance page.

The keys that you may use are listed below with just a brief explanation. As is indicated, a key can be required, recommended, optional or discouraged. For most of the optional keys, there are defaults.

File template.php

In the file template.php, there are two main types of functions: theme function overrides and pre-process functions. The template system handles these two types differently.

Theme functions are called through theme('[hook]', $var, …). When a sub-theme overrides a theme function, no other version of that theme function is called.

Pre-process functions are called before processing a .tpl file. For instance, [theme]_preprocess_page is called before page.tpl.php is rendered. Unlike theme functions, pre-process functions are not overridden in a sub-theme. Instead, the base theme pre-process function will be called first, and the sub-theme pre-process function will be called next.

There is no way to prevent the functions in the base theme template.php from being inherited. However, it is possible to override theme functions (but not pre-process functions). If you want to disable a base theme's pre-process function, you must use hook_theme_registry_alter().

Inheritance

The following summarises what is inherited by a sub-theme from a base theme:

All style sheets defined in the base theme will be inherited, but only if you declare at least one style-sheet file in your sub-theme's .info file. The file can be empty. but it must be declared and it must exist.

Anything defined in the base theme's template.php will be inherited. This includes theme function overrides, pre-process functions and anything else in that file.However, each sub-theme should have its own template.php file, to hold additional functions and override functions.

The general method for overriding theme files is to create a file with the same file name in the appropriate sub-theme directory.

For instance, to override style.css inherited from a base theme you must create an alternative style.css style-sheet file, and place it in the css sub-directory. If you only wish to disable the otherwise inherited styles, you can create an empty file.

If you override a stylesheet, you need to declare it in the .info file for the sub-theme. E.g. add the following line:

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

Templates aren't declared .info file. If you override a template, it is sufficient to create an alternative template file in the template sub-directory.

Except for the the elements discussed above, the sub-theme will inherit everything else from the base theme. This means that there is no need to copy stylesheets or templates from the base theme unless you want to modify or override them.

Regions

A Drupal web page divided into regions. If your theme's .info file dos not define a regions array, the following seven regions are defined by default:

  1. Header
  2. Left sidebar
  3. Right sidebar
  4. Highlighted
  5. Help
  6. Content
  7. Footer

However, if this may be overridden by settings in the themes's .info. For instance, bartik.info contains:

regions[sidebar_first] = 'Sidebar first'

This creates a region with the machine name 'sidebar_first'. The content of any block assigned to this region will be placed in the array element element $page['sidebar_first']. In page.tpl.php, the following is used to render this region:

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

The layout of a Drupal generated web page shall be how regions are positioned within page.tpl.php. CSS is used to style the regions.

Each block may (or may not) be assigned to a region. This happens per theme. You probably want to exclude almost every content block from the administration theme.

regionsettings01.png

noteWhen you create a new theme or sub-theme, blogs assigned to some region will be reassigned “content“ by default. This will make any newly created administration theme look very confusing until you visit the block structure page and make sure those regions are assigned to - None -.

Intercepts and overrides

By using specific naming conventions, you can intercept and override the styling of any theme. The techniques discussed in this section enable you to customise the site as a whole or, by using named selectors, control the styling by type of content, page and user.

Intercepts and overrides can be applied to three different, but related, Drupal tools:

How you implement intercepts and overrides varies between these, but, the underlying principles are the same.

[more TBA]

Templates

Most Drupal themes will come with overrides for the following three default template files:

These files can be overridden simply by creating a new file in the sub-theme folder with the same name.

For example, to override the global page.tpl.php for an entire site, create an alternative file also named page.tpl.php in the sub-theme directory.

Many other template files may be included that control the display of more specific elements such as comments or individual fields.

Each of these files can be overridden for a specific condition simply by creating a new file in the sub-theme folder with the correct name. These file names are called “Template Suggestions”.

Template suggestions are alternate templates that are used when a specific condition is met and a matching file exists. They are usually created by modifying existing .tpl.php files.

All layers from core, modules, theme engines and themes can provide template suggestions. You can think of them as naming hints telling the system to pick and choose based on the right circumstances. The idea is simple, but it is a powerful feature of Drupal that can be used to provide another layer of customisation to any existing design.

See alsoThere is a standard set of suggestions built into Drupal and listed in the documentation as Drupal 7 Template Suggestions. A file belonging to these standard set of suggestions is used if it exists in your sub-themes templates directory. There is no need to add a pre-process function. You may also create template suggestions that is used when a condition specified in a pre-process function is met and the suggested template exists. For more about this, see: Working with template suggestions at Drupal.org.

For instance, to override the node type for the content type, you add the machine name of the type to the file name after a double hyphen. To override the template for the Article content type, create node--article.tpl.php.

To create a page template for the front page that make the font page different from the other pages, create page--front.tpl.php.

You may also override the page template for a single page. For instance, to override the page template for node 42, create page--node--42.tpl.php.

To create a template for all instances of a type of page, you may % as a wildcard for any nunber, for instance, to override the page template for all taxonomy terms create page--taxonomy--term--%.tpl.php.

More specific, to override the template for a path such as foo/bar, you create a template file named page--foo--bar.tpl.php.

In case the standard template suggestions do not offer the flexibility you need, you can add more suggestions based on your own criteria. You do this by adding a the hook THEME_preprocess_page (where THEME must be replaced withe name of your subtheme) to you sub-theme's template.php-file. For instance, you may specify different page templates for different roles. Below is an example of how to set up a custom front page template (page--anonymous.tpl.php) for the anonymous user role:

function THEME_preprocess_page(&$vars) {
  // If the function already exists, just leave the code here intact, and add
  // the three lines below to the end.
  if (drupal_is_front_page() && user_is_anonymous()) {
    $vars['theme_hook_suggestions'][] = 'page__anonymous';
  }
}

Now, copy the theme's page.tpl.php to a file named page--anonymous.tpl.php and place it inside your sub-theme's templates folder. Edit this file with customisations you want. You may, for instance, replace premium content with a notice that says that you need to register and log in to see this content.

noteThere is a standard node.tpl.php override based on content type, but there is not a standard override based on content type for page.tpl.php. However, some themes like Zen add this type of override to the template suggestions, which sometimes makes those familiar with Zen to believe that this is part of the standard template suggestions. Check the theme documentation to see if this override has been added to the template suggestions by the theme. If it hasn't, and you need it, you must add it manually, as outlined below.

An often requested override that is not included in the standard list is the page.tpl.php override based on the content type being displayed. Here is how to add it:

  1. Open the template.php file in your theme for editing.
  2. Look for a function called THEME_preprocess_page (replace THEME with the theme's name).
  3. If this function already exists, you will need to add your code to it inside an if-statement that goes at the end of this function, just before the closing bracket.
  4. Otherwise, if it doesn't exist, you'll need to create a new function.

This is how the function should look like:

function THEME_preprocess_page(&$vars) {
  // If the function already exists, just leave the code here intact, and add
  // the three lines below to the end.
  if (isset($vars['node']->type)) {
    $vars['theme_hook_suggestions'][] = 'page__' . $vars['node']->type;
  }
}

Now, copy the theme's page.tpl.php to a file named page--machinename.tpl.php and place it inside your sub-theme's templates folder. For machinename, use the machine name of the content type with underscores replaced by hyphens. You can inspect the machine name if you navigate to Structure » Content types.

Observe the following conventions when naming the template override file:

For instance, the name of a template override file for a content type with a machine name my_machine_name should be named page--my-machine-name.tpl.php.

To help you debug template suggestions, the Drupal core has a theme debug mode that can be enabled and disabled via the theme_debug variable. You can enable and disable it with drush:

$ drush vset theme_debug 1
$ drush vset theme_debug 0

The theme debug mode is used to see possible template suggestions and the locations of template files inserted in the page markup as HTML comments.

noteAdding a new template file to an enabled theme is like adding a hook to an enabled module: In both the cases, Drupal will not notice there is a new template file, or a new hook until the cache is cleared. After adding or changing a template, you always need to clear the cache in order to view the results. In addition to clearing Drupal's cache, you may also need to clear the browser's cache. In most browsers, including Chrome, Firefox and MSIE, you do this for a single page by holding down the Shift key while reloading the page. To clear the browser's cache for an entire site, press Ctrl+Shift+Delete, tick "Cache" and click "Clear Now".

Stylesheets

[TBA]

Themable functions

[TBA]

Adding a local style sheet

Usually, one wants to add some custom css to style a site. If the site is based upon a custom sub-theme, you can do this by copying one of the base theme style sheets and modifying that copy. However, a better approach is to leave the original style sheets of the base theme untouched, and instead create a local style sheet with any local styles required. This makes it simpler to upgrade the base theme without having to think about retrofitting your modifications.

In the .info file for your theme or sub-theme, Drupal lets you add additional style sheets to the stylesheets array. Below is an example of how a local style sheet (named local.css) is added to the all array.

stylesheets[all][] = css/local.css

You place this file in the css sub-directory of your theme or sub-theme.

Final word

[mumble]


Last update: 2018-07-31 [gh].