
Drupal 8 - Twig Templates for Bootstrap Forms
Published: 8/2/2017
One of my first major Drupal 8 projects required a good bit of custom theming due to a) designing a custom administrative theme, and b) working with custom entity types. Because the site contained multiple forms, making them look nice was important to the project. Using a Bootstrap base, I themed the forms to create nicely-positioned elements with inline labels.
I started by copying the following Twig templates from core into my theme:
core/themes/classy/templates/form/form.html.twig
core/themes/classy/templates/form/form-element.html.twig
I initially modified form.html.twig to wrap all forms in a Bootstrap class that limited the form to a width of 66%. Depending on the implementation, this may not be a desired format, but I found that my forms looked better when they didn't naturally span 100% of their area.
However, I later removed the wrapper after discovering that it also applied to View results, which I did not want.
{#
/**
* @file
* Default theme implementation for a 'form' element.
*
* Available variables
* - attributes: A list of HTML attributes for the wrapper element.
* - children: The child elements of the form.
*
* @see template_preprocess_form()
*
* @ingroup themeable
*/
#}/*
* Add Bootstrap class col-lg-8 to limit form width to 66%
*/
<div class="col-lg-8">
<form{{ attributes }}>
{{ children }}
</form>
</div>
So form.html.twig was reverted to the original code:
{#
/**
* @file
* Default theme implementation for a 'form' element.
*
* Available variables
* - attributes: A list of HTML attributes for the wrapper element.
* - children: The child elements of the form.
*
* @see template_preprocess_form()
*
* @ingroup themeable
*/
#}
<form{{ attributes }}>
{{ children }}
</form>
I still didn't want my forms spanning 100%, so in addition to other changes, I implemented the Bootrap wrapper in form-element.html.twig. Additional changes included: setting classes at the element level, changing the Description font size and color, and float and width adjustments to labels and input elements.
{%
set classes = [
'js-form-item',
'form-item',
'col-lg-8',
'js-form-type-' ~ type|clean_class,
'form-type-' ~ type|clean_class,
'js-form-item-' ~ name|clean_class,
'form-item-' ~ name|clean_class,
disabled == 'disabled' ? 'form-disabled',
errors ? 'form-item--error',
]
%}
{%
set description_classes = [
'description',
'f-s-12',
'text-grey-darker',
description_display == 'invisible' ? 'visually-hidden',
]
%}
<div{{ attributes.addClass(classes) }}>
<div class="form-group row">
<div class="col-form-label col-md-3">
{% if label_display in ['before', 'invisible'] %}
{{ label }}
{% endif %}
</div>
<div class="col-md-9">
{% if prefix is not empty %}
<span class="field-prefix">{{ prefix }}</span>
{% endif %}
{% if description_display == 'before' and description.content %}
<div{{ description.attributes }}>
{{ description.content }}
</div>
{% endif %}
{{ children }}
{% if suffix is not empty %}
<span class="field-suffix">{{ suffix }}</span>
{% endif %}
{% if label_display == 'after' %}
{{ label }}
{% endif %}
{% if errors %}
<div class="form-item--error-message">
<strong>{{ errors }}</strong>
</div>
{% endif %}
{% if description_display in ['after', 'invisible'] and description.content %}
<div{{ description.attributes.addClass(description_classes) }}>
{{ description.content }}
</div>
{% endif %}
</div>
</div>
</div>
I also themed the Submit button, input--submit.html.twig, using some Bootstrap classes for size and positioning:
{#
/**
* @file
* Theme suggestion for "button" input form element.
*
* Available variables:
* - attributes: A list of HTML attributes for the input element.
* - children: Optional additional rendered elements.
* - icon: An icon.
* - icon_only: Flag to display only the icon and not the label.
* - icon_position: Where an icon should be displayed.
* - label: button label.
* - prefix: Markup to display before the input element.
* - suffix: Markup to display after the input element.
* - type: The type of input.
*
* @ingroup templates
*
* @see \Drupal\bootstrap\Plugin\Preprocess\InputButton
* @see \Drupal\bootstrap\Plugin\Preprocess\Input
* @see template_preprocess_input()
*/
#}
{% spaceless %}
{%
set classes = [
'btn', 'btn-150', 'btn-sm', 'btn-success', 'm-r-5', 'm-b-10', 'm-t-10', 'm-l-10',
type == 'submit' ? 'js-form-submit',
icon and icon_position and not icon_only ? 'icon-' ~ icon_position,
]
%}
<div class="form-group row m-b-16">
<div class="col-form-label col-md-2">
</div>
<div class="col-md-9">
<input {{ attributes.addClass(classes) }} />
{{ children }}
</div>
</div>
{% endspaceless %}
The end result:
