Change record status: 
Project: 
Introduced in branch: 
8.0.x
Description: 

In a first step of the "Consensus Banana" (#2289511: [meta] Results of Drupalcon Austin's Consensus Banana,) all CSS class creation is moving out of preprocess functions and into Twig templates. Several of these changes have already been committed, with the classes related to Node templates being the most significant change. This change gives themers greater control over class usage, without having to use preprocess functions and complex logic to remove or add classes.

For Themers

All you need to do is copy the template to your theme. You can then add, remove, or otherwise manipulate the classes however you choose.

For Developers

Classes can still be added in preprocess functions, but any classes added in the template cannot be removed without overriding or modifying the template.

The code below shows some of the changes being worked on for the Field module. (see #2217731: Move field classes out of preprocess and into templates) While those changes have not been finalized, it demonstrates many of the ways to leverage the new API additions.

PHP Before

function template_preprocess_field(&$variables, $hook) {
...
  $variables['attributes']['class'] = array(
    'field',
    'field-' . $variables['entity_type_css'] . '--' . $variables['field_name_css'],
    'field-name-' . $variables['field_name_css'],
    'field-type-' . $variables['field_type_css'],
    'field-label-' . $element['#label_display'],
  );
...

Twig Before

<div{{ attributes }}>
  {% if not label_hidden %}
    <div class="field-label{% if title_attributes.class %} {{ title_attributes.class }}{% endif %}"{{ title_attributes|without('class') }}>{{ label }}:&nbsp;</div>
  {% endif %}
  <div class="field-items"{{ content_attributes }}>
    {% for delta, item in items %}
      <div class="field-item"{{ item_attributes[delta] }}>{{ item }}</div>
    {% endfor %}
  </div>
</div>

PHP After

function template_preprocess_field(&$variables, $hook) {
...
  $variables['entity_type'] = $element['#entity_type'];
  $variables['field_name'] = $element['#field_name'];
  $variables['field_type'] = $element['#field_type'];
  $variables['label_display'] = $element['#label_display'];
...

Twig After

{%
  set classes = [
    'field',
    'field-' ~ entity_type|clean_class ~ '--' ~ field_name|clean_class,
    'field-name-' ~ field_name|clean_class,
    'field-type-' ~ field_type|clean_class,
    'field-label-' ~ label_display|clean_class,
    label_display == 'inline' ? 'clearfix',
  ]
%}
{%
  set title_classes = [
    'field-label',
    label_display == 'visually_hidden' ? 'visually-hidden'
  ]
%}
<div{{ attributes.addClass(classes) }}>
  {% if not label_hidden %}
    <div{{ title_attributes.addClass(title_classes) }}>{{ label }}:&nbsp;</div>
  {% endif %}
  <div{{ content_attributes.addClass('field-items') }}>
    {% for delta, item in items %}
      <div{{ item_attributes[delta].addClass('field-item') }}>{{ item }}</div>
    {% endfor %}
  </div>
</div>

Instead of creating class names in the preprocess function, as seen in the PHP Before example, many of the element's properties are added to variables that get passed to the Twig template. Those variables are then used to create class names directly in the template.

In the Twig After example, an array called 'classes' is populated with class names and is added to the wrapper div's attribute using the addClass() method. attributes.addClass(classes) Classes can also be added directly to an attribute, as seen with content_attributes. content_attributes.addClass('field-items')

The following line demonstrates some of Twig's operators:

'field-' ~ entity_type|clean_class ~ '--' ~ field_name|clean_class

We start with the string 'field-'. The tilde ~ concatenates the value of entity_type to 'field-'. The pipe | is used to apply a filter, in this case clean_class. Clean_class is a filter provided by Drupal to convert strings into CSS compatible class names. The end result for the whole line is something like field-node--body.

See the Twig documentation for more information on operators and working with templates. http://twig.sensiolabs.org/doc/templates.html

See the following issues for the API additions that helped make this happen:

clean_class and clean_id filters

#2283301: Add Twig filters for drupal_html_class() and drupal_clean_id_identifier()

addClass and removeClass methods

#2285451: Create addClass() and removeClass() methods on Attribute object for merging css class names.
Change record: https://www.drupal.org/node/2315471

Impacts: 
Module developers
Themers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done