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

  1. Save the CSS or JS to a file using the proper naming conventions and file structure.
  2. Define a "library", which can contain both CSS and JS files.
  3. "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)
  • print
  • 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).
print 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:

  1. Base — CSS reset/normalize plus HTML element styling.
  2. Layout — macro arrangement of a web page, including any grid systems.
  3. Component — discrete, reusable UI elements.
  4. State — styles that deal with client-side changes to components.
  5. 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