Drupal 8 Component Based Theming

Twitter @laurii1

Lauri Eskola (lauriii)

Twitter @laurii1

"Themer" vs "Designer"

Data driven front-end

Overlapping work between roles

Achieving DRY is difficult

Traditional problems in Drupal theming

Twig & Debugging

Twig & Debugging


  {#
  /*
   * @file
   * Defines list theme component.
   *
   * Available variables:
   * - items: A list of list items.
   */
  #}
  <ul class="list">
    {% for item in items %}
      <li class="list__item">
        {{ item }}
      </li>
    {% endfor %}
  </ul>

Example Twig file

The “Say Something” Syntax: {{ ... }}

The “Do Something” Syntax: {% ... %}

The Comment Syntax: {# ... #}

Twig & Debugging

Twig & Debugging


  {#
  /*
   * @file
   * Defines list theme component.
   *
   * Available variables:
   * - items: A list of list items.
   */
  #}
  <ul class="list">
    {% for item in items %}
      <li class="list__item">
        {{ item }}
      </li>
    {% endfor %}
  </ul>

Example Twig file


  <?php

  // Array key.
  $node['published'];

  // Object property.
  $node->published;

  // Object method.
  $node->published();

  // Object get method convention.
  $node->getPublished();

  // Object is method convention.
  $node->isPublished();

Twig magic

Accessing array items, object properties and calling methods happens using one syntax.

{{ node.published }}

Twig & Debugging

Twig & Debugging


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

  {% include "link.html.twig" with {
      link: node.url,
      text: 'Read more'|t,
    } only
  %}

Twig Include


  <a href="{{ url }}">{{ text }}</a>

link.html.twig

node--teaser.html.twig

Twig & Debugging


  {% extends "page.html.twig" %}
  {% block content %}
    Modify a small part of the parent template
  {% endblock %}

Twig Blocks

Perfect complement for theme hook suggestions


  <h1>Title</h1>

  {% block content %}
    This will be overridden by page--front.html.twig
  {% endblock %}

  {% block footer %}
    Another block
  {% endblock %}

page.html.twig

page--front.html.twig

Twig & Debugging


  {% embed "content.html.twig" with {
      title: title 
    } only 
  %}
    {% block left %}
      {{ content.left }}
    {% endblock %}

    {% block right %}
      {{ content.right }}
    {% endblock %}
  {% endembed %}

Twig Embed


  <h1>{{ title }}</h1>

  {% block left %}
  {% endblock %}

  {% block right %}
  {% endblock %}

content.html.twig

page.html.twig

Component-based theming

Our deliverables should be systems, not pages

Instead of handing a working page to our clients, we should be handing them re-usable elements that could be used to build other pages.

Component-based theming

What is component?

Self-contained

Re-usable

Nestable

 

Could consist following things

  • Markup
  • Styling
  • JavaScript
  • Other assets

Component-based theming

Component-based theming

Modules

Data

Base theme

Default theme

HTML, CSS, JS

Component library

Node, Views etc

Integration layer

Styleguide centric development

Component library

HTML

Sass

JavaScript

Styleguide

Application(s)

Component-based theming

Component-based theming

Your development process becomes design process!

Component-based theming

Creating our first component

card.html.twig

card.css

 
  <div class="card">
    <h3 class="card__title">{{ title }}</h3>
    <p class="card__content">{{ text }}</p>
    <a href="{{ link_url }}" class="card__link">
      {{ link_text }}
    </a>
  </div>

  .card {
    border: 1px solid grey;
    margin: 20px 35px;
    padding: 20px 35px;
  }

Component-based theming

Creating our first component

Component-based theming

Creating our first component

Place component files into a new folder

themename/components/card

Integration to Drupal

Component-based theming

Disclaimer

This method will easily break lots of Drupal's features

Component-based theming

Component-based theming


  component-libraries:
    themename:
      paths:
        - components
        - templates

themename.info.yml

This configuration maps path

@themename/button.html.twig into two folders:

  • themename/components
  • themename/templates

Configuring Twig for debugging

sites/default/services.yml


  parameters:
    twig.config:    
      debug: true

  <!-- THEME DEBUG -->
  <!-- THEME HOOK: 'node' -->
  <!-- FILE NAME SUGGESTIONS:
     * node--article--teaser.html.twig
     * node--article.html.twig
     * node--teaser.html.twig
     x node.html.twig
  -->
  <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/node.html.twig' -->

Theme suggestions in the HTML as comments:

Component-based theming

Disabling caching while theming

sites/default/services.yml


  <?php

  /**
   * Disable caching.
   */
  $settings['cache']['bins']['render'] = 'cache.backend.null';
  $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';

  /**
   * Disable CSS and JS aggregation.
   */
  $config['system.performance']['css']['preprocess'] = FALSE;
  $config['system.performance']['js']['preprocess'] = FALSE;

  services:
    cache.backend.null:
      class: Drupal\Core\Cache\NullBackendFactory

sites/default/settings.php

Component-based theming

Debugging variables

Devel module ships with Kint module which enables this awesome debugging tool


  {{ kint(node) }}

Component-based theming

Component-based theming

Finding the right template


  <!-- THEME DEBUG -->
  <!-- THEME HOOK: 'node' -->
  <!-- FILE NAME SUGGESTIONS:
     * node--article--teaser.html.twig
     * node--article.html.twig
     * node--teaser.html.twig
     x node.html.twig
  -->
  <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/node.html.twig' -->

Component-based theming


  {% include "@themename/card/card.html.twig" with {
      title: label,
      text: content.body,
      link_url: path('entity.node.canonical', {'node': node.id}),
      link_text: 'Read more'|t,
    } only
  %}

node--article--teaser.html.twig

Create a new template file

Libraries

Loading the component assets

Libraries

Get the component styles loaded


  card:
    css:
      component:
        components/card/card.css: {}

themename.libraries.yml


  {{ attach_library('themename/card') }}

  <div class="card">
    <h3 class="card__title">{{ title }}</h3>
    <p class="card__content">{{ text }}</p>
    <a href="{{ link_url }}" class="card__link">
      {{ link_text }}
    </a>
  </div>

card.html.twig

Libraries

You can include multiple assets for a single component


  card:
    css:
      component:
        components/card/card.css: {}
      state:
        components/card/card-state.css: {}
    js:
      components/card/card.js: {}

themename.libraries.yml

Libraries

Detach inherited assets


  libraries-override:
    classy/base:
      css:
        component:
          css/components/button.css: false

themename.info.yml

Libraries

Another component


  {{ attach_library('bartik/card-container') }}
  <div class="card-container">
    {% for item in items %}
      <div class="card-container__item">
        {{ item }}
      </div>
    {% endfor %}
  </div>

card-container.html.twig


  .card-container {
    display: flex;
  }

card-container.css

Creating library


  card-container:
    css:
      component:
        components/card-container/card-container.css: {}

themename.info.yml

Finding the template name


    <!-- FILE NAME SUGGESTIONS:
     * views-view-unformatted--frontpage--page-1.html.twig
     * views-view-unformatted--page-1.html.twig
     * views-view-unformatted--default.html.twig
     * views-view-unformatted--frontpage--page.html.twig
     * views-view-unformatted--page.html.twig
     * views-view-unformatted--frontpage.html.twig
     x views-view-unformatted.html.twig
    -->

Integrating to Views template


  {% set items = [] %}
  {% for row in rows %}
    {% set items = items|merge([row.content]) %}
  {% endfor %}
  
  {% include "@themename/card-container/card-container.html.twig" with {
      'items': items,
    } only
  %}

views-view-unformatted--frontpage--page.html.twig

Drupal.org issue about making Drupal work component based by default

 

https://www.drupal.org/node/2702061

Drupal.org issue for theme system improvements to better support component- based approach

 

https://www.drupal.org/node/2821399

Resources to get started with a design system

"Themer" vs "Designer"

Data driven front-end

Overlapping work between roles

Achieving DRY is difficult

Traditional problems in Drupal theming

Questions

Drupal 8 component based theming

By lauriii

Drupal 8 component based theming

  • 1,869