Mike Cho Development

Add CSS class to a Drupal menu in Twig

In a custom theme I was making in Drupal 8, I wanted to remove the bullets in front of the list items and found no easy way to do so. The menu didn’t have an ID or css class to hook into. It looked a bit like this:

  • Unmasquerade
  • My account
  • Log out

The menu, called “User account menu,” machine name, “account,” was not able to be styled in any way I knew how. So I had to learn how to modify the template on which it was based.

As I have debugging enabled, I could inspect the html and see that we were getting this from themes/business_responsive/templates/navigation/menu.html.twig. I made a templates/navigation directory in my own theme (a sub-theme of Business Responsive) and got to work modifying it.

However, there was in issue here: this template was used for all menus. I couldn’t just plop a class in there, because the same problem would remain, the lack of a unique handle to use for css.

This meant I would have to dig into the template logic.

Menu Macros

Guided by this tutorial, I began to hack away. The twig template has a mechanism to add classes based on the menu name. You find this line:

{{ menus.menu_links(items, attributes, 0) }}

And change it to this:

{{ menus.menu_links(items, attributes, 0, menu_name) }}

That’s step one, which will add the menu name as an argument to the macro (a type of function).

Step two is to create an array of classes.

{% set menu_classes = [
      'c-menu-' ~ menu_name | clean_class
    ]
    %}

This step takes the argument and appends it to ‘c-menu’ if it exists. So in my case, I will be hoping this ends up as a class of ‘c-menu-account.’ With that, I’ll open up my css file and add the desired styling:

.c-menu-account {
   list-style-type: none;
   padding-left: 0;
 }

The next final step is to add the menu_classes variable we just created to the ul that is the container for the menu:

<ul{{ attributes.addClass(menu_classes) }}>

Flush the cache and reset the page. The new styling should appear!

Unmasquerade
My account
Log out

Before and after code for the menu.twig below.

Before

{{ menus.menu_links(items, attributes, 0) }}

{% macro menu_links(items, attributes, menu_level) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
      <ul{{ attributes.addClass('menu') }}>
    {% else %}
      <ul class="menu">
    {% endif %}
    {% for item in items %}
      {%
        set classes = [
          'menu-item',
          item.is_expanded ? 'menu-item--expanded',
          item.is_collapsed ? 'menu-item--collapsed',
          item.in_active_trail ? 'menu-item--active-trail',
        ]
      %}
      <li{{ item.attributes.addClass(classes) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
    </ul>
  {% endif %}
{% endmacro %}

After

{{ menus.menu_links(items, attributes, 0, menu_name) }}

{% macro menu_links(items, attributes, menu_level, menu_name) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
    {% set menu_classes = [
      'c-menu-' ~ menu_name | clean_class
    ]
    %}
      <ul{{ attributes.addClass(menu_classes) }}>
    {% else %}
      <ul class="menu">
    {% endif %}
    {% for item in items %}
      {%
        set classes = [
          'menu-item',
          item.is_expanded ? 'menu-item--expanded',
          item.is_collapsed ? 'menu-item--collapsed',
          item.in_active_trail ? 'menu-item--active-trail',
          
        ]
      %}
      <li{{ item.attributes.addClass(classes) }}>
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      </li>
    {% endfor %}
    </ul>
  {% endif %}
{% endmacro %}