Port your Drupal 7 Theme to Drupal 8

Boats at a port

Let’s port an existing theme (and subtheme) to Drupal 8. There are quite a few posts out there describing the process, but those mostly cover the creation of a theme rather than its port to D8. To help you out, I’ve gathered most of the relevant information here that helped me achieve a D8 beta version of our beloved theme at Zivtech. You can find a full version of the theme here.

Install Drupal 8

Nothing special here. Download the latest beta and run the install process. You are then going to create a new theme instance and then add D7 components one at a time.

Our Zivtech theme used to be an actual sub theme of Zen. However, there is no D8 release available, and the dev version is broken and unusable at the moment. While I am sure this will change, it was the trigger for us to part from Zen since our D7 was only using some of Zen’s preprocess functions that were easily integrated or left behind.

Create the Folder Structure

First we need to take a quick look at the folder structure. In Drupal 8 our themes go into the “Themes” folder located in the root folder of your Drupal install (themes shipped with core are located in root > core > themes).
I recommend creating both a “contrib” and “custom folder,” and create a folder for your new custom D8 theme so you end up with  root > themes > custom > [yourtheme].

Create the Necessary Files

No .info file anymore. They have been replaced with Twig and Yaml (.yml) files. The syntax is easy and intuitive, but make sure to check out a few examples as there are conventions to respect. For example, set indentation to spaces, not tabs; respect precise indentation, etc). Here is the one I ended up writing:

The first settings are pretty self-explanatory if you’re used to the D7 .info file. The syntax just varies a little. The naming convention is as follows: yourtheme.info.yml

stylesheets-remove, as well as stylesheets-override, are new ways to control some of the stylesheets shipped with core that you may want to alter or remove altogether. I recommend taking a look here to get an overview of overrides and dependencies as they relate to themes and sub-themes when using this function. Additionally, make sure that you have the right paths. You’ll need an absolute path for core files, while you can use tokens for modules and themes css/js. See here for more.

The libraries are a new way to include our styles and scripts in a more modular fashion. In order to reference our inclusions, we need to create a yourtheme.libraries.yml

In our case:

One important thing to notice here is that we need to include jQuery manually as a dependency, which is fair considering a lot of people don’t necessarily need it (Drupal 8 leverages jQuery less than its predecessors). The dependencies function will load things globally BUT you have other options if you wish to include a library on certain parts of your site only (in a template, page, etc).


Example:

In a twig template:

{{ attach_library(yourtheme/global-scripts') }}

 

As a preprocess:

The regions function is similar to the old .info file. Pretty straightforward.

You can also set default theme settings as you could before. The settings.php file remains as is in D8 and the functions seem to work the same (I did not have to change anything in my case).

breakpoints: The breakpoints module is now part of core in D8. While we control breakpoints in our stylesheets, we need to make Drupal API aware of what they actually are, so that contrib modules such as picture or responsive image can leverage your theme breakpoint settings. Here is how you would configure it, in a yourtheme.breakpoints.yml.

 

Label: A human readable label for the breakpoint.

MediaQuery: Media query text proper ('all and (min-width: 851px)'.

Weight: Positional weight (order) for the breakpoint.

Multipliers: Supported pixel resolution multipliers.
 

You can also create breakpoint groups as shown here.

You don’t need to declare this file in your info.yml files; however you’ll need to clear your caches after adding it.

Templating & Twig

While we all need to learn the Twig syntax, there is one very useful tool to convert your D7 modules and themes to D8. Follow the drush directions from the module’s read-me and see the magic happen. This will not only convert the markup, but also your variables, PHP conditional statements, and so on. While this is not a final version of what I wanted, it showed me quite a bit about twig syntax and I encourage comparing both old and new files to get a good idea about the new twig templating.

Here are a few conversion examples:

 

Function

PHP template

Twig

Naming
Conventions

html.tpl.php
html.html.twig
 
page.tpl.php
page.html.twig
 
node--[content-type].tpl.php
node--[content-type].html.twig

Variable (set)

<?php $tags = $content->tags; ?>
{% set tags = content.tags %}

Variable (print)

<?php print $tags; ?>
{{ tags }}

Conditional Statements

<?php if ($content->tags): ?>
<?php endif; ?>
{% if content.tags %}
{% endif %}
 
<?php if (!empty($content->tags)): ?><endif; ?>
{% if content.tags is not empty %} {% endif %}
 
<?php if (isset($content->tags)): ?>
<?php endif; ?>
{% if content.tags is defined %}
{% endif %}
 
<?php if ($count > 0): ?>

<?php endif; ?>
{% if count > 0 %}

{% endif %}

Filters

<?php print check_plain($something); ?>
{{ something|striptags }}
 
<?php print t('Home'); ?>
{{ 'Home'|t }}

 

 

More info and examples can be found here.

 

Here is what our page.html.yml looks like:

Preprocess Functions

Now this is where it gets iffy. twigify will not convert these. It will comment out some of them and mark them as deprecated, but for the rest we’re on our own and have to convert all these manually by finding the right D8 API functions.

Luckily, many of the preprocess functions work similarly to D7. For example, if you need to include an image in one of your page template file, you can still use the page preprocessor function:

Then, over in your page.html.twig file you can access that image URL via “{{ my_image }}”. For these more simple preprocessing needs, not much has changed.

On the other hand, for more complex preprocessing there are some changes to keep in mind! For example, in D7 if you were preprocessing a node, and you needed to know its type or its created date, you’d do that like this:

In D7, you can drill down into the $variables array in a similar fashion for any of your preprocessing functions.

Yet D8 has a major shift to OOP, of course. As a result, we have the introduction of the Typed Data API, which drastically alters how you’d perform the same preprocessing:

In D8, the other major change is that only the base hook of a preprocessing function exists. For example, in D7 you can have a “node--event-teaser.tpl.php” and subsequently a “hook_preprocess_node__event__teaser” preprocessing function available for you in your template.php file. In D8, you can still have the template file for your event teaser, but you only get the base hook, “hook_preprocess_node”. Instead, you’d need to do logic within the base hook to operate on your event teaser specifically.

Sub Theme

As far as sub theming, not much has changed here. All you need is to include the base theme line in your subtheme yoursubtheme.info.yml

Example:

 

Resources

You can find a full version of the theme here.

Check out these helpful articles from Drupal.org:

Alban Bailly
Alban Bailly

Principal Front-End Developer

Before joining Zivtech in May 2013, Alban studied jazz and composition in his native France and relocated to Philadelphia in 2005. Soon after, he added web design and development to his portfolio and began specializing in Open Source Software and Drupal, working with private clients and B2B advertising agencies.

Ready to get started?

Tell us about your project