Building a custom theme in Drupal 8

Based on the Classy base theme

Presentation by Anne Tomasevich


This is the part where I talk about myself

Durham skyline by Jessie Gladin Kramer

Photo by Jessie Gladdek


  • What's changing?
  • Let's make a theme
  • Twig basics and debugging

Let's start from the very beginning

What is theming?

Themes allow you to change the look and feel of your Drupal site.

- Someone who's good at being very general

Why would you use Drupal 8 now?!

And when is it coming out?

What's changing?

...and how do I, a person who probably doesn't like change, cope with these changes...

What's new in Drupal 8?

  • Twig, a template engine by SensioLabs
  • Classy, a new base theme
  • template.php becomes [theme-name].theme. (Dear Drupal gods, can we please have theme.php?)

What's new in Drupal 8?

  • Responsive design elements are included by default
  • Breakpoints can be set and used across modules and themes

What's new in Drupal 8?

Look at all the pretty things
we get to use now!!

  • HTML5
  • CSS3
  • Modern jQuery libraries
  • BEM
  • Standardized breakpoints
Ron Paul gif

What's new in Drupal 8?

  • Support for IE8 and below is dropped meaning the use of jQuery 2.0, HTML5, and CSS3 (including pseudo selectors) are now supported
  • CSS:
    • Far fewer IDs are used
    • CSS file structure now uses SMACSS
    • Default class names follow the BEM format
  • CSS and JS files are attached to pages differently

What's new in Drupal 8?

For a more comprehensive list, visit Drupal's change log.

Why all the changes?

Accessibility to non-Drupalers

  • Fewer Drupal-specific conventions and more popular, well-documented frameworks (such as Twig), meaning non-Drupalers can jump in much more quickly.
  • D8 themers don’t need to know PHP to whip up a theme.*

* I mean kinda ¯\_(ツ)_/¯

Why all the changes?


  • Text is automatically escaped in Twig, meaning a lower chance of XSS attacks.
  • Template files are more secure since they no longer contain PHP code.
<?php db_query('DROP TABLE {users}'); ?>

Scary example courtesy of sqndr at

Why all the changes?

Current frameworks and tools

  • Separation of logic from appearance = more modular (reusable) code
  • More semantic CSS class names = leaner CSS and a more readable DOM
  • A general trend towards more extendable, modular, well-organized, better-performing code

Any disadvantages?

  1. Many contributed themes and modules don't having their 8.x branches ready.

A fun note on the 8.x branch of a popular contrib theme:

This is a development branch. Please note that this branch is probably severely broken. It is strongly recommended to wait before using this branch or even creating issues regarding it.

Any disadvantages?

  1. Serious lack of documentation online

Of the documentation that DOES exist, much of it is:

  • Outdated, and marked as such.
  • Outdated, and NOT marked as such.

Let's build this thing

What am I totally glossing over?

  • template.php *.theme
  • Grid framework and CSS architecture
  • Probably a lot of other things

Create a theme folder

Screenshot of theme folder location
  • Drupal core now resides in its own folder. Contributed and custom modules and themes are in the modules and themes folders, respectively.
  • Don't forget to create a custom folder for your custom theme!

Create a .info.yml file

name: Mappy
type: theme
description: 'D8 Theme for a basic leaflet site.'
core: 8.x
base theme: classy
 - mappy/global-styling
 - mappy/leaflet
  navbar: 'Top Navigation Bar'
  content: Content # required!
  sidebar: 'Sidebar'
  footer: 'Footer'

[theme-name].info becomes [theme-name].info.yml

Dot info dot what?

  • YAML is a data serialization language.
  • Key-value pairs.
  • Check out Symfony's excellent writeup on YAML syntax before getting started.
  • And mind your whitespace!

Create a .info.yml file

name: Mappy
type: theme
description: 'D8 Theme for a basic leaflet site.'
core: 8.x
  navbar: 'Top Navigation Bar'
  content: Content # required!
  sidebar: 'Sidebar'
  footer: 'Footer'

This looks familiar, right?

Classy, a new base theme

base theme: classy
  • In D8, default classes are stripped from Drupal core and moved into the base theme Classy.
  • To avoid these default classes, simply don't base your theme on Classy.
  • But we want them because BEM is awesome! Check out this awesome introduction.


 - mappy/global-styling
 - mappy/leaflet
  • In D8, assets can be added in a few different ways: globally, per-template, per-page, and more.
  • All libraries must be defined in your *.libraries.yml file.

Create a .libraries.yml file

# mappy.libraries.yml
      css/styles.css: {}

      css/leaflet.css: {}
    js/leaflet.js: {}
    js/map.js: {}
    - core/jquery

Note that jQuery is listed as a dependency of the Leaflet library. Since jQuery is no longer loaded automatically on every page, it must be explicitly required.

