#drupalsummer @pakmanlh

Theming in Drupal 8

¿Quien soy?

  • Pako Garcia
  • Drupal Front-end developer
  • Miembro de
  • Vegan
  • 日本語の学生

Quienes estamos hoy

  • ¿Desarrolladores?
  • ¿Drupaleros?
  • ¿Themers?
  • ¿Diseñadores?

Conceptos a olvidar

  • Funciones theme() Plantillas twig
  • template_process_hook() desaparece!
  • Ahora serán todo render arrays

¿Render qué?

Render arrays are nested and thus form a tree. Consider them Drupal's «render tree» — Drupal's equivalent of the DOM.
+info

¿Render qué?

$element = [
  '#type' => 'my_element',
  '#config_one' => 'blue',
];

$element['#attributes'] = [
  'id' => 'my_blue_element_1',
  'class' => ['my-element','blue'],
];

summer_theme

Crearemos una carpeta summer_theme
en la carpeta/themes

NUNCA en la carpeta /core/themes

Briconsejo: crear subcarpetas:
/themes/contrib
/themes/custom

summer_theme.info.yml

name: My theme
type: theme
base theme: classy
description: 'My custom theme for the DrupalSummer'
core: 8.x
regions:
  header: Header
  content: Content  # the content region is required
  sidebar_first: 'Sidebar first'
  footer: Footer

Temas en core:

  • Bartik: nuestro viejo amigo, incluye el archifamoso módulo color.
  • Seven: para la administración.
  • Stark: para realizar tests.

Temas base

Para crear un nuevo tema podemos escoger entre dos temas base (ocultos) incluidos en core.

Stable

Marcado mínimo y muy pocas classes. Contiene todas las plantillas y estilos CSS de core.
Favorite horses
Black Stallion
Shadowfax
Hidalgo

Classy

Proporciona muchas classes predeterminadas a partir de diferentes características que se pueden usar para dar estilo.
Favorite horses
Black Stallion
Shadowfax
Hidalgo

Trabajar con Twig

Variables

Imprimir variable

<h2{{ title_attributes }}>{{ label }}</h2>

Acceso a variables

{{ foo.bar }}

Ejecuta la siguiente cadena para obtener el valor

// Elemento de una array.
$foo['bar'];
// Propiedad de un objeto.
$foo->bar;
// Método de un objeto.
$foo->bar();
// Get de un objeto.
$foo->getBar();
// Existencia del valor.
$foo->isBar();
null

Variables

Asignar valores a variable

{%
  set classes = [
    'block',
    'block-' ~ configuration.provider|clean_class,
    'block-' ~ plugin_id|clean_class,
  ]
%}

Condicionales

{% if not page %}
  <h2{{ title_attributes }}>
    <a href="{{ url }}" rel="bookmark">{{ label }}</a>
  </h2>
{% endif %}

Condicionales

{% if user_name %}
  Hi {{ user_name|default('Bartolo') }}
{% endif %}

Condicionales

{% if (summary is not empty) or (description is not empty) %}
  <details>
    {% if summary is not empty %}
      <summary>{{ summary }}</summary>
    {% endif %}
    {% if description is not empty %}
      {{ description }}
    {% endif %}
  </details>
{% endif %}

Estructuras de control

Estructura for simple

{% for content in column.content %}
  {{- content.separator }}{{ content.field_output -}}
{% endfor %}

Estructuras de control

Iterando por clave y valor

{% for key, column in row.columns %}
  <a href="#{{ key }}>{{ column }}</a>
{% endfor %}

Estructuras de control

Variable loop

{% for content in node if content.published %}
  {% if loop.first %}
  {# primer elemento #}
  {% elseif loop.last %}
  {# último elemento loop #}
  {% elseif loop.index == "2" %}
  {# segundo elemento #}
  {% elseif loop.revindex == "2" %}
  {# segundo elemento contando alrevés #}
  {% else %}
  {{- content.separator }}{{ content.field_output -}}
  {% endif %}
{% endfor %}

Atributos

Imprimir atributos de una etiqueta

Hello Drupaleros!
<div{{ attributes }}>

O bien imprimir solamente aquellos atributos que nos interesen

<div class="myclass {{ attributes.class }}"{{ attributes|without('class') }}>

Atributos

Manipular los valores; añadiendo o quitando elementos.

<div{{ attributes.addClass('hello').removeClass('bye') }}>

Realizando comprobaciones.

{% if attributes.hasClass('field-label-inline') %}
  {# My inline custom code #}
{% endif %}

Atributos

Establecer valores

<div{{ attributes.setAttribute('id', 'menu-link') }}>

Borrar valores

<div{{ attributes.removeAttribute('id') }}>

Filtros

Se pueden usar para manipular las variables...

{{ ponies|safe_join(", ")|lower}}

...o para traducir cadenas

<span class="marker">{{ 'New'|t }}</span>

Traducir cadenas con variables

Aunque para traducir cadenas que incluyan variables se puede usar

{% trans %}
  Submitted by {{ author_name }} on {{ date }}
{% endtrans %}

Filtros

clean_class, format_date, without, drupal_escape,...

+info

Reuso de código en plantillas

Se pueden definir zonas en las plantillas que sean modificadas (extendidas) en otras plantillas.

Herencia de plantillas

Definimos una «zona variante»
page.html.twig


{% block variante %}
{% endblock %}
					

Herencia de plantillas

Heredamos toda la página,
modificando solamente ese trozo
page--front.html.twig


{% extends "page.html.twig" %}
{% block variante %}

Contenido específico de la página principal.

{% endblock %}

Reuso de código en plantillas

Aprovechar código para regiones iguales


{% include('sidebar.twig') %}

Herencia de plantillas

block.html.twig

<div{{ attributes.addClass(classes) }}>
  {{ title_prefix }}
  {% if label %}
    <h2{{ title_attributes }}>{{ label }}</h2>
  {% endif %}
  {{ title_suffix }}
  {% block content %}
    {{ content }}
  {% endblock %}
</div>

Herencia de plantilles

block--local-actions-block.html.twig

{% extends "block.html.twig" %}
{#
/**
 * @file
 * Theme override for local actions (primary admin actions.)
 */
#}
{% block content %}
  {% if content %}
    <nav class="action-links">{{ content }}</nav>
  {% endif %}
{% endblock %}

Debugging

services.yml:

parameters:
  twig.config:
    debug: true

Debugging

Obtenemos información adicional

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
   * block--bartik-powered.html.twig
   * block--system-powered-by-block.html.twig
   * block--system.html.twig
   x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<div class="block block-system contextual-region" id="block-bartik-powered" role="complementary">
  <div data-contextual-id="block:block=bartik_powered:"></div>
  <div class="content">
    <span>Powered by <a href="http://drupal.org">Drupal</a></span>
  </div>
</div>
<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->

Chrome extension

Drupal Template Helper

Debugging

Usando el módulo Devel y Kint search

{{ kint(content) }}
Ejemplo

Consejos útiles

Configurar la visibilidad de los filtros contextuales y la toolbar de Drupal

Añadir CSS y JS a nuestro tema

Ahora todos los ficheros a cargar se definen en
summer_theme.libraries.yml

my-library:
  version: 1.x
  css:
    theme:
      css/my-library.css: {}
  js:
    js/my-library.js: {}

Añadir CSS global

global-styling:
  version: 1.x
  css:
    component:
      css/components/action-links.css: { weight: -10 }
      css/components/breadcrumb.css: { weight: -10 }
      css/components/button.css: { weight: -10 }
    layout:
      css/layout.css: {}
    theme:
      css/style.css: {}
      css/colors.css: {}
      css/print.css: { media: print }

Gestión de dependencias

Drupal 8 no carga jQuery, por ejemplo, si no lo especificamos.

my-library:
  version: 1.x
  css:
    theme:
      css/my-library.css: {}
  js:
    js/my-library.js: {}
  dependencies:
    - core/jquery

Añadir una librería

De forma global

name: My theme
type: theme
base theme: classy
description: 'My custom theme for DrupalSummer'
core: 8.x
libraries:
  - my_theme/my-library

Añadir una librería

En un template de Twig

{{ attach_library(active_theme()'/my-library') }}
<div>Some markup {{ message }}</div>

Añadir una librería

En una página concreta, tenemos que crear el archivo summer_theme.theme

function summer_theme_preprocess_page(&$variables) {
  $variables['#cache']['contexts'][] = 'route';
  if (\Drupal::routeMatch()->getRouteName() === 'entity.node.preview') {
    $variables['#attached']['library'][] = 'summer_theme/my-library';
  }
}

my_theme.breakpoints.yml

my_theme.mobile:
  label: mobile
  mediaQuery: ''
  weight: 0
  multipliers:
    - 1x
my_theme.narrow:
  label: narrow
  mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
  weight: 1
  multipliers:
    - 1x
my_theme.wide:
  label: wide
  mediaQuery: 'all and (min-width: 851px)'
  weight: 2
  multipliers:
    - 1x

That's all folks

¿Preguntas?


pako@ymbra.com @pakmanlh

Gracias a

Bola extra: Uso de macros

{% import _self as menus %}
{{ menus.menu_links(items, attributes, 0) }}

{% macro menu_links(items, attributes, menu_level) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
      
    {% else %}
      
    {% endif %} {% for item in items %} {{ link(item.title, item.url) }} {% if item.below %} {{ menus.menu_links(item.below, attributes, menu_level + 1) }} {% endif %} {% endfor %}
{% endif %} {% endmacro %}

Uso de macros

+info +info