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 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

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.

*.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 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

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 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

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 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

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 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

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 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

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