HOWTO.drupal8_theming - raynaldmo/HOWTO GitHub Wiki

Table of Contents

Drupal 8 Theming

Courses & Resources

Bootstrap

Useful Modules

What is a Theme ?

  • Presentational layer to content
  • Consists of HTML markup, CSS JavaScript and assets (images, video, audio)

Theme Types

Core Themes

  • Drupal core comes with a few themes. These are suitable for very basic sites
  • Bartik
    • Integrates with color module to provide COLOR SCHEME page/selection
    • Good example of how to create a custom theme
    • Implements responsive features
  • Classy
    • TBD
  • Stark

Contributed Standard Themes

Contributed Starter and Base Themes

Custom Themes

  • Custom themes are created as a subtheme of a Starter or Base theme
  • Most sites use a custom theme to create a custom look and feel

Contributed Administration Themes

Premium Themes

  • Pre-made themes available for purchase

Theme Administration

  • TBD
  • Installing new themes
  • Appearance Settings etc.
  • Logo, favicon etc.

Theme System Overview

Theme Components

  • THEME.info.yml file
  • THEME.libraries.yml file
  • CSS, JS and media (image, video, audio) files
  • THEME.theme file
  • TWIG Template files (page.html.twig etc)
  • Regions

Theme Files

  • THEME.info.yml
  • Located in theme root directory
  • Provides metadata (name, type, base theme etc.) for theme
  • libraries key is used to specify CSS and JS files to load
# THEME.info.yml file
name: Example
description: 'An Example theme.'
type: theme
package: Custom
base theme: classy
core: 8.x

libraries:
  - example/global-styling

regions:
  header: Header
  primary_menu: 'Primary menu'
  secondary_menu: 'Secondary menu'
  page_top: 'Page top'
  page_bottom: 'Page bottom'
  highlighted: Highlighted
  breadcrumb: Breadcrumb
  content: Content
  sidebar_first: 'Sidebar first'
  sidebar_second: 'Sidebar second'
  footer: 'Footer'
  • THEME.libraries.yml
    • Used to specify location of CSS and JS files
  • CSS, JS and media files (non-Drupal specific)
    • CSS files (in css directory)
    • JavaScript files (in js directory)
    • Image files (in images directory)
    • Drupal provides assets like jQuery in core/assets/vendor/ directory
  • THEME.theme file
    • Each theme can have one THEME.theme file
    • Contains theme specific PHP functions, Preprocess functions and Hooks
      • All can be used to over-ride core functionality
    • Replaces template.php in previous Drupal versions
    • Can be used to manipulate dynamic content of template files
  • TWIG template files (*.html.twig)
    • Located in theme templates directory
    • Provides the layout and HTML markup of your theme
    • Consists of HTML and TWIG template syntax for dynamic elements
  • THEME.breakpoints.yml file
    • Used to add media queries for different viewports (mobile, tablet, desktop) etc.
  • When using breakpoints optionally setup Responsive Images support
    • Enable Core / Responsive Image module
    • Go to admin/config/media/responsive-image-style to configure Image styles
    • Labels will be those defined in THEME.breakpoints.yml file

Regions

  • Regions (Header, Main Menu, Status, Content, Left Sidebar, Right Sidebar, Footer etc.) are areas of a page into which content can be placed
    • Regions are defined in a theme's THEME.info.yml file
  • Content is assigned to regions via content Blocks
  • Blocks contain individual pieces of a site’s content
  • Block examples:
    • Site Branding (logo, site name)
    • Main navigation
    • Search form
    • User account form / status
    • Page title
    • Main page content
    • Footer menu
    • Custom view
    • Custom content
    • Custom block
    • etc.
  • Content can then be displayed via various Display / View Modes and Content Layout modules
    • Example Content Layout modules are Layout Builder, Display Suite, Panels
  • Themes define regions in the THEME.info.yml file
  • Sample set of regions
    • Note: page_top and page_bottom don't appear in Block layout page (admin/structure/block) but are required
