Creating basic a sub-theme
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.
In 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.
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.
The
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:
- Create the new theme directory.
- Copy the
.info
file from the base theme into this directory. - Modify this file.
- Retain the region and settings keys from the base theme.
- Delete keys added by the packaging script.
- Create a minimum sub-theme framework.
- 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 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:
- Blocks (the machine and human names of a theme's blocks are defined in the
region
array in the theme's.info
file. - Other HTML markup.
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.
The
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.
- base theme – required if a sub-theme (otherwise forbidden), machine name of base theme
- name – required, human readable name of theme
- description – recommended, human readable description of theme
- stylesheets – reference to at least one style-sheet is required (can be empty)
- scripts – optional, references JavaScript that is part of the theme
- version – discouraged (except for custom-themes), version string
- core – required, core version theme is compatible with
- engine – optional, default = PHPengine
- regions – optional, default = Header, Left sidebar, Right sidebar, Highlighted, Help, Content, Footer
- features – optional, default = default features checkboxes
- settings – optional, default = default settings for features
- screenshot – optional, default =
screenshot.png
, name of screenshot file - php – discouraged, minimum PHP ver., default =
DRUPAL_MININIMUM_PHP
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:
- Core version: Not inherited.
- Style sheets: Inherited.
- JavaScript: Inherited.
template.php
functions: Inherited.*.tpl.php
files: Inherited.- Screen shot: Inherited
- Logo : Not inherited
- Favicon: Not inherited
- Regions: Not inherited
- Colours: Not inherited
- Theme settings: Not inherited
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:
- Header
- Left sidebar
- Right sidebar
- Highlighted
- Help
- Content
- 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.
When
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:
- templates
- stylesheets
- themable functions
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:
html.tpl.php
– to display the basic html structure of a single Drupal page.page.tpl.php
– to display a single Drupal page.node.tpl.php
– to display a node.
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.
There 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.
There
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:
- Open the template.php file in your theme for editing.
- Look for a function called
THEME_preprocess_page
(replaceTHEME
with the theme's name). - 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. - 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
.
Observe the following conventions when naming the template override file:
- Use two hyphens to separate the elements in the filename and slashes in paths.
- If the machine mane of your content type is two or more words, replace the underscore character (
_
) with a single hyphen (-
) in the file name.
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.
Adding 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].