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
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
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 and works in limited amount of features
Component-based theming
Components module
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
More library override magic
https://ffwagency.com/blog/managing-css-and-javascript-files-drupal-8-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 Mountain Camp 2017: Drupal 8 component based theming
By lauriii
Drupal Mountain Camp 2017: Drupal 8 component based theming
- 2,997