Drupal 8 Theming
Week 5
Advanced Twig
JavaScript

Summary
- Recap: Twig
- JavaScript
Recap
Drupal Theming
Twig
Drupal theming
What is a theme?
Why a theme?
Types of themes
Structure

themes van drupal.org ->
custom themes ->
d8imd.info.yml
name: D8IMD
type: theme
description: 'A base theme based on Classy. Learn how to use Classy as a base theme in the <a href="https://www.drupal.org/theme-guide/8">Drupal 8 Theming Guide</a>.'
core: 8.x
base theme: classy
libraries:
- core/normalize
- d8imd/global-styling
regions:
header: Header
primary_menu: 'Primary menu'
secondary_menu: 'Secondary menu'
breadcrumb: Breadcrumb
highlighted: Highlgihted
imd: 'IMD region'
help: Help
content: Content
sidebar_first: 'Sidebar first'
sidebar_second: 'Sidebar second'
footer: Footer
Read the docs!
Twig
Twig debugging
services.yml

twig.config:
# Twig debugging:
debug: false
Clear cache!
Twig debugging

Naming templates
field.html.twig
- field--node--field-intro--article.html.twig
- field--node--field-intro.html.twig
- field--node--article.html.twig
- field--field-intro.html.twig
- field--text-long.html.twig
- html
- page
- regions
- blocks
- nodes
- taxonomy terms
- fields
- comments
- forums
- maintenance page
- search result
Naming conventions
Which templates
- templates/a/node.html.twig
- templates/z/node.html.Twig
- templates/a/node.html.twig
- templates/z/node-foo.html.Twig
A -> Z
Specific selector takes preference
Variables
{#
this is a twig comment
twig comment is not rendered in the DOM
#}
{{ variable }}
{% set foo = bar %}
Attributes
<div class="nickelback">
<div {{
attributes
.removeClass('nickelback')
.addClass('rush')
.setAttribute('id', 'top')
}}>
<div id="top" class="rush">
For HTML attributes
Attributes
.addClass()
.removeClass()
.setAttribute()
.removeAttribute()
.hasClass()
More functions
Filters
{% filter upper %}
uppercase is awesome
{% endfilter %}
{{ variable|upper }}
Hardcoded text
Drupal variable
More filters
{{ var|clean_class }}
{{ var|clean_id }}
{{ var|format_date }}
{{ var|raw }}
{{ var|render }}
{{ var|safe_join }}
{{ var|without }}
Twig
{{ 'Home'|t }}
{% trans %}Translatable text{% endtrans %}
{% trans %}
Submitted on {{ date|placeholder }}
{% endtrans %}
Drupal
Without

Libraries
In Drupal 8, stylesheets (CSS) and JavaScript (JS) are loaded through the same system for modules (code) and themes, for everything: asset libraries.
Drupal uses a high-level principle: assets (CSS or JS) are still only loaded if you tell Drupal it should load them.
Drupal does not load every asset on every page, because it slows down front-end performance.
The process
- Save the CSS or JS to a file using the proper naming conventions and file structure.
- Define a "library", which can contain both CSS and JS files.
- "Attach" the library to
- all pages,
- specific Twig templates,
- a render element in a preprocess function.
The process

global-styling
global-styling:
version: 1.x
css:
theme:
css/layout.css: {}
css/style.css: {}
css/colors.css: {}
css/print.css: { media: print }
CSS Properties
global-styling:
version: 1.x
css:
theme:
css/print.css: { media: print }
- all (default)
- screen
css/print.css: { media: type }
all | Suitable for all devices. |
aural | Intended for speech synthesizers. |
braille | Intended for braille tactile feedback devices. |
embossed | Intended for paged braille printers. |
handheld | Intended for handheld devices (typically small screen, monochrome, limited bandwidth). |
Intended for paged, opaque material and for documents viewed on screen in print preview mode. Please consult the section on paged media. |
projection | Intended for projected presentations, for example projectors or print to transparencies. Please consult the section on paged media. |
screen | Intended primarily for color computer screens. |
tty | Intended for media using a fixed-pitch character grid, such as teletypes, terminals, or portable devices with limited display capabilities. |
tv |
Intended for television-type devices. |
Loading order
js-header:
header: true
js:
header.js: {}
js-footer:
js:
footer.js: {}
Multiple declarations
js-header:
header: true
js:
header.js: {}
js-footer:
js:
footer.js: {}
in same libraries.yml file
Weight
base:
foo.css: { weight: - 666 }
js-header:
header: true
js:
header.js: {}
Show in <head>
External
theme:
http://fonts.googleapis.com/css?
family=Loved+by+the+King: { type: external }
JavaScript
global-styling:
version: 1.x
js:
js/scripts.js: {}
JavaScript
global-styling:
version: 1.x
js:
js/scripts.js: {}
/libraries/cycle2/jquery.cycle2.min.js: {}
Place all Downloaded JS/CSS files in a folder /libraries outside of your theme!
Libraries folder

Dependencies
adding jQuery
global-styling:
version: 1.x
css:
base:
css/d8imd.css: {}
js:
js/d8imd.js: {}
dependencies:
- core/jquery
More dependencies
- core/drupal
We need core/drupal in order to take advantage of the Drupal.behaviors.
- core/jquery
To include the Drupal core version of jQuery, we add core/jquery.
- core/jquery.once
A jQuery plugin allowing to only apply a function once to an element.
More dependencies
To get a complete overview of all the core libraries, take a look inside core/core.libraries.yml.
Attaching a library
libraries:
- core/normalize
- d8imd/global-styling
d8imd.info.yml
Attaching a library
{{ attach_library('d8imd/global-styling') }}
foo.html.twig
{{ attach_library(active-theme()~'/global-styling') }}
Example
block--views-block--carousel-block-home.html.twig

Example
trainingstore_foundaton.libraries.yml

Other example
{% if id == "view_carousel" %}
{{ attach_library('theme/carousel') }}
{% endif %}
Another example
{{ attach_library('dazzle_slideshow/dazzle-slideshow-carousel') }}
<section{{ attributes.addClass(classes) }}>
{% endif %}
{{ title_prefix }}
{% if label %}
<h2 {{ title_attributes }}>{{ label }}</h2>
{% endif %}
{{ title_suffix }}
{% if content_attributes|length %}
<div class="block-content" {{ content_attributes }}>
{% endif %}
{% block content %}
<div class="row partner-view">
<div class="cycle-slideshow"
data-cycle-slides=".views-row"
data-cycle-fx=carousel
data-cycle-timeout=4000
>
{{ content }}
</div>
</div>
{% endblock %}
{% if content_attributes|length %}
</div>
{% endif %}
{% if block.delta != 'main' %}
</section>
{% endif %}
Stylesheets remove

d8imd.info.yml
Yes, we have documentation for this to!
Overriding and extending libraries
Drupal coding standards
Drupal 8 follows a SMACSS-style categorization of its CSS rules:
- Base — CSS reset/normalize plus HTML element styling.
- Layout — macro arrangement of a web page, including any grid systems.
- Component — discrete, reusable UI elements.
- State — styles that deal with client-side changes to components.
- Theme — purely visual styling (“look-and-feel”) for a component.
css:
base: base.css
layout: layout.css
component: components.css
css:
base: css/base/normalize.css css/base/elements.css
layout: css/layout/layout.css css/layout/layout--medium.css css/layout/layout--wide.css
component: css/components/button.css css/components/dropdown.css css/components/pagination.css css/components/tabs.css …
theme: css/theme/theme--light.css css/theme/theme--dark.css

JavaScript
Where?

Use libraries

Creating .js file
Coding standards
As part of the Drupal 8 Javascript Coding Standards, all of the javascript code must be declared inside a closure wrapping the whole file. This closure must be in strict mode.
(function () {
'use strict';
// Custom javascript
})();
Plain JS
(function () {
'use strict';
window.alert("sometext");
})();

.js file: using jQuery

.js file: using jQuery
(function () {
'use strict';
$( document ).ready(function() {
console.log( "ready!" );
});
})();

But we have jQuery included?
.js file: using jQuery
(function ($) {
'use strict';
$( document ).ready(function() {
console.log( "ready!" );
});
})(jQuery);

Enter Drupal.behaviors
/**
* @file
* Placeholder file for custom sub-theme behaviors.
*
*/
(function ($, Drupal) {
'use strict';
/**
* Example drupal behavior
*/
Drupal.behaviors.exampleBehavior = {
attach: function (context, settings) {
$('.example', context).once('example-behavior').each(function () {
});
}
};
})(jQuery, Drupal);
Why Drupal.behaviors?
Drupal behaviors (Drupal.behaviors) are still part of javascript in core.
These behaviors will be executed on every request, including AJAX requests.
Calls multiple times
- After an administration overlay has been loaded into the page.
- After the AJAX Form API has submitted a form.
- When an AJAX request returns a command that modifies the HTML, such as
Can also be triggered from a module!
Behaviors examined
Including Drupal object
/**
* @file
* Placeholder file for custom sub-theme behaviors.
*
*/
(function ($, Drupal) {
})(jQuery, Drupal);
Add Drupal & jQuery.once

Behaviors examined
/**
* Example drupal behavior
*/
Drupal.behaviors.awesome = {
attach: function(context, settings) {
$('main', context).once('awesome').append('<p>Hello world</p>');
}
};
Behaviors examined
namespace:
A Drupal behavior has to have a unique namespace.
In this example, the namespace is awesome (Drupal.behaviors.awesome).
Behaviors examined
attach:
Contains the actual function that should be executed.
attach: function (context, settings) {
$('.example', context).once('example-behavior').each(function () {
alert('I\'m helping!');
});
}
Behaviors examined
context:
The context variable is the part of the page for which this applies. On a page load, this will be the entire document.
On a AJAX request however, the context variable will only contain all the newly loaded elements.
Behaviors examined
settings:
The settings variable is used to pass information from the PHP code to the javascript (core/drupalSettings).
Behaviors examined
once:
Using the .once('awesome') will make sure the code only runs once. Otherwise, the code will be executed on every AJAX request.
It adds a processed- class to the main tag (<main role="main" class="awesome-processed">) in order to accomplish this (core/jquery.once).
$('main', context).once('awesome').append('<p>Hello world</p>');
Behaviors examined
detach:
Alongside attach lives detach, which can be used to detach registered behaviors from a page element.
Besides from a context and settings, it also expects a trigger. The trigger is a string containing the causing of the behavior to be detached. Possible causings are:
- unload (default)
- move
- serialize
Advanced example
// Engine
Drupal.sbs_nu_v2.startAnimation = function() {
Drupal.sbs_nu_v2.animateActiveItem();
window.setInterval(function(){
Drupal.sbs_nu_v2.animateActiveItem();
}, 20000);
};
// Init
Drupal.behaviors.sbs_nu_v2 = {
attach: function (context, settings) {
$('#sbs-nu-v2', context).once('init-epg-box', function () {
Drupal.sbs_nu_v2.container = $(this);
Drupal.sbs_nu_v2.init();
});
}
};
.theme functions
Drupal.theme.example = function () {
var markup = '<p>Hello world!</p>';
return markup;
};
Drupal.theme.anotherExample = function (title, name) {
var markup = '<p>Hello ' + title + ' ' + name '!</p>';
return markup;
};
var example = Drupal.theme('example'); // Hello world!
var another_example = Drupal.theme('anotherExample', 'Mr.', 'Dries');
Only executed on theme load or when called from another file
https://sqndr.github.io/d8-theming-guide/javascript/theme-function.html
Multilingual
Use Drupal.t()
// Make string available on translate interface
var close = Drupal.t('Close');
// Same, but with @placeholder
var nodesOfType = Drupal.t('Showing nodes of @type', {@type: nodeType});
Debugging behaviors?
Set breakpoint in devtools
misc/drupal.js

Debugging behaviors?
Set breakpoint in devtools

Debugging behaviors?
Use console.log();
console.log('Hi');
console.log(Drupal);
Docs
Exercise
Add Masonry to a view
Add Masonry to a view
Add a masonry style library and customize the Recipes view.
Pick one:
The goals is for the view to show its content in a masonry style.
Add additional fields to Recipe Card View Mode

Exercise (extra)
Implement your own mobile menu
Use what you have learned so far

Lets get to it!
Drupal 8 Theming IMD W5
By Pieter Mathys
Drupal 8 Theming IMD W5
Les week 5 D8IMD Theming
- 629