…there was a man who had to build a site for a client. This site was really a simple one: a contact form, a static page, articles, comments – and that’s all. And if you listen carefully you may realize that it sounds just like Drupal core! And because this story took place in December, last year, it could be not only Drupal 7, but Drupal 8! (We already had a nice alpha version at that time…)
To share some more details with you, the client was my wife and the developer was me. And I decided to use Drupal 8 because it was possible and I really wanted to learn about it. And I think the best way to learn new things is to face real challenges.
While the core functions of Drupal 8 were enough for us, we didn’t want to use core Bartik theme. We didn’t want to look like thousands of others sites on the net. We wanted something what is a little bit different. So I had to create something custom… And I did it.
Today I want to share some of my experiences with you from this process. I want to show you some examples that changed in theming when we use Drupal 8.
I decided to create a sub-theme. So I looked fore a base theme. And I found one! I was so happy that I tweeted about it! To tell you the truth, it was the only one full Drupal 8 contrib theme at that time. It is called Gratis, made by Danny Englander. And I really like it!
So the first step of creating a new theme is to provide all the information the system has to know about it. And we do it by creating a mysubtheme.info file. Well… we do it in this way in Drupal 7. However thighs have changed in Drupal 8!
While Drupal 7 stores the configuration information in the database, Drupal 8 uses YAML configuration files for the same purpose. So Drupal 8 themes switched from .info files to .info.yml files. How does YAML look like?
Its syntax is similar to the .info files, but there are some differences. Instead of equals sign we have colon between key-value pairs. We use hashmark to mark comments and indentation to structure the file content (like the original syntax of Sass does it).
name: My sub-theme
description: Sub-theme of Gratis
version: VERSION
core: 8.x
type: theme
# engine: twig
base theme: gratis
screenshot: images/screenshot.png
The required content of the file didn’t change radically. But there is a new required element. I had to tell to Drupal what kind of extension I provided to it. So I wrote type: theme
. But we should write type: module
if we provided a module or type: profile
if we provided an installation profile.
Of course I had to define the human readable name and the Drupal core version of my sub-theme and because it’s a sub-theme I had to define the base theme (using the machine name of it).
It is good to know that while lots of the declarations in YAML use underscore in keys we still have to write base theme
with a space between the words and we mustn’t use underscore.
Of course one can define additional information but they are not required and we don’t go into the details now.
Update: note that this section below is outdated, as Drupal switched to using libraries to manage CSS and JavaScript.
So I notified Drupal about my sub-theme, but it didn’t do too much in this state. Just duplicated the look and feel of the base theme. So I had to declare the .css files what I used later to change the look of the site. This is the way we have to do it in Drupal YAML files. And you can see a new example of the YAML syntax: we use a hyphen and a space to define a list element.
# Stylesheets
stylesheets:
screen:
- css/base.css
- css/components.css
print:
- css/print.css
As you know our theme inherits the stylesheets from the parent theme. They can be overridden or ignored but this facility in Drupal 7 is a little bit hacky and partially broken. That is why Drupal 8 introduces dedicated, separate properties in theme .info.yml file for adding, overriding, and removing stylesheets.
# Stylesheets override
stylesheets-override:
- system.theme.css
# Remove not used stylesheets
stylesheets-remove:
- node.module.css
As you can see how much I talk about it you may realized that I like to deal with CSS-related things. So here is one more information about the topic (but the last, I promise).
In Drupal 8 the CSS file structure is built following the SMACSS method, and selector declaration in the CSS files follows the BEM method. I won’t go in details but you can – and you should! – read more about it at Drupal.org in the CSS Coding Standards documentation. (Which means that we have CSS Coding Standards – did you know that?)
OK. That’s all about CSS. But I haven’t talked about that thing yet what we style with CSS. This is the HTML output of Drupal. Drupal 7 uses the PHPTemplate engine and .tpl.php template files to create this output.
But – because of more reasons – PHPTemlate is not the best option today. We had to look for a better solution. And…, Ladies and Gentleman…, we have it! We have Twig in core! And we are happy with that! Are you really happy?
But why should we be happy? Let me tell you some reasons.
Twig is a modern templating language and a template engine too. And it is secure. Using PHPTemplate you can delete records from the database or files from the file system by accident or of malice prepense. Using Twig you can’t do that. You haven’t got access to the database and to the file system.
Twig is fast. When your web page renders, the Twig engine takes the template and converts it into a ‘compiled’ PHP template which it stores in a protected directory. The compilation is done once. Then template files are cached.
Twig has a concise, tag-based syntax which is easy to learn. It is easy to understand and learn for not hardcode developers (site builders, CSS guys) too.
{# Content links #}
{% if content.links%}
<footer class="{{attributes.class}} link-wrapper"
{{ attributes|without('class') }}>
<span>{{ 'Click?'|t }}<span> {{ content.links }}
</footer>
{% endif %}
You can see a sample twig markup here. It is just an example but shows you some typical twig tags. They are all placed between curly brackets. And we have everything we need. We can print variables and elements of arrays or objects. We can add comments, define expressions like if
conditions or for
loops. And we also have the translate function which is a filter type in Twig.
The last example shows how we can work with HTML attributes. One can choose those which should be printed individually – like the class names in the example – and all the others* are printed afterwards by using the attributes
variable. This is the standard method of the .html.twig templates of Drupal 8 core.
*Update:
“Attribute objects can now be printed multiple times, and printing out individual attributes (for example the class attribute) from the Attribute object no longer marks the individual attribute as printed. The Twig “without” filter can be used to prevent attributes from being printed. This change makes attributes within Twig templates behave the same as render arrays.”
See the full change record.
It is important that you should only use a template, if you really need it. This means if you cannot modify the output using only CSS. This would be the case if you want to hide e.g. the secondary menu.
Tip: enable twig_debug in settings.php.
.theme
fileI have some advanced theming examples for you and the first one is about template.php. In Drupal 8 it is renamed to a .theme file what should has the same name as the theme. Developers use this file to create custom functions or define custom variables. So it is still pure PHP code inside. Let me show you a quick example.
// In mysubtheme.theme file:
// Note: this code is outdated!
// Render the main scripts file.
$local_js = array(
'#attached' => array(
'js' => array(
$path . '/js/scripts.js',
),
),
);
drupal_render($local_js);
#attached
In Drupal 7, drupal_add_css()
, drupal_add_js()
and drupal_add_library()
functions could be used to add JS and CSS to a page. In Drupal 8 you must use the #attached
method in a render array instead. #attached
was already available in Drupal 7, and was widely used, but now it’ll be the only way* to attach assets.
*Update: there are several ways to attach CSS and JS.
.libraries.yml
My last example is the hook_library_info()
function which can be used to load third party libraries. You may not used this too much, because Drupal 7 automatically loads the core libraries.
In Drupal 8 the function is replaced by the .libraries.yml configuration file. And the reason why you do have to know about it is that Drupal 8 does not load a lot of javascript by default for anonymous users. So you’ll need to use a mysubtheme.libraries.yml file to create some dependencies for loading things like jQuery.once or even drupal.js for that matter.
A short session is not enough to teach you all the new information. So if you want to know more you should do your homework yourself. Here are some resources which can help you.
I suggest you to read the change records which are very useful and contain a lot of information. We also have a growing Drupal 8 theming documentation at drupal.org. It is a good read too.
I started my session with the story of the site which I built to my wife. Just with some CSS modifications and some small configuration changes I created a new theme based on Gratis.
Here it is: https://noi-kerekasztal.hu. I’m sure that I won’t win a design award with it – what is normal since I am not a designer – but there is no other site on the net which looks exactly the same. And that is enough for us.
(I didn’t include all the slides to this post, but you can find them at my speakerdeck.)