SilverStripe 2.4: Customizing the Layout


SilverStripe 2.4 Module Extension, Themes, and Widgets: Beginner's Guide

SilverStripe 2.4 Module Extension, Themes, and Widgets: Beginner's Guide

Create smashing SilverStripe applications by extending modules, creating themes, and adding widgets

        Read more about this book      

(For more resources on this subject, see here.)

We assume a good understanding of HTML/XHTML and CSS as well as a basic knowledge of JavaScript and image editing. A solid understanding of PHP with a focus on object-orientation is also highly recommended.

If you want to improve on your PHP and object-orientation coding, you might want to take a look at Hasin Hayder's book Object-Oriented Programming with PHP5 (published by Packt Publishing, ISBN 1847192564).

Templates and themes

All pages within SilverStripe are rendered using a template, which is basically an HTML/XHTML file with added control code. A template allows you to make conditional statements, create menus, access data stored in the database, and much more. Instead of using PHP for these enhancements, templates rely on a dedicated template engine. When the user accesses a page the engine replaces the control code with PHP. This transformation is then evaluated by the web server to regular markup code, which every browser can display.

A theme consists of a collection of template, CSS, and image files. Design-specific JavaScript is also stored here. Together they form the layout of a page.

Switching between themes

You can have multiple themes and switch between them whenever you like.

You're definitely encouraged to give everything described here a try. It helps you in becoming an active developer instead of a passive user. So let's get things started!

Time for action - change the default theme

Simply perform the following steps in the CMS and you're all set:

  1. Log into the CMS by appending /admin to your page's base URL. Provide the credentials you defined during the installation and you should be logged-in.
  2. Go to the general configuration page by clicking on the globe within the Page Tree section. The default text of this element is Your Site Name.
  3. The Theme is set to (Use default theme) by default; change this to tutorial.

  4. Click on the Save button.
  5. Go to the base URL / and reload the page.

What just happened?

We've successfully changed the theme of our website. It's as easy as that.

Note that you need to package your theme into a subfolder of themes/ to be able to switch between them.

While you can also put the contents of a theme directly into the mysite/ folder, keeping your system modularized is a good practice. Therefore we won't integrate the layout into the site specific folder and will instead always create a dedicated, switchable theme.

Getting more themes

There are many prebuilt themes that you can download at After unpacking a theme, copy it to the themes/ folder, change the theme in the CMS, and you're good to go.

SilverStripe 2.4: Customizing the Layout

Using a prebuilt theme really is that simple. Next, we'll take a look at the facilities for building our own themes—specifically, the template engine that powers all themes in SilverStripe.

By the end of this article and next, you'll even be ready to upload and share your own themes with the rest of the community at the address above. New contributions are always welcome.

Template engine

As described earlier, the SilverStripe architecture consists of three layers, each serving a specific purpose. The one responsible for the layout is called View, which we'll cover in detail in this article.

Another template engine?

One might wonder why it is necessary to learn another template language, while there are already so many others available and PHP can do all of it as well. The main reasons behind this are:

  • The template engine and the rest of the system fit perfectly together. After getting the hang of it, it makes the creation of templates and communication with the other layers faster and easier.
  • The available control code is very simple. Designers without a lot of programming knowledge can create feature-rich layouts.
  • It enforces good coding practice. Without the ability to use raw PHP, one cannot by-pass the other layers and undermine the architecture. Only layout and rendering information should be used within templates.

Taking a look at BlackCandy

First of all, switch back to the BlackCandy theme as we want to take a better look at it.

Within your installation, navigate to the folder themes/blackcandy/ and you'll see three folders: css/, images/, and templates/. The images/ folder contains any image used in your theme; if you like, you can create subfolders to keep things organized. The remaining folders are a bit more complicated, so let's break them down.


At first this looks a bit messy—five files for the CSS; couldn't we use just a single one? We could, but many developers consider it a good practise splitting the functionality into different parts, making it easier to navigate later on. Sticking to this convention adds functionality as well.

This is a common principle, called convention over configuration. If you do things the way they are expected by the system, you don't need to configure them specifically. Once you know the "way" of the system, you'll work much quicker than if you had to configure the same things over and over again.