Adding assets to your site

 - mappy/global-styling
 - mappy/leaflet
  • Since this is a small site and we need our libraries on every page, we're adding them globally.
  • Visit for more information on methods for adding assets.

Create a .breakpoints.yml file

# mappy.breakpoints.yml
  label: mobile
  mediaQuery: '(min-width: 0px)'
  weight: 2
    - 1x
  label: narrow
  mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
  weight: 1
    - 1x
  label: wide
  mediaQuery: 'all and (min-width: 851px)'
  weight: 0
    - 1x

Ours is stolen adapted from the Bartik theme.


  • Once you add a .breakpoints.yml file (and uninstall and reinstall your theme), the breakpoints you've set will be exposed in the admin UI.
  • These breakpoints can be used across various modules.

With these files set up, we now have a working custom theme!

Baby, you got a stew goin'!

Template files

  • In our theme's current state, we're using Classy's default template files.
  • If we want to override them, it's time to learn some Twig.
  • Young Will Smith

    If one more person makes a "Gettin' Twiggy with it" joke...

Intro to Twig

  • Twig is a template engine with syntax similar to Django, Jinja, and Liquid.
  • It simplifies template creation with clean syntax and useful built-in filters, functions, and tags.
  • In a Drupal template file (now with the extention .html.twig), anything between curly braces is Twig.

Twig delimiters

{{ These }} are for printing content, either explicitly or via functions

{% These %} are for executing statements

{# These #} are for comments

Printing variables and regions

In D7 we render content like so:

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

Printing variables in D8 is as easy as including them inside double curly braces:

{# In page--front.html.twig #}
{# Print the sidebar region. #}

  {{ page.sidebar }}

Printing variables with special characters

If a variable name contains special characters, use Twig's subscript syntax (which will look familiar to seasoned Drupalers).

{# In page--front.html.twig #}
{# Print the page type. #}

  {{ page['#type'] }}

This will come in handy more during debugging.


Twig comes with many built-in filters that variables are passed to via the pipe character.

Here's the date filter:

	{# Format the post date. #}

  {{ post.published|date("Y-m-d") }}

Drupal-specific filters

Check them out here.

Remember our old friend t()?

	{# Run an ARIA label through t() #}

  <nav class="tabs" role="navigation" aria-label="{{ 'Tabs'|t }}">


Twig also comes with various functions that can be used in double curly braces.

Here's the cycle function doing some very important work:

{% set fruits = ['apple', 'orange', 'banana'] %}

{% for i in 0..10 %}
    {{ cycle(fruits, i) }}
{% endfor %}


Used for control flow and other fun things.

{# From Bartik's page.html.twig #}
{# If there are tabs, output them. #}

  {% if tabs %}
  {% endif %}

In template files, we’ll use the if statement quite often.

Get psyched

Make sure you check out the Twig coding standards!

Dries doge meme

Twig debugging

To enable debug mode and turn off caching, we need to do 3 things:

  1. Turn on Twig’s debug mode
  2. Turn on Twig auto reload, meaning that Twig templates are automatically recompiled when the source code is changed
  3. Disable Drupal’s render cache

You do NOT need turn off Twig caching - turning on auto reload is enough.

Use settings.local.php

  1. Copy sites/example.settings.local.php to sites/default and rename to settings.local.php. Fill out your local database settings.
  2. Uncomment the following at the bottom of sites/default/settings.php:
    if (file_exists(__DIR__ . '/settings.local.php')) {
      include __DIR__ . '/settings.local.php';

Turn on debug mode and auto reload

Check out settings.local.php

// In settings.local.php

 * Enable local development services.
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/';

This tells us to head over to

// In sites/
    debug: true
    auto-reload: true

Turn off Drupal's render cache

Just kidding, you already did.

// In settings.local.php

 * Disable the render cache (this includes the page cache).
 * This setting disables the render cache by using the Null cache back-end
 * defined by the file above.
 * Do not use this setting until after the site is installed.
$settings['cache']['bins']['render'] = 'cache.backend.null';

Debug away

Now you can:

  • View HTML comments in your browser’s code inspector with lots of helpful info:
    • Which theme hook is being implemented
    • Theme hook suggestions (i.e. how to override the current theme hook)
    • Which template file is being output.
  • Make changes to your source code and simply refresh the page to see your changes rather than constantly rebuilding the cache.

dump( )

With Twig debugging turned on, we can use the Twig function dump().

{# In a template file #}
{# Print out all variables on the page. #}

  {{ dump() }}

{# Print the page's base path. #}

  {{ dump(base_path) }}

Even better: kint( )

Install the Devel and Devel Kint modules to use kint() in the same way, but with a pretty, expandable array instead.

{# In a template file #}
{# Print out all variables on the page. #}

  {{ kint() }}

Resources and further reading