regions:
  header: Header
  content: Content
  sidebar_first: Sidebar First
  footer: Footer
  page_top: Page top
  page_bottom: Page bottom
  • Regions give your site layout, and your markup its structure
  • Drupal assigns default regions for a theme if none are defined in THEME.info.yml
  • If any region is defined in THEME.info.yml all desired regions must be defined

TWIG Templates

Twig Related Modules

Twig HOWTOs

Twig Tweak HOWTOs

{# Twig Tweak file_uri filter #}
{% set image_uri = 
   paragraph.field_image|file_uri // also node.field_image works
%}
{% if image_uri is not empty %}
  {# Use Twig Tweak image_style filter to get url #}
  {% set image_url = 
   image_uri|image_style('image_style_machine_name')
  %}
{% endif %}
{% if image_uri is not empty %}
  {# Use Twig Tweak to render image #}
  {{ drupal_image(image_uri, 'image_style_machine_name') }} // 
{% endif %

Twig Debugging

Regions and Templates

  • Core TWIG template files are located at:
    • core/modules/system/templates
    • core/modules/node/templates
    • core/modules/comment/templates
    • core/modules/block/templates

  • HTML wrapper (html.html.twig)
    • Contains the top-level HTML markup, including title, metadata, style sheets, and scripts
  • Page wrapper (page.html.twig)
    • Contains the content generally found between the body tags of an HTML document
  • Header (region.html.twig)
    • Maps to Header region
    • Contains header content of web page
    • Can be part of the page.html.twig template or in a region specified within THEME.info.yml
  • Content
    • Maps to Content region
    • Generally contains main page content
    • Can consist of multiple sub-content regions, such as nodes and comments
    • Nodes and comments each have their own respective templates node.html.twig and comment.html.twig
  • Sidebar
    • Maps to Sidebar regions
    • Contains blocks of content
    • Blocks are either created by the end user or by Drupal itself
    • Content within blocks generally reside within block.html.twig
  • Footer
    • Maps to Footer region
    • Contains HTML content as well as blocks of content

TWIG Template Overrides

  • Any existing template can be over-ridden
  • Inspect content and see which template is being used
  • Copy (and optionally rename) template to THEME/templates folder
  • Modify template per design requirements

Theme Hooks

Preprocess Functions

  • Preprocessing and modifying attributes in a .theme file
  • A preprocess function is an implementation of hook_preprocess_HOOK
  • TWIG template files use variables to output data retrieved from the database
  • Preprocess functions are very useful as they can be used to create or modify variables and/or render arrays
    before they’re passed to a TWIG template
  • A core set of preprocess functions are defined in web/core/includes/theme.inc
    • template_preprocess_html - creates variables for html.html.twig template
    • template_preprocess_page - creates variables for page.html.twig template
    • template_preprocess_node - creates variables for node.html.twig template
  • Themes and modules can implement hook_preprocess_HOOK preprocess functions to add to or override the core
    template_preprocess_HOOK_NAME preprocess functions
    • Examples: THEME_preprocess_page, MODULE_preprocess_page
  • Modules that define a theme hook (via hook_theme) can implement template_preprocess_HOOK_NAME
  • template_preprocess_HOOK_NAME hooks fire BEFORE hook_preprocess_HOOK hooks

Example Preprocess Functions

// file: THEME.theme
<?php
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_preprocess_page()
 */
function THEME_preprocess_page(&$variables) {
    // Add container class to page.html.twig template
    $variables['attributes']['class'][] = 'container';
}

/**
 * Implements hook_preprocess_block().
 */
function THEME_preprocess_block(&$variables) {
  // Add layout class to Featured Blocks
  $featured = array('block-customblockone','block-customblocktwo','block-customblockthree');
  $id = $variables['attributes']['id'];

  // If block id matches list - add class
  if(in_array($id, $featured)) {
    $variables['attributes']['class'][] = 'col-md-4';
  }

}
/**
 * Implements hook_preprocess_field().
 */
function THEME_preprocess_field__block_content__hero(&$variables) {
   $variables['my_date'] = '<h3>'. date("Y-m-d") . '</h3>';
}

/**
 * Implements hook_preprocess_field().
 */
function THEME_preprocess_field__block_content__field_learn_more__hero(&$vars) {
  // <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more &gt;&gt;</a></p>

  $vars['items'][0]['content']['#options']['attributes']['class'][] = 'btn btn-primary btn-lg';
  $vars['items'][0]['content']['#options']['attributes']['role'] = 'button';
  $title = $vars['items'][0]['content']['#title'];
  $title = new FormattableMarkup(
    '@title&nbsp;&nbsp;<span style="font-size:0.7em;" class="glyphicon glyphicon-forward" aria-hidden="true"></span>', [
    '@title' => $title,
  ]);

  $vars['items'][0]['content']['#title'] = $title;
}

/**
 * Implements hook_preprocess_node().
 */
function THEME_preprocess_node(&$variables) {
  // Get the node's content type
  $type = $variables['node']->getType();

  // Get its view mode
  $mode = $variables['view_mode'];

  // Make sure we have a class array
  if (!isset($variables['attributes']['class'])) {
    $variables['attributes']['class'] = [];
  }

  // Add our classes
  $variables['attributes']['class'][] = 'node--type-' . $type; // ex: node--type-article
  $variables['attributes']['class'][] = 'node--mode-' . $mode; // ex: node--mode-teaser
  $variables['attributes']['class'][] = 'node--type-' . $type . '--mode-' . $mode; // ex: node--type-article--mode-teaser
}

/**
 * Implements template_preprocess_image
 */
function THEME_preprocess_image(&$variables) {
  // Add class to <img> tag based on the image style
  // Check the image style.
  if ($variables['style_name'] == 'blog_image') {
    // Set class.
    $variables['attributes']['class'][] = 'img-fluid';
  }
}

Other Example Hooks

  • See *.api.php for all core and module defined hooks
  • For example, see form.api.php for all form related hooks
/**
 * Implements hook_form_FORM_ID_alter
 */
function THEME_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Add alterations specific to the user registration form
  // FORM_ID is the CSS id of the form
  // In this example id = user-registration-form
  // In the function name convert "-" (hyphens) in form id to "_"  (underscores)
}

/**
 * Implements hook_form_alter().
 */
function THEME_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'user_register_form') {
    // Modify form
  }
}

Theme Hook Suggestions

Example Theme Hook Suggestions

/**
 * Implements hook_theme_suggestions_HOOK_alter
 */
function THEME_theme_suggestions_container_alter(&$suggestions, $vars) {
  $element = $vars['element'];
  if (isset($element['#type'])) {
    if ( $element['#type'] == 'webform_actions' &&  $element['#webform'] == 'newsletter_signup') {
      // container--webform-actions-newsletter-signup.html.twig
      $suggestions[] = 'container__' . $element['#type'] . '_' . $element['#webform'];
    }
  }
}

/**
 * Implements hook_theme_suggestions_HOOK_alter
 */
function THEME_theme_suggestions_input_alter(&$suggestions, $vars) {
  $element = $vars['element'];
  // Newsletter submit button
  if (isset($element['#attributes']['data-twig-suggestion'])) {
    // input--submit--webform-newsletter-submit.html.twig
    $suggestions[] = 'input__' . $element['#type'] . '__' . $element['#attributes']['data-twig-suggestion'];
  }

  // Search form input field in Header
  if ($element['#attributes']['name'] == 'search_api_fulltext') {
    //  input--textfield--search-api-fulltext.html.twig
    $suggestions[] = 'input__' . $element['#type'] . '__' . $element['#attributes']['name'];
  }
}

function THEME_theme_suggestions_image_alter(&$suggestions, $vars) {
  dsm($vars);
}

Theme Settings

Color Module

Theming Paragraphs

Theming Views

Theming Views Fields

View Structure Diagram

View Structure Diagram

  • Views will most likely need custom styling
  • Three ways to approach this
    • Use the Drupal Views UI
      • Remove default classes
      • Add your own custom classes
      • Add basic styling to field output
      • Rewrite field output (with custom markup)
    • Modify existing views template files
    • Create your own template files specific to your view

Views Template Hierarchy

  • Example for Table Display format
html.html.twig
  page.html.twig
    block.html.twig
      views-view.html.twig
        form.html.twig
          views-exposed-form.html.twig
            form-element.html.twig
              form-element-label.html.twig
              input.html.twig
            form-element.html.twig
              form-element-label.html.twig
              input.html.twig
            container.html.twig
              input.html.twig
        views-view-table.html.twig
          views-view-fields.html.twig
            views-view-field.html.twig
            views-view-field.html.twig
            views-view-field.html.twig
        pager.html.twig
        container.html.twig
          views-view.html.twig

Template Name Example

  • For a view named "Features"
  • Main view template - views-view--features.html.twig
  • Template for unformatted rows display - views-view-unformatted--features.html.twig
  • Template to display all the fields in a row - views-view-fields--features.html.twig
  • Can also use hook_theme_suggestions_HOOK or hook_theme_suggestions_HOOK_alter to customize
    views template file name

Page Construction

Home / Front Page

  • By default:
    • Site's home page is set to http(s)://<site-name>/node
      • Set in admin/config/system/site-information
    • Home page by default points to Frontpage view - admin/structure/views/view/frontpage
    • Template page--front.html.twig is normally used to render home page and renders
      Frontpage view
    • This view is rendered via Content region (page.content) -> Main page content block
  • In a lot of cases though page--front.html.twig is constructed to not render Frontpage view
    but instead renders selected regions (and the assigned blocks)
  • Main content region page.content and its blocks may not necessarily be rendered

Content Page

  • Pages that display a content type (Article content, Page content, other content types)
  • Page template page.html.twig is used by default
  • Node template is used
    • node.html.twig
    • node--page.html.twig
    • etc
  • Node content is rendered via Content region (page.content) -> Main page content block
  • Other regions (and their assigned blocks) can also be added

View Page

  • Page created from a view
  • Page template page.html.twig is used by default
  • View template (views-view.html.twig) along with a _Node _template is used
  • View content is rendered via Content region (page.content) -> Main page content block
  • Other regions (and their assigned blocks) can also be added

Contact Page

  • Applies if using core Contact Form module (admin/structure/contact)
  • Page template page.html.twig is used by default
  • Template core/modules/system/templates/form.html.twig is used by default to render contact form
  • Form content is rendered via Content region (page.content) -> Main page content block
  • Other regions (and their assigned blocks) can also be added

Search Page

  • Page template page.html.twig is used by default
  • Templates core/modules/system/templates/form.html.twig and core/modules/system/templates/item-list.html.twig
    are used by default

Webform

Theme Development Setup

Set up local develop environment

  • Local environment should be setup to:
    • Disable CSS/JS aggregation
    • Disable render and page cache
    • Enable Twig debugging
  • Copy sites/example.settings.local.php to sites/default/settings.local.php
  • In sites/default/settings.php, enable the following lines:
if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) {
   include $app_root . '/' . $site_path . '/settings.local.php';
}
  • In sites/default/settings.local.php, configure the following:
/**
 * Disable CSS and JS aggregation.
 */
$config['system.performance']['css']['preprocess'] = TRUE;
$config['system.performance']['js']['preprocess'] = TRUE;
...

/**
 * Disable the render cache (this includes the page cache).
 */
$settings['cache']['bins']['render'] = 'cache.backend.null';
...

/**
 * Disable Dynamic Page Cache.
 */
$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';

/**
 * Allow test modules and themes to be installed.
 */
$settings['extension_discovery_scan_tests'] = FALSE;
  • At this point all site accesses will grab new copy of all files

    • No need to clear Drupal cache to see changes
    • In some instances may need to navigate to core/rebuild.php to fix some issues
  • In sites/development.services.yml add twig.config parameters to enable TWIG debugging

parameters:
  http.response.debug_cacheability_headers: true
  twig.config:
    debug: true
    auto_reload: true
    cache: false
services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory
  • Clear/rebuild cache
drush cr all

Use Twig dump() or dsm() to show variables

  • Note: Using dump() causes WSOD, dsm() works fines
{{ dump(content)}}
{{ dsm(content)}}

Creating Themes

  • There a two approaches to creating themes
    • Starter Theme
    • Sub-Theme

Starter Theme

  • Useful for Leveraging a CSS framework like Bootstrap
  • No base theme is used
  • In THEME.info.yml set base theme to false
...
base theme: false
...
  • This allows full control over theme assets (CSS, JS) and Twig templates
  • Removes need to override any existing markup (TWIG template files) from a base theme

Sub-Theme

...
base theme: classy
...
  • Leverages markup and default classes of a good base theme like classy
  • Disadvantage is that a lot of markup (TWIG template files) may need to be overridden

Design Mockup Review

  • After deciding to implement Starter theme or Sub-theme for a particular design, perform a Mockup Review
  • Mockup should provide
    • HTML/CSS/JavaScript files
    • Images
    • YouTube video links
    • Logo
    • favicon
  • For each page (Home, Products, Services, Contact, Search etc.)
    • Identify page layout (note differences page to page)
    • Identify regions needed
    • Note user experience features and characteristics
      • Fonts
      • Menu type, Fixed header, sticky header etc
      • Search box behavior
      • Slideshow ,carousel,
      • Icons
      • Animations
      • Footer design
  • Identify content types
  • Identify taxonomy categories / terms
  • Identify views
  • Select Layout tool (Layout Builder, Display Suite, Paragraphs, Panels)

Starter Theme Implementation

Sub-Theme Implementation

  • Choose / Install base theme
  • Create theme folder (i.e themes/custom/SUBTHEME)
  • Create SUBTHEME.info.yml file
    • Set base theme: BASETHEME
  • If required, incorporate Bootstrap (or other CSS framework) files
  • Create SUBTHEME.libraries.yml file
    • Add Google fonts, FontAwesome, Javascript Libraries
  • Add libraries file references to SUBTHEME.info.yml file
  • Copy (or create) site logo, favicon, screenshot image to folder
  • Copy mockup CSS and JS to css/ and js/ folders
  • Use SMACSS standard to organize CSS files
    • CSS File Organization
    • Minimum set of files base.css, layout.css, component.css
    • Organize CSS and JS files into subdirectories
      • css/base, css/layout, css/components etc.
      • js/components, etc.
  • Add desired regions to SUBTHEME.info.yml
  • Use libraries-override key in SUBTHEME.info.yml file to override CSS and JavaScript
  • Example below overrides classy message.css with sassy version (not shown)
# sassy.info.yml
name: Sassy
type: theme
description: 'A Classy sub theme but a little more Sassy'
core: 8.x
base theme: classy
libraries-override:
  classy/messages: sassy/messages
# sassy.libraries.yml
messages:
  version: VERSION
  css:
    theme:
      css/messages.css: {}
  • Install theme and set as default theme
  • Perform site building
    • Configure content paths with pathauto
    • Create content types
    • Create taxonomy categories / terms (if required)
    • Generate sample content
    • Create Views
  • Implement Design Page-by-Page
    • Incorporate Mockup CSS / JS into page layout and components
    • Over-ride page / region / block templates as necessary

Example Design Mockup Review and Sub-Theme Implementation (eg theme)

  • From OSTraining - How to Design Drupal 8 Themes
* Design Theme for fictional Elegant Gardens website
* Page mockups and assets are located in ```Development/mockups/eg```
* This design uses a Sub-theme based on ```classy``` base theme

### Design Mockup Review
* Mockups should provide all html/css/javascript files_
* For each page (home, products, services, contact, search etc.)
  * Identify page layout (note differences page to page)
  * Identify regions needed
  * Is page responsive ?
  * Note user experience features and characteristics
    * Fonts
    * Fixed header
    * Search box behavior
    * Full page "slider"
    * Icons
    * Animations
* Identify content types
* Identify taxonomy categories / terms
* Identify views
* Select Layout tool (Layout Builder, isplay Suite, Paragraphs, Panels)
      
#### Responsive
* Site doesn't appear to take responsive design into  consideration
   
#### Navigation
* Horizontal, selected link is highlighted
* Logo is svg file
* Doesn't adjust for smaller screen sizes

#### Home Page
* Header image
* Three columns of content, middle column has a set of links
* Probably can just create a front page and use custom css to produce the columns ?
  * Also consider creating a Landing Page content type
  * Use a block for each column ?
* Note: Author used Banner content type and blocks for home / front page but  
doesn't explain this decision
           
#### Services Page
* Need to create Services content type
* Need to create page view to show services in a grid

#### Blog Page
* Create Blog content type
  * Uses sprites for author, post date, comments icon
  * Replace with font-awesome icons ?
* Two column page design, use block view for archive list
* Note: Author decided to use basic Article content type for blog instead of Blog content type
  
#### Contact Page
* Use standard Contact form
* Will need custom css styling

### Sub-Theme Implementation
* Create ```themes/custom/eg``` folder
* Create ```eg.info.yml``` file
* Create ```eg.libraries.yml``` file
  * Add google fonts
* Copy mockup ```screenshot.png, logo.svg and favicon.ico``` to theme folder
* Copy ```Development/mockups/eg/css/styles.css``` to theme ```css/base/```
* Decide on and add desired regions to ```eg.info.html```
  * Author doesn't explain why he chose these particular regions
* _At this point enough of the sub-theme iniital work is done - move to  
initial site building phase_

### Site Building
#### Create content types
* Service Content type
* Banner Content - for site front page
* Article (Blog) Content type
* _Generate various content before working on templates_
  * Articles
  * Banner
  * Services
  
#### Create Views  
* Create Services Page view
* Create Article / Blog page view
* Note: Author uses fields option to build Blog view
* Construct Home Page as a set of blocks
  * Create "Work" block view
    * This is a view for articles tagged with work
    * Note: The need for this view wasn't very obvious from the analysis of the  
    mockup
  * Create "Banner View" block view
  * Create "Who" custom block
  * Create "My Philosophy" custom block
  * Place blocks in appropriate regions
* **_Note:_** Not sure why author didn't use built-in Front page view for the front page

#### Contact form
* Create contact form
* Add contact form link to main navigation menu

### Sub-Theme Template Overrides
* Create ```web/themes/custom/eg/templates/page--front.html.twig``` from  
```web/core/themes/classy/templates/layout/page.html.twig```
* Note: At this point suthor has us copy CSS files he built  
  BUT DOESN'T EXPLAIN HOW THEY WERE BUILT!! 
* This is really a lot of the work of integrating the Mockup CSS into the theme  
  
### Misc Additions
* Replace floats with flexbox -- DONE
* Add and style Article page  -- DONE
* Review CKEditor config and style content with CKEditor 
* Implement responsive navigation bar / site header - DONE but revisit
* Make site responsive -- DONE
* Add Blog Image style and Responsive Image style -- DONE
* Use font-awesome for Article / Blog icons  -- DONE
* Style and get search box working  -- TODO
  * Re-style search box button
   * Fix search form border - doesn't look right on FF
* Add footer regions and style -- DONE
  * Use footer-mockup.png  -- TODO
  * Add social media icons -- DONE
* Fix block region demonstration page  -- DONE
* Review and set up Responsive Images  -- DONE
* Check site on all browsers and mobile devices
  * Use ngrok
* Use layout builder to style Article and Service content -- TODO
* Add and style pagers for Blog and Services page -- TODO
* Investigate Smart Trim module -- TODO
* Center content on mobile and tablet views
* Experiment with e-commerce solutions  

⚠️ **GitHub.com Fallback** ⚠️