This file is automagically loaded by SilverStripe's what you see is what you get (WYSIWYG) editor. So if you apply the correct styling via this file, the content in the CMS' backend will look like the final output in the frontend. Additionally you'll have all custom elements available under the Styles dropdown, so the content editors don't need to mess around with pure HTML.

As this file is not automatically linked to the frontend, it's common practice to put all frontend styling information into typography.css and reference that file in editor.css:

@import "typography.css";

If you want to provide any styling information just for the CMS, you can put it below the @import.

layout.css, form.css, and typography.css

These files are automagically included in the frontend if they are available in your theme. While layout.css is used for setting the page's basic sections and layout, form.css deals with the form related styling.

typography.css covers the layout of content entered into the CMS, generally being imported by editor.css as we've just discussed. Elements you will include here are headers (<h1>, <h2>, and so on), text (for example <p>), lists, tables, and others you want to use in the CMS (aligning elements, <hr>, and so on).


This file isn't part of the SilverStripe convention, but is a useful idea you might want to adopt. You must include it manually but it will still be a good idea to stick to this naming schema: ie.css for styling elements in any version of Internet Explorer, ie6.css for Internet Explorer 6 specifics, and so on.

What about performance?
Cutting down on the number of files being loaded is an effective optimization technique for websites. We'll take a look at how to do that.


Now that we've discussed the styling, let's take a look at how to put the content together with the help of templates.

Learning the very basics

Before we continue, some basic facts and features of templates:

  • Templates must use the file extension .ss instead of .html or .php.
  • Templates can be organized alongside their PHP Controllers, so you can use the same powerful mechanism as in the other layers. We'll take a better look at what this means a little later.
  • Page controls consist of placeholders and control structures, which are both placed within the regular markup language.
  • Placeholders start with a $ and are processed and replaced by the template engine.
  • Control structures are written between opening <% and closing %>. They look a bit like HTML tags and they are used the same way. Some consist of a single element, like HTML's <br>, whereas others consist of two or more.

Starting to use templates

Now that we've covered the basics, let's put them into practice.

Time for action - using site title and slogan

We shall use placeholders, taking a look at the code level and how to use them in the CMS:

  1. Open the file themes/blackcandy/templates/ in your preferred editor.
  2. If the syntax highlighting is disabled due to the unknown file extension, set it to HTML.
  3. Find the two placeholders $SiteConfig.Title and $SiteConfig.Tagline in the code.
  4. Go to the general configuration page in the CMS, the one where we've changed the theme.
  5. Edit the Site title and Site Tagline/Slogan to something more meaningful.
  6. Save the page.
  7. Go to the base URL and hit refresh.
  8. If you view the page's source, you can see that the two placeholders have been replaced by the text we have just entered in the CMS.

If you are wondering about the short doctype declaration: there is nothing missing, this is HTML5, which SilverStripe is already using in its default theme. If you prefer XHTML or an older HTML standard, you can freely change it. The CMS happily works with all of them.

What just happened?

The file we've just edited is the base template. It's used for every page (unless specifically overwritten). You can define your general layout once and don't have to repeat it again.

You should always try to avoid repeating code or content. This is generally called don't repeat yourself (DRY) or duplication is evil (DIE). SilverStripe supports you very well in doing this.

The site title and slogan are globally available. You can use them on every page and they share the same content across the whole website. These placeholders are prefixed with SiteConfig. as well as a $ sign. In the CMS they are all available on the site's root element, the one with the globe. By default, there are just two, but we'll later see how to add more.

Other placeholders are available on specific pages or can be different on each page. We'll come to those next.


Looking again at the we've just opened, you'll also see a $Layout placeholder. This is replaced by a file from the themes/blackcandy/templates/Layout folder. There are only two files available by default and for every standard page is used.

When a page is loaded, the template engine first finds the base templates/ (excluding the theme specific part of the path as this can vary). It's evaluated and the $Layout is replaced by templates/Layout/, which is also evaluated for further SilverStripe controls.

Ignore for the moment. It's only used for search queries which we'll cover later. We'll also add more page types so the layout's can then be replaced by a more specific template, while the base templates/ is always used.


