The Drupal 8
Theming Experience

Scott Reeves (Cottser)

Scott Reeves

  • Drupal 8 theme system
    and Stable (co-)maintainer

     
  • Provisional Drupal 8 core
    committer

     
  • Team Lead @ Digital Echidna
     
  • Former child pilot

TX (Themer Experience)

DX (Developer Experience)

What we'll cover

  • Core base themes
  • The without filter
  • Twig theme registry loader
  • Working with libraries

Two main camps

  • Sensible ⅔
  • Clean ⅓

Use Classy

Use Stable

base theme: classy
 

my_theme.info.yml:

my_theme.info.yml:

Power to the templates*

{%
  set classes = [
    'node',
    'node--type-' ~ node.bundle|clean_class,
    node.isPromoted() ? 'node--promoted',
    node.isSticky() ? 'node--sticky',
    not node.isPublished() ? 'node--unpublished',
    view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
  ]
%}
{{ attach_library('classy/node') }}
<article{{ attributes.addClass(classes) }}>

Additive vs. Subtractive

{{ content|without('comments', 'links') }}

{{ content.links }}

{{ content|without('comments') }}
{% hide(content.comments) %}
{% hide(content.links) %}
{{ content }}
{{ content.links }}
{% show(content.links) %}

{# Content with links but without comments #}
{{ content }}

Print what you want, when you want

Referencing other Twig templates

  1. By namespace:
     
  2. Full path from Drupal root:
     
"core/themes/stable/templates/block/block.html.twig"
"@stable/block/block.html.twig"

Twig blocks

{% extends "@stable/block/block.html.twig" %}
{% block content %}
  <div class="block-content-wrapper">
    {{ parent() }}
  </div>
{% endblock %}
<div{{ attributes }}>
  {{ title_prefix }}
  {% if label %}
    <h2{{ title_attributes }}>{{ label }}</h2>
  {% endif %}
  {{ title_suffix }}
  {% block content %}
    {{ content }}
  {% endblock %}
</div>

block.html.twig:

Starting to build

{% extends "@stable/block/block.html.twig" %}
{% block content %}
  <div class="block-search-form-wrapper">
    {{ parent() }}
  </div>
{% endblock %}
.
├── my_theme.info.yml
└── templates
    ├── block--search-form-block.html.twig
    └── page.html.twig

block--search-form-block.html.twig:

Starting to grow

{% extends "@stable/block/block.html.twig" %}
{% block content %}
  <div class="block-search-form-wrapper">
    {{ parent() }}
  </div>
{% endblock %}
.
├── my_theme.info.yml
└── templates
    ├── block--search-form-block.html.twig
    ├── block.html.twig
    └── page.html.twig

block--search-form-block.html.twig:

What more do
we need?

Planning for growth

{% extends "block.html.twig" %}
{% block content %}
  <div class="block-search-form-wrapper">
    {{ parent() }}
  </div>
{% endblock %}
.
├── my_theme.info.yml
└── templates
    ├── block--search-form-block.html.twig
    ├── block.html.twig
    └── page.html.twig

block--search-form-block.html.twig:

Referencing other Twig templates

  1. By template name:
     
  2. By namespace:
     
  3. Full path from Drupal root:
     
"@stable/block/block.html.twig"
"block.html.twig"
"core/themes/stable/templates/block/block.html.twig"

Libraries*

cuddly-slider:
  version: 1.x
  css:
    theme:
      css/cuddly-slider.css: {}
  js:
    js/cuddly-slider.js: {}
  dependencies:
    - core/jquery

my_theme.libraries.yml:

Overrides

libraries-override:
  classy/base:
    css:
      component:
        css/components/menu.css: false
  user/drupal.user: false

my_theme.info.yml:

jQuery Update

libraries-override:
  core/jquery:
    js:
      assets/vendor/jquery/jquery.min.js: js/jquery-5000.js

my_theme.info.yml:

Your Custom Modernizr Build

libraries-override:
  core/modernizr:
    js:
      assets/vendor/modernizr/modernizr.min.js: js/modernizr.min.js

my_theme.info.yml:

Extending

libraries-extend:
  user/drupal.user:
    - my_theme/user
  classy/node:
    - my_theme/node

my_theme.info.yml:

Remember:
Drupal 8 is not done.

Thanks.

@Cottser

drupaltwig.org

Twitter: #drupaltwig

IRC: #drupal-twig

The Drupal 8 Theming Experience

By Scott Reeves

The Drupal 8 Theming Experience

Presented at DrupalCon Asia 2016

  • 1,984