When all goes according to plan—which is surprisingly often—theming in Drupal 8 is a straightforward matter of editing templates and stylesheets. We found things did not go according to plan when styling page-level action links (such as "Add new Forum topic") and content-level action links (or node links as Drupal 8 still calls them, such as "read more" or "add comment"). We are going to show how to add Bootstrap-specific styles, but the same approaches would be useful to add special stylings, button-like or otherwise, to selected action links and node links.
First, let's revisit the steps to follow when trying to add classes to Drupal-produced markup:
- Enable Twig debugging.
- Using your browser's inspector, identify the template providing the code you need to modify.
- Copy the template from where Twig debug output says it lives in Drupal core (or contrib) to your theme. If needed, use template name suggestions with a
--modifier
to selectively override the template in specific cases. - Add BEM-compliant classes to your template. This is usually accomplished by tacking the
addClass("here are--my-classes")
method onto anattributes
variable. - Use the classes you added in your CSS.
Now let's try applying that to styling action links and node links (but only the actual link parts, not the surrounding text) as buttons.
Page-level action links
Here is an action link provided by Drupal core in the Forum module. It's classes don't align with Bootstrap's, so it displays without style.
The process
If we open the template that is providing the HTML for each link, menu-local-action.html.twig
, it is only one line of code (and 12 lines of comments). It couldn't be simpler!
{# /** * @file * Default theme implementation for a single local action link. * * Available variables: * - attributes: HTML attributes for the wrapper element. * - link: A rendered link element. * * @see template_preprocess_menu_local_action() * * @ingroup themeable */ #} <li{{ attributes }}>{{ link }}</li>
Except... the attributes
variable we have available is on the list item (li
tag), not the link itself. Using this template we can't add classes to the already-rendered link element. Putting the button class on the list item would result in a common UX problem: button-looking elements with parts that are not clickable.
Even though this template cannot be used directly, it points us in the right direction. On line 10, a comments suggest us to see template_preprocess_menu_local_action()
. So we shall.
A symbol finder in an IDE or grep
will quickly take us to line 65 of core/includes/menu.inc
:
/** * Prepares variables for single local action link templates. * * Default template: menu-local-action.html.twig. * * @param array $variables * An associative array containing: * - element: A render element containing: * - #link: A menu link array with 'title', 'url', and (optionally) * 'localized_options' keys. */ function template_preprocess_menu_local_action(&$variables) { $link = $variables['element']['#link']; $link += array( 'localized_options' => array(), ); $link['localized_options']['attributes']['class'][] = 'button'; $link['localized_options']['attributes']['class'][] = 'button-action'; $link['localized_options']['set_active_class'] = TRUE; $variables['link'] = array( '#type' => 'link', '#title' => $link['title'], '#options' => $link['localized_options'], '#url' => $link['url'], ); }
Here we can see exactly how Drupal is adding classes ('button' and 'button-action') to the buttons. Let's add our own preprocess function assuming our theme is name exampletheme
:
- Add a function to our .theme file. In this example, the file would be
exampletheme.theme
. - Name the function
exampletheme_preprocess_menu_local_action()
. That is, replace the word 'template' with the name of our theme name. - Modify the
$variables
array to add our classes.
We could even remove the existing classes from the link, but we'll leave them for now. Note that the link that gets processed is $variables['link']
rather than $variables['element']['#link']
.
The solution
/** * Extends template_preprocess_menu_local_action(). * * Add Bootstrap button classes to a single local action link. * * Default template: menu-local-action.html.twig. * * @param array $variables * An associative array containing: * - element: A render element containing: * - #link: A menu link array with 'title', 'url', and (optionally) * 'localized_options' keys. */ function exampletheme_preprocess_menu_local_action(&$variables) { $variables['link']['#options']['attributes']['class'][] = 'btn'; $variables['link']['#options']['attributes']['class'][] = 'btn-success'; }
Content-level action links
Next let's style node links as buttons. It's well-nigh impossible to get the btn
and btn-success
classes on the login and register links within the sentence "Log in or register to post comments". Therefore, we will use Bootstrap's handy mixins. The following is a SCSS code snippet which is turned into CSS by a SASS preprocessor.
.links--node a { @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); }
Finally, we just need to add the links--node
class. Assuming our theme is called exampletheme
:
/** * Implements hook_preprocess_links() for node entities. */ function exampletheme_preprocess_links__node(&$variables) { $variables['attributes']['class'][] = 'list-unstyled'; $variables['attributes']['class'][] = 'links--node'; }
Bonus: Link field links as buttons
For styling the output of link fields as buttons, the aptly-named Button link formatter module can help you out without the need for custom code nor templating.
Are you styling action links, node links, and other links?
Have you faced similar needs for changing the look of links, to be buttons or otherwise? How have you met them? Let us know in the comments!
Comments
2020 August 07
Alex
Or you can use https://www…
Or [as of 2020] you can use https://www.drupal.org/project/enhanced_button_link
2024 September 17
mlncn
Note that the module in Alex…
Note that the module in Alex's comment is for links added to content via Drupal core's link field type, not for the action links covered in the first section of this post. Local action links are… special. For additional theming of menu local actions, the Twig Template Suggester module recently added a template suggestion (based on the link route).
Add new comment