Both files include statements like <% include BreadCrumbs %>. These controls are replaced by files from the themes/blackcandy/templates/Includes/ folder. For example, the above include grabs the themes/blackcandy/templates/Includes/ file.

Note that filenames are case sensitive. Otherwise you're free to select a meaningful name. Try sticking to one naming convention to make your own life easier later on, and also note that you mustn't include the <code>.ss</code> extension.

If you're seeing a Filename cannot be empty error, make sure the included file really exists.

Have a go hero - using page name, navigation label, and metadata title

Now that we've explored all available files and how they are used, let's take a better look at the system.

In the CMS backend, go to the Home page. Change the text currently entered into Page name, Navigation label (both in the Main tab), and Title (Metadata tab). Take a look at:

  • Where on the page they are used
  • In which file they are located (take a look at all three possible locations)
  • What template placeholders represent them

You might notice $MetaTitle, $Title, and $MenuTitle in your template files (for the moment ignore the appended .XML). We will explore these later on.

        Read more about this book      

(For more resources on this subject, see here.)

Page control overview

We've already covered some of the page controls, but let's look at a few more in detail, so you can take full control over the look of your pages.

The syntax allows the following three use cases:

  • $Placeholder simply calls the placeholder.
  • $Placeholder(Parameter) adds a parameter to the placeholder call. Think of it as a function or method call: Placeholder("Parameter").
  • $Placeholder.Subelement or $Placeholder.Cast follows the same syntax. The first one accesses a sub-element of the given placeholder, specifically the property of an object or an array element. The second one casts or formats its placeholder, for example by defining a specific date format.

More placeholders

So far we've taken a look at the site-wide placeholders, $Layout, and the three different title elements on each page. You might already have noticed some more in the templates. Let's cover the most common ones:

SilverStripe 2.4: Customizing the Layout

Casting placeholders

Remember the mysterious .XML we've already seen in the templates? It is used to cast the placeholder it's appended to.

Note that the dot (.) does not necessarily mean casting. It can also be used for a selection within a bulk of information—remember $SiteConfig.Title for example.

The two main areas for using placeholder casting are security and formatting.


Without going into too much detail, depending on where you want to use the output, you'll need to encode it differently in order to display it properly and securely.

SilverStripe 2.4: Customizing the Layout

Using $Title.XML we ensure that $Title can't contain any raw HTML as it's escaped to simple text.

Date formatting

Formatting is common with dates and times, so we'll take a short look at some useful functions for them:

SilverStripe 2.4: Customizing the Layout

Have a go hero - add the time of the creation and when it was edited

For each page's title element add when the page was created and how long ago the last update happened—recall $Created and $LastEdited. You only need to edit one of the files—simply find the $Title placeholder.

The result should look something like the following image:

Note that you'll need to use the $Created placeholder twice to output the data and time exactly like this (once for the date and once for the time—refer to the previous table).

Flushing the cache
Changes to your theme's templates/Includes/ or templates/ directories require you to manually flush the cache as these modifications are not automatically picked up; templates are cached for quicker load time. templates/Layout/ works without it. Flushing is done by appending ?flush=all to a page's URL. Don't forget this as it is one of the main sources of frustration for beginners when their changes don't seem to have any effect!


Tracking users is a common task for websites. SilverStripe provides three placeholders for making it very easy to do that:

  • $PastVisitor: If the user has visited the site sometime in the past, it returns true, otherwise nothing. The tracking is done via cookies which are automatically set by the CMS.
  • $PastMember: If the user is a registered member of the site, it returns true, otherwise nothing.
  • $CurrentMember: Checks if the user is currently logged in, returning true again. But it can also do much more, providing access to the user's data, just like $SiteConfig. For example, $CurrentMember.FirstName will output the given name of the currently logged in user.

If your site suddenly addresses you as Default Admin, you are using the default admin account and haven't yet set your name. You can do that by going to the page /admin/security/ in your installation.

        Read more about this book      

(For more resources on this subject, see here.)

Control structures

Now that we've covered placeholders, it's time to get to the second concept: control structures. As you'll recall, they are always written between <% and %>.

Handle with care

If you work with control structures, note that they can easily break, for instance if you forget a space or closing tag. This can also happen with placeholders, but a lot more easily with control structures:

It will happen but don't worry; we'll take a look at how to easily debug any template related errors. And the more experience you get, the less errors you'll make and you'll also find them much more quickly.

Whitespaces inside control structures matter: you must put a space after the <% and before the %>, but don't use tabs or newlines. Outside of the control structures, as between two tags that belong together, you can use any whitespace—just like with HTML.

Embedding and linking files

We've already covered the <% include BreadCrumbs %> control structure.

A very similar concept is the <% require themedCSS(layout) %>. It includes a CSS file, specifically from the theme's CSS directory. Again you should include the filename, without the file extension, as the only argument.

The advantages over a static <link rel="stylesheet" type="text/css" href="/themes/blackcandy/css/layout.css"> are that you don't have to specify the path. Additionally, SilverStripe ensures that the file is referenced only once, even if you include it multiple times with require.

Additionally, one other neat trick is used to save you from the problem of CSS files being cached by the browser. A URL parameter is automatically attached by SilverStripe to the file. It stays the same as long as the file is unchanged and changes as soon as the file is edited. URL parameters for plain CSS files don't make any sense, but the browser doesn't know that. It simply sees that a different file is referenced and reloads it, regardless of any cache settings. So the actual output of a CSS file might look like this:

<link rel="stylesheet" type="text/css" href="http://localhost/themes/
blackcandy/css/layout.css?m=1271032378" />

If you want to include a CSS file from outside the theme's CSS directory, you can use <% require css(sapphire/thirdparty/tabstrip/tabstrip.css) %>. This is useful if the file is already included in one of the core directories so you don't need to keep multiple copies. Be sure not to use a leading slash, as the file won't be found this way. Start with the name of the base directory.

For including a JavaScript file, instead of the plain HTML way, you can use <% require javascript(sapphire/thirdparty/jquery/jquery-packed.js) %>.

Comments and base tag

If you want to add a comment to the template, use <%-- My first comment... --%>. The advantage over a regular HTML comment is that it is not included in the final output.

Instead of setting the base tag via <base href="$BaseHref"></base> you can simply use <% base_tag %>. This will also take care of browser specific requirements, providing the following output:

<base href="http://localhost/"><!--[if lte IE 6]></base><![endif]-->

Conditional statements

The first control structures we saw in the base were conditional statements. The basic structure is:

Downloading the example code for this article
The author mantains an updated repository for this title's code bundle, downloadable from You can also visit for additional support.

<% if ConditionA %>
ConditionA has been met.
<% else_if ConditionB %>
ConditionA hasn't been fulfilled but ConditionB has.
<% else %>
Neither ConditionA nor ConditionB were true.
<% end_if %>

If the placeholder ConditionA exists and is neither empty nor false, the first text will be inserted into the final HTML page. If the first condition doesn't fulfill these requirements, but the second one does, the second text is used. If neither conditions can be met, the third text is presented.

Note that the else_if and else parts are optional. As a minimum you only need the opening if and the closing end_if. If the control structure consists of two or more tags it's always closed off with an end_ followed by the control's opening element.

So what can be a condition? For the moment let's just focus on placeholders. Take a look at the base, what does this accomplish?

<% if MetaTitle %>
<% else %>
<% end_if %>

It means that, if a meta-title has been set: use it; otherwise take the regular title. Note that MetaTitle in the if statement doesn't start with a $. You mustn't use a $ in a control structure, only outside of it..

Another practical example for conditional statements can be:

<% if CurrentMember %>
<h3>Welcome Back, $CurrentMember.FirstName</h3>
<% end_if %>

You can use the comparison operators == and != as well as the Boolean && and || in conditional statements. Other than that the template engine is rather limited. You can't use negations, comparison operators such as < or >, brackets to group conditions, or more than one Boolean operator.

That's both a boon and a bane: you are limited to certain statements (at least in the template), but your templates will always be short and clean. You cannot cram too much logic into them. We'll use Controller and Model for that.

To add a message for people not logged in, create an empty if block and put your message into the else. To avoid unnecessary whitespace put if and else in the same line:

<% if CurrentMember %><% else %>
Hello Anonymous! Please log in to continue...
<% end_if %>

Control statements

Using control statements, you can access multiple records from a data set. The basic structure is:

<% control element %>
Content of your control statement.
<% end_control %>

You can use a control statement on a single record, like $CurrentMember and use its elements like directly accessible placeholders:

<% control CurrentMember %>
Hello $FirstName $Surname!
<% end_control %>

Note the similarity to placeholders in general in this case:

Hello $CurrentMember.FirstName $CurrentMember.Surname!

Both will output your name, for example, Hello Philipp Krenn!

What else can we do with control statements? Quite a lot, especially when working with navigation elements. The following code fragments are all intended to be used within the opening control block.

SilverStripe 2.4: Customizing the Layout

Furthermore, there are some very handy elements for styling our records. Use these inside the control block:

  • Zebra-striping: $EvenOdd returns even or odd, depending on the row number. Alternatively, you can use <% if Odd %> and <% if Even %>.
  • You can achieve the same and a little more with $MultipleOf(Number, Offset), which returns a Boolean value. For example, to insert a dot after every fourth element and for all others a comma call: <% if MultipleOf(4) %>.<% else %>,<% end_if %>
  • In order to target every second entry within the groups of four from the statement above, use $Modulus(Number, Offset), which returns a number. By calling $Modulus(4) you'll get 1, 2, 3, 0, 1, 2,... , which can be easily used for styling within an HTML class attribute. Take special note of the initial value of 1 as most IT systems start with 0. However, this is more intuitive for most designers, who will generally focus on the templates.
  • Counting can be done with $Pos, starting at 1 again, while $TotalItems returns the number of rows in the set.
  • First, last, and middle elements: $FirstLast will be first, last, or empty. Alternatively, you can use <% if First %>, <% if Middle %> and <% if Last %>.
  • If you are inside a control statement, you can only access locally available elements. If you need an outside value, prefix it with $Top. For example, $Top.URLSegment will give you the page's URL segment. $URLSegment alone might return nothing, assuming you're inside a control block.

BlackCandy revisited

Let's get back to one of the more complex parts of the BlackCandy theme, which we've left out so far. By now you should easily understand what's going on, so open up the file in the folder themes/blackcandy/templates/Includes/ (the relevant part is below):

<ul id="Menu2">
<% control Menu(2) %>
<% if Children %>
<li class="$LinkingMode">
<a href="$Link" title="Go to the $Title.XML page"
class="$LinkingMode levela">
<% else %>
<li><a href="$Link" title="Go to the $Title.XML page"
class="$LinkingMode levela">
<% end_if %>
<% if LinkOrSection == section %>
<% if Children %>
<ul class="sub">
<ul class="roundWhite">
<% control Children %>
<li><a href="$Link"
title="Go to the $Title.XML page"
class="$LinkingMode levelb">
<% end_control %>
<% end_if %>
<% end_if %>
<% end_control %>

Creating two pages (SubpageA and SubpageB) on the second level and one on the third (SubSubpage, child of SubpageA), the output looks like this:

SilverStripe 2.4: Customizing the Layout

The page source will read as follows:

<ul id="Menu2">
<li class="current"><a class="current levela"
title="Go to the SubpageA page" href="/home/subpagea/">
<ul class="sub">
<ul class="roundWhite">
<li><a class="link levelb"
title="Go to the SubSubpage page"
<li><a class="link levela" title="Go to the SubpageB page"

Here's an explanation of the code:

  • First, we're getting the second-level menu items: <% control Menu(2) %>.
  • Then we're checking if the current item has sub-items, which is true for SubpageA but not SubpageB, adding some additional styles:

    <% if Children %>

  • If you currently are on a page with children or on one of the child pages themselves, expand them (this is the case in the image above):

    <% if LinkOrSection == section %>

  • Finally, loop over all child elements and output them:

    <% control Children %>


Having covered the basics of Customizing the Layout, in the next article we will be Creating our own theme.

Further resources on this subject:

You've been reading an excerpt of:

SilverStripe 2.4 Module Extension, Themes, and Widgets: Beginner's Guide

Explore Title