"Themer" vs "Designer"
Data driven front-end
Overlapping work between roles
Achieving DRY is difficult
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>
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>
<?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();
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
%}
<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 %}
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 %}
<h1>{{ title }}</h1>
{% block left %}
{% endblock %}
{% block right %}
{% endblock %}
content.html.twig
page.html.twig
Component-based theming
Self-contained
Re-usable
Nestable
Could consist following things
Component-based theming
Component-based theming
Modules
Data
Base theme
Default theme
HTML, CSS, JS
Component library
Node, Views etc
Integration layer
Component library
HTML
Sass
JavaScript
Styleguide
Application(s)
Component-based theming
Component-based theming
Component-based theming
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
Component-based theming
Place component files into a new folder
themename/components/card
Component-based theming
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:
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
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
Devel module ships with Kint module which enables this awesome debugging tool
{{ kint(node) }}
Component-based theming
Component-based theming
<!-- 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
Libraries
Libraries
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
card:
css:
component:
components/card/card.css: {}
state:
components/card/card-state.css: {}
js:
components/card/card.js: {}
themename.libraries.yml
Libraries
libraries-override:
classy/base:
css:
component:
css/components/button.css: false
themename.info.yml
Libraries
{{ 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
card-container:
css:
component:
components/card-container/card-container.css: {}
themename.info.yml
<!-- 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
-->
{% 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
"Themer" vs "Designer"
Data driven front-end
Overlapping work between roles
Achieving DRY is difficult