Last week, I had to solve a problem where a prototyped form contained a button element and svg icon for the submit button on three search forms.
While you can change the type of a input from submit
to button
in Drupal 8, that does not change the tag the form element uses.
I needed a clean solution that allowed me to provide the markup and svg icon without interfering with the function of the form itself.
Here is the output I needed to achieve:
<button type="button" class="search-box__button">
<svg class="icon search-box__open">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/build/img/svg-sprite.svg#icon-search"></use>
</svg>
</button>
I did a little digging, and saw that Drupal base theme (classy or stable) provide an input.html.twig
and the option of overriding an element with input--(type).html.twig
. That’s cool, but I don’t want to override all submits on the website, just specific ones.
Fortunately, core provides many ways to hook in and provide more template suggestions if none suit. I had the idea to add a data-attribute
on my form element that I wanted to override. Then, using hook_theme_suggestions_input_alter()
, I look to see if this attribute exists, if so, create the suggestion based on that value.
Here it is in action:
/**
* @param $suggestions
* @param array $variables
*/
function mytheme_theme_suggestions_input_alter(&$suggestions, array $variables) {
$element = $variables['element'];
if (isset($element['#attributes']['data-twig-suggestion'])) {
$suggestions[] = 'input__' . $element['#type'] . '__' . $element['#attributes']['data-twig-suggestion'];
}
}
/**
* @param $form
* @param \Drupal\Core\Form\FormStateInterface $form_state
* @param $form_id
*/
function mytheme_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if ($form['#id'] == 'views-exposed-form-search-results') {
$form['actions']['submit']['#attributes']['data-twig-suggestion'] = 'search_results_submit';
$form['actions']['submit']['#attributes']['class'][] = 'search-box__button';
}
}
I simply use the #attributes
key to add my data attribute. Originally, I was using the #name
key only, but I figured I would create my own data attribute so nothing else in the system interferes with it, and I can guarantee uniqueness if multiple exist on the page.
Then, I created the necessary twig file. In this case, it is input--submit--search-results-submit.html.twig
:
{#
/**
* @file
* Theme override for an 'input' #type form element.
*
* Available variables:
* - attributes: A list of HTML attributes for the input element.
* - children: Optional additional rendered elements.
*
* @see template_preprocess_input()
*/
#}
<button{{ attributes }}>
<svg class="icon search-box__open">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/themes/custom/mytheme/build/img/svg-sprite.svg#icon-search"></use>
</svg>
</button>
Now, if I want to apply the same treatment to other submit buttons, I simply pop that data attribute on the end and reload the page.
I suppose you could also create custom theme hooks for this, and I wonder if that is more “proper”, but all in all this only took about 10 minutes to put together and gives me the ability to scale out if I need to change how elements are marked up in the future. I assume that if my needs became more complex, a theme hook would be necessary - but for now, I don’t think that it is.