Drupal 8 Theming
Focusing on differences from Drupal 7
Your Presenter
Nick Wilde
From Victoria, BC.
drupal.org/u/nickwilde
github.com/NickWilde1990
twitter.org/NickWilde1990
Developer w NorthStudio: northstudio.com & twitter.org/North_Studio
Lead Developer (read: freelancer) BriarMoon Design: design.briarmoon.ca
Recent Themes
- Lethbridge College -
lethbridgecollege.ca - Lethbridge College Wider Horizons
lethbridgecollege.ca/wider-horizons - Wildewood Custom Homes
wildewood.ca - BriarMoon Farm
farm.briarmoon.ca - BriarMoon Design (ouch)
design.briarmoon.ca

Goals
Depending on prior experience, by the end of this session, you should be capable or possibly comfortable with:
- Creating a new Drupal 8 theme
- Converting a Drupal 7 (or 6) theme to Drupal 8
- Modifying an existing Drupal 8 theme - both within the theme or through using theme inheritance
- Theming specific components in Drupal 8
- Adding assets (CSS/JS) files/libraries.
Drupal 8 Theming
This is going to be divided into a few rough sections
- Theming Topics
Going into more detail on some specific building blocks
- Walk-through: Creating a new Drupal 8 theme
- Walk-through: Converting a Drupal 7 theme to Drupal 8
Topics
Topics we're going to delve into today include:
- Introduction/What is Drupal theming
- What you can and can't do with themes vs modules
- Differences from Drupal 7
- Drupal 8 specific files
- Theme inheritance
- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Topics
Topics we're going to delve into today include:
- Introduction/What is Drupal theming
- What you can and can't do with themes vs modules
- Differences from Drupal 7
- Drupal 8 specific files
- Theme inheritance
- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Drupal Theming
- Should be accessible
- Should be navigable
- Should be user friendly
- Should look good
- Should only concern itself with display and front end interaction
- Should not do significant interaction stuff
- Can change any design/styling
- Can be as fancy as you want
Building Blocks
Languages:
- HTML & TWIG
- CSS
- JS
- PHP
- YAML
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal theming- What you can and can't do with themes vs modules
- Differences from Drupal 7
- Drupal 8 specific files
- Theme inheritance
- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Modules vs Themes
- Preprocess: both
- Render plugins: modules
- Templates: both
- Asset libraries: both
- Hooks: varies.
Modules vs Themes
- Preprocess: both
- Render plugins: modules
- Templates: both
- Asset libraries: both
- Hooks: varies.
Drupal 8 Specifics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modules- Differences from Drupal 7
- Drupal 8 specific files
- Theme inheritance
- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Differences from D7
- THEMENAME.info replaced with THEMENAME.info.yml
- Like Drupal 8 modules, also uses other *.yml files. Specifically the allowed/used files are:
- THEMENAME.libraries.yml
Covered in detail a bit later, this is how you create asset (JS/CSS) libraries for use in your theme. Used by pretty much all themes - THEMENAME.breakpoints.yml
Defines breakpoints that are used by some components of Drupal (for example, responsive image module). Rare/Occasional.
- THEMENAME.libraries.yml
*.yml & YAML syntax
YAML Ain’t Markup Language (YAML™)
Full spec at yaml.org
see also: http://symfony.com/doc/current/components/yaml/yaml_format.html
Very similar to JSON - any valid YAML is valid JSON (although not necessarily vice versa)
Most important thing really is the indentation
location: PDX
When: 'Right now'
key with space: 'Makes me twitch'
key-with-dashes: 'Ah that's better :)'
sequence:
- Drupal
- theming
# Comment: next a map with multiple levels
human:
name: 'Arnold Bros'
descendants:
grand-son: 'Richard, 39'
death: 'unknown'
notes: 'Deified by the gnomes of the store especially before...'
# Sequences and maps can be combined
mix_tape:
- name: 'Salt Water'
artist: 'Leah Daniels'
genre: 'country'
notes: >
The water running down your face
Yes, this
- name: 'Dance, Dance, Dance'
artist: 'Steve Miller'
genre:
- '70s'
- 'rock'
THEMENAME.info.yml
Drupal 8
name: Neato!
type: theme
base theme: classy
description: Base Drupal theme based on the Neat grid system from Thoughtbot.
core: 8.x
regions:
header: Header
featured: Featured
breadcrumb: Breadcrumb
sidebar_first: 'Sidebar first'
content: Content
sidebar_second: 'Sidebar second'
footer_top: 'Footer Top'
footer_bottom: 'Footer Bottom'
Drupal 7
name = Neato!
description = Base Drupal theme based on the Neat grid system from Thoughtbot.
core = 7.x
engine = phptemplate
; Image appears on admin/appearance
screenshot = images/screenshot.jpg
; Regions are output in the page.tpl.php
regions[header] = Header
regions[featured] = Featured
regions[sidebar_first] = Left Sidebar
regions[content] = Content
regions[sidebar_second] = Right Sidebar
regions[footer_top] = Footer Top
regions[footer_bottom] = Footer Bottom
THEMENAME.libraries.yml
bootstrap.libraries.yml
attributes:
js:
js/attributes.js: {}
dependencies:
- core/jquery
- core/underscore
drupal.bootstrap:
js:
js/drupal.bootstrap.js: {}
dependencies:
- core/jquery
- core/drupal
- core/drupalSettings
# Create a library placeholder for livereload.
# This is altered dynamically based on the set URL.
# @see \Drupal\bootstrap\Plugin\Alter\LibraryInfo::alter
livereload:
js:
livereload.js: {}
theme:
js:
js/theme.js: {}
dependencies:
- bootstrap/drupal.bootstrap
- bootstrap/attributes
theme-settings:
js:
js/theme-settings.js: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
- core/drupalSettings
This is where you define an asset library.
- New to D8
- Covered more later
Differences from D7
- Templates now use TWIG instead of PHP Template
Other template engines are available in contrib including a rough/alpha/minimally documented PHPTemplate
- HTML5 by default vs XHTML for Drupal 7
- Core CSS/classes use a SMACSS & BEM ish approach
- CSS/JS is only present on a page if explicitly added
Better performance - no more jQuery everywhere even pages that don't use it.
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modulesDifferences from Drupal 7Drupal 8 specific files- Theme inheritance
- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Theme Inheritance
Although almost fully inheritable, some facets of themes can't be inherited:
- Region definitions (in THEMENAME.info.yml)
- Features
- Logo
- Color module support
- Theme settings
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modulesDifferences from Drupal 7Drupal 8 specific filesTheme inheritance- Templating: Twig vs PHP templates
- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Templating
- Provides the HTML structure of elements
- Fully inherited between modules/themes
- Twig is similar to PHPTemplate but:
- Safer
- slightly less verbose for the same effect
- Twig templates also support some internal inheritance
- Two automated methods exist to convert PHPTemplate to Twig:
- https://github.com/makinacorpus/php-twig-converter
- https://www.drupal.org/project/twigify
Twig vs PHPTemplate
{# A comment #}
{# Printing (rendering) a variable #}
{{ content }}
{# Set a variable #}
{% set title_length = label|length %}
{# If statements #}
{% if title_length is not defined %}
<h1>NO ?</h1>
{% elseif title_length > 20 %}
<h1 class='long'>{{ label }}</h1>
{% else %}
<h1> {{ label }}
{% endif %}
{# Walk through an array #}
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
<?php /* A comment */ ?>
<?php /* Printing (rendering) a variable */ ?>
<?php print $content; ?>
<?php /* Set a variable */ ?>
<?php $title_length = strlen($label); ?>
<?php /* If statements */ ?>
<?php if (!$label): ?>
<h1> No Label?</h1>
<?php elseif ($title_length > 20): ?>
<h1 class='long'><?php print $label; ?></h1>
<?php else: ?>
<h1><?php print $label; ?></h1>
<?php endif ?>
<?php /* Walk through an array */ ?>
<?php foreach($items as $items) { ?>
<li><?php print $item;?></li>
<?php} ?>
Twig Filters
Replace the usage of PHP functions directly in templates
- Twig provides many
- Drupal 8 provides more
- Modules can provide more
- Twig Tweak (drupal.org/project/twig_tweak) provides some nice ones
- Filters can be chained (multiple used on one variable).
Common Twig Filters
- trans/t
Uses the core translate functionality on the string
{{ "string here"|t }} - clean_class
sanitize a string to be safe to use as a html class - clean_id
sanitize a string to be safe to use as a html id - without(key1, key2, keyn)
Render a map (array) variable without some keys - render
render a variable for further processing
Block Template
Drupal 8 - Omega theme 8.x-5.x - block.twig.html
<div{{ attributes }}>
{{ title_prefix }}
{% if label %}
<h2{{ title_attributes }}>{{ label }}</h2>
{% endif %}
{{ title_suffix }}
<div{{ content_attributes }}>
{% block content %}
{{ content }}
{% endblock %}
</div>
</div>
Drupal 7 - Omega theme 7.x-4.x - block.tpl.php
<div<?php print $attributes; ?>>
<?php print render($title_prefix); ?>
<?php if ($block->subject): ?>
<h2<?php print $title_attributes; ?>><?php print $block->subject; ?></h2>
<?php endif; ?>
<?php print render($title_suffix); ?>
<div<?php print $content_attributes; ?>>
<?php print $content; ?>
</div>
</div>
Twig Inheritance
{% extends "paragraph.html.twig" %}
{% block content %}
<div class="blur">
{{ blur_image}}
</div>
<div class="slide">
{{ content }}
</div>
{% endblock %}
{%
set classes = [
'paragraph',
'paragraph--type--' ~ paragraph.bundle|clean_class,
view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
]
%}
{% block paragraph %}
<div{{ attributes.addClass(classes) }}>
{% block content %}
{{ content }}
{% endblock %}
</div>
{% endblock paragraph %}
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modulesDifferences from Drupal 7Drupal 8 specific filesTheme inheritanceTemplating: Twig vs PHP templates- Basic asset attachment/configuration
- Preprocess functions
- Best Practices
Asset Attachment
- Very different than Drupal 7
- Should always use the library system
- Multiple methods of attachment
- In THEMENAME.info.yml (all pages)
- In preprocess functions (per element)
- In templates (per element)
- In render functions (in modules, per element)
Asset Attachment
name: Adminimal
type: theme
description: 'Drupal administration theme with modern minimalist design.'
core: 8.x
base theme: seven
libraries:
- adminimal_theme/global-styling
regions:
header: 'Header'
pre_content: 'Pre-content'
breadcrumb: Breadcrumb
highlighted: Highlighted
help: Help
content: Content
page_top: 'Page top'
page_bottom: 'Page bottom'
sidebar_first: 'First sidebar'
regions_hidden:
- sidebar_first
global-styling:
version: VERSION
css:
base:
css/adminimal.css: {}
Global via THEMENAME.info.yml
Asset Attachment
/**
* Implements template_preprocess_node.
* @param array $variables
*/
function wellgousa_preprocess_node(array &$variables) {
/** @var \Drupal\node\Entity\Node $node */
$node = $variables['node'];
$bundle = $node->bundle();
if ($node->hasField('field_dynamic_bar')) {
$variables['#attached']['library'][] = 'wellgousa/dynamic-bar';
}
if ($bundle === 'page') {
$variables['#attached']['library'][] = 'wellgousa/node-page';
}
elseif ($bundle === 'film') {
$variables['#attached']['library'][] = 'wellgousa/node-film';
}
}
Via Preprocess functions
Asset Attachment
{% set bg_color = entity.field_bg.0['#taxonomy_term'].field_css_.value ~ ' color' %}
{% set slide_classes = [
'entity--hero',
bg_color
]
%}
{{ attach_library('lc_oe/slick') }}
{{ attach_library('lc_oe/bleed') }}
<div{{ attributes.addClass(slide_classes) }}>
{{entity.field_background_image}}
<div class="slide-inner-container">
<h3>{{entity.title}}</h3>
<h4>{{entity.field_slide_copy}}</h4>
</div>
</div>
Via twig
Asset Libraries
global-styling:
version: 1.0
css:
base:
https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css: {type: external}
css/lc_oe.css: {}
css/lc_oe-ie-compatibility.css: {browser: {'!IE': false}}
global-scripts:
version: 1.0
js:
js/scripts.js: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
hero-video:
js:
js/hero-video.js: {}
https://www.youtube.com/player_api: {type: external}
parallax:
js:
bower_components/parallax.js/parallax.js: {}
dependencies:
- core/jquery
slick:
version: 1.0
css:
base:
../../../libraries/slick/slick/slick.css: {}
theme:
../../../libraries/slick/slick/slick-theme.css: {}
js:
../../../libraries/slick/slick/slick.min.js: {}
js/slick-init.js: {}
dependencies:
- core/jquery
view-awards:
js:
js/views-view--awards.js: {}
dependencies:
- lc_oe/responsive-tabs
bleed:
js:
js/bleed.js: {}
responsive-tabs:
js:
js/ie-compatibility.js: {browser: {'!IE': false}}
../../../libraries/responsiveTabs/responsive-tabs.js: {}
../../../libraries/responsiveTabs/jquery.responsiveTabs.js: {}
css:
component:
../../../libraries/responsiveTabs/responsive-tabs.css: {}
Browser Conditionals
@param array $element
* A render array with a '#browsers' property. The '#browsers' property can
* contain any or all of the following keys:
* - 'IE': If FALSE, the element is not rendered by Internet Explorer. If
* TRUE, the element is rendered by Internet Explorer. Can also be a string
* containing an expression for Internet Explorer to evaluate as part of a
* conditional comment. For example, this can be set to 'lt IE 7' for the
* element to be rendered in Internet Explorer 6, but not in Internet
* Explorer 7 or higher. Defaults to TRUE.
* - '!IE': If FALSE, the element is not rendered by browsers other than
* Internet Explorer. If TRUE, the element is rendered by those browsers.
* Defaults to TRUE.
* Examples:
* - To render an element in all browsers, '#browsers' can be left out or set
* to array('IE' => TRUE, '!IE' => TRUE).
* - To render an element in Internet Explorer only, '#browsers' can be set
* to array('!IE' => FALSE).
* - To render an element in Internet Explorer 6 only, '#browsers' can be set
* to array('IE' => 'lt IE 7', '!IE' => FALSE).
* - To render an element in Internet Explorer 8 and higher and in all other
* browsers, '#browsers' can be set to array('IE' => 'gte IE 8').
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modulesDifferences from Drupal 7Drupal 8 specific filesTheme inheritanceTemplating: Twig vs PHP templatesBasic asset attachment/configuration- Preprocess functions
- Best Practices
Preprocess Functions
Finally! Something stays the same.
Well in theory they stay the same, but...
function pacific_analytics_css_alter(&$css) {
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
// Remove defaults.css file.
$theme_name = $theme_key;
// @FIXME
// // @FIXME
// // The correct configuration object could not be determined. You'll need to
// // rewrite this call manually.
// $path = variable_get('theme_' . $theme_name . '_files_directory');
$file = $theme_name . '.default.layout.css';
$filepath = $path . '/' . $file;
unset($css[$filepath]);
}
Topics
Topics we're going to delve into today include:
Introduction/What is Drupal themingWhat you can and can't do with themes vs modulesDifferences from Drupal 7Drupal 8 specific filesTheme inheritanceTemplating: Twig vs PHP templatesBasic asset attachment/configurationPreprocess functions- Best Practices
Best Practices
It's actually easier.
Twig is more secure than PHPTemplate without work
Creating your theme
Drupal Console
drupal gt
OR
../vendor/bin/drupal gt
Generated or updated files
Generation path: /mnt/c/sites/theme_loc/web
1 - /themes/custom/dc_gen/dc_gen.info.yml
2 - /themes/custom/dc_gen/dc_gen.theme
Generated lines of code: 159
Drupal 8 Theming - Differences from Drupal 7
By Nick Wilde
Drupal 8 Theming - Differences from Drupal 7
